]> git.llucax.com Git - z.facultad/75.06/emufs.git/blob - emufs/tipo1.c
* Se agrega generador de facturas. Primero hay que correr el generador de articulos
[z.facultad/75.06/emufs.git] / emufs / tipo1.c
1 /* vim: set noexpandtab tabstop=4 shiftwidth=4 wrap:
2  *----------------------------------------------------------------------------
3  *                                  emufs
4  *----------------------------------------------------------------------------
5  * This file is part of emufs.
6  *
7  * emufs is free software; you can redistribute it and/or modify it under the
8  * terms of the GNU General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option) any later
10  * version.
11  *
12  * emufs is distributed in the hope that it will be useful, but WITHOUT ANY
13  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with emufs; if not, write to the Free Software Foundation, Inc., 59 Temple
19  * Place, Suite 330, Boston, MA  02111-1307  USA
20  *----------------------------------------------------------------------------
21  * Creado:  vie abr  9 16:47:32 ART 2004
22  * Autores: Leandro Lucarella <llucare@fi.uba.ar>
23  *----------------------------------------------------------------------------
24  *
25  * $Id$
26  *
27  */
28
29 /** \file
30  *
31  * Archivo con bloque de longitud parametrizada, registro de longitud variable.
32  * 
33  * Implementación del archivo con bloques de longitud parametrizada y registros
34  * de longitud variable.
35  *
36  */
37
38 #include "tipo1.h"
39 #include "idx.h"
40 #include "fsc.h"
41 #include "did.h"
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #ifndef MIN
48 #       define MIN(x, y) (((x) > (y)) ? (y) : (x))
49 #endif
50
51 /*------------------ Declaraciones privadas ----------------------*/
52
53 /** Cabecera de un registro de un archivo tipo1. */
54 typedef struct {
55         EMUFS_REG_ID   id;   /**< Identificador del registro. */
56         EMUFS_REG_SIZE size; /**< Tamaño del registro. */
57 } EMUFS_TIPO1_REG_HEADER;
58
59 static size_t emufs_tipo1_header_size(void);
60
61 static int emufs_tipo1_header_jump(FILE*);
62
63 static int emufs_tipo1_block_jump(EMUFS*, FILE*, EMUFS_BLOCK_ID);
64
65 static void emufs_tipo1_escribir_reg_en_memoria(char*, EMUFS_TIPO1_REG_HEADER,
66                 char*);
67
68 static void emufs_tipo1_escribir_reg_chunk_en_memoria(char* dst,
69                 EMUFS_TIPO1_REG_HEADER header, char* reg, EMUFS_REG_SIZE reg_size);
70
71 /** Lee el bloque \param num_bloque y lo almacena en \c ptr. */
72 static void* emufs_tipo1_leer_bloque(EMUFS*, EMUFS_BLOCK_ID, int*);
73
74 /** Graba el bloque apuntado por \c ptr en el archivo. */
75 static EMUFS_BLOCK_ID emufs_tipo1_grabar_bloque(EMUFS*, void*, EMUFS_BLOCK_ID,
76                 int*);
77
78 /** Obtiene el tamaño del archivo. */
79 static long emufs_tipo1_get_file_size(EMUFS*, int*);
80
81 /** Guarda un registro con un id determinado */
82 static int emufs_tipo1_grabar_registro_con_id(EMUFS* efs, void* reg,
83                 EMUFS_TIPO1_REG_HEADER header);
84
85 /*------------------ Funciones públicas ----------------------*/
86
87 int emufs_tipo1_inicializar(EMUFS* efs)
88 {
89         /* como mínimo el tamaño de bloque debe ser 2 veces el tamaño de la cabecera
90          * (una relación 1/2 entre datos e info de control ya es lo suficientemente
91          * mala */
92         if (efs->tam_bloque < (sizeof(EMUFS_TIPO1_REG_HEADER) * 2)) {
93                 PERR("bloque demasiado chico");
94                 return 1000; /* EMUFS_ERROR_BLOCK_SIZE_TOO_SMALL */
95         }
96         /* Asigna punteros a funciones. */
97         efs->leer_bloque       = emufs_tipo1_leer_bloque;
98         efs->grabar_registro   = emufs_tipo1_grabar_registro;
99         efs->borrar_registro   = emufs_tipo1_borrar_registro;
100         efs->leer_registro     = emufs_tipo1_leer_registro;
101         efs->leer_registro_raw = emufs_tipo1_leer_registro_raw;
102         efs->leer_estadisticas = emufs_tipo1_leer_estadisticas;
103         efs->compactar         = emufs_tipo1_compactar;
104         return 0; /* EMUFS_OK */
105 }
106
107 void* emufs_tipo1_leer_registro(EMUFS* efs, EMUFS_REG_ID reg_id,
108                 EMUFS_REG_SIZE* reg_size, int *err)
109 {
110         char* block; /* bloque leido (en donde está el registro a leer) */
111         char* registro; /* registro a leer */
112         EMUFS_BLOCK_ID block_id; /* id del bloque en donde esta el registro a leer */
113         EMUFS_BLOCK_SIZE offset; /* offset del bloque leído */
114         EMUFS_TIPO1_REG_HEADER curr_reg_header; /* cabecera del registro a leer */
115
116         block_id = emufs_idx_buscar_registro(efs, reg_id);
117         if (block_id == EMUFS_NOT_FOUND) {
118                 /* TODO Manejo de errores */
119                 PERR("Registro no encontrado");
120                 *err = EMUFS_NOT_FOUND;
121                 return NULL;
122         }
123         if (!(block = (char*) emufs_tipo1_leer_bloque(efs, block_id, err))) {
124                 /* TODO Manejo de errores */
125                 PERR("no se pudo reservar memoria");
126                 *err = 2; /* EMUFS_ERROR_OUT_OF_MEMORY */
127                 return NULL;
128         }
129
130         /* Busco secuencialmente en el bloque el registro a leer */
131         offset = 0;
132         do {
133                 /* Copio la cabecera del registro actual. */
134                 memcpy(&curr_reg_header, block + offset, sizeof(EMUFS_TIPO1_REG_HEADER));
135                 offset += sizeof(EMUFS_TIPO1_REG_HEADER);
136                 if (curr_reg_header.id == reg_id) {
137                         /* tamaño máximo ultilizable para datos en un bloque */
138                         EMUFS_BLOCK_SIZE block_space
139                                         = efs->tam_bloque - sizeof(EMUFS_TIPO1_REG_HEADER);
140                         /* tamaño de la porción de registro que se guarda */
141                         EMUFS_REG_SIZE chunk_size = 0; 
142                         /* puntero a la porción actual del registro */
143                         char* chunk_ptr;
144
145                         *reg_size = curr_reg_header.size;
146                         registro = chunk_ptr = (char*) malloc(*reg_size);
147                         if (registro == NULL) {
148                                 /* TODO Manejo de errores */
149                                 free(block);
150                                 PERR("No hay memoria");
151                                 *err = 2; /* EMUFS_ERROR_OUT_OF_MEMORY */
152                                 return NULL;
153                         }
154                         while (1) {
155                                 chunk_ptr += chunk_size; /* Avanzo para guardar prox chunk */
156                                 curr_reg_header.size -= chunk_size; /* Resto lo que ya guardé */
157                                 chunk_size = MIN(curr_reg_header.size, block_space);
158                                 /* copio porción de registro en el buffer */
159                                 memcpy(chunk_ptr, block + offset, chunk_size);
160                                  /* falta leer un bloque */
161                                 if (curr_reg_header.size > block_space) {
162                                         free(block);
163                                         if (!(block = (char*) emufs_tipo1_leer_bloque(efs,
164                                                                         ++block_id, err))) {
165                                                 /* TODO Manejo de errores */
166                                                 free(registro);
167                                                 PERR("no se pudo reservar memoria");
168                                                 *err = 2; /* EMUFS_ERROR_OUT_OF_MEMORY */
169                                                 return NULL;
170                                         }
171                                 } else { /* se terminó de leer */
172                                         break;
173                                 }
174                         }
175                         break;
176                 }
177                 /* Desplazo el offset */
178                 offset += curr_reg_header.size;
179
180         /* esto no debería ser nunca false porque sé positivamente que el */
181         } while (offset < efs->tam_bloque); /* registro está en el bloque */
182
183         free(block);
184         return registro;
185 }
186
187 void* emufs_tipo1_leer_registro_raw(EMUFS *efs, EMUFS_REG_ID id, EMUFS_REG_SIZE *size, int *pos)
188 {
189         char *chunk_ptr;
190         char* block; /* bloque leido (en donde está el registro a leer) */
191         char* registro; /* registro a leer */
192         EMUFS_BLOCK_ID block_id; /* id del bloque en donde esta el registro a leer */
193         EMUFS_BLOCK_SIZE offset, block_space; /* offset del bloque leído */
194         EMUFS_TIPO1_REG_HEADER curr_reg_header; /* cabecera del registro a leer */
195         EMUFS_REG_SIZE cant_bloques;
196         int err, i;
197
198         block_id = emufs_idx_buscar_registro(efs, id);
199         if (block_id == EMUFS_NOT_FOUND) {
200                 /* TODO Manejo de errores */
201                 PERR("Registro no encontrado");
202                 *pos = 0;
203                 *size = 0;
204                 return NULL;
205         }
206         err = 0;
207         if (!(block = (char*) emufs_tipo1_leer_bloque(efs, block_id, &err))) {
208                 /* TODO Manejo de errores */
209                 PERR("no se pudo reservar memoria");
210                 *pos = 0;
211                 *size = 0;
212                 return NULL;
213         }
214
215         /* Busco secuencialmente en el bloque el registro a leer */
216         offset = 0;
217         do {
218                 /* Copio la cabecera del registro actual. */
219                 memcpy(&curr_reg_header, block + offset, sizeof(EMUFS_TIPO1_REG_HEADER));
220                 offset += sizeof(EMUFS_TIPO1_REG_HEADER);
221                 if (curr_reg_header.id == id) {
222                         /* tamaño máximo ultilizable para datos en un bloque */
223                         *pos = offset-sizeof(EMUFS_TIPO1_REG_HEADER);
224                         block_space = efs->tam_bloque - sizeof(EMUFS_TIPO1_REG_HEADER);
225                         /* tamaño de la porción de registro que se guarda */
226
227                         cant_bloques = curr_reg_header.size / block_space + 1;
228                         *size = cant_bloques*efs->tam_bloque;
229                         registro = chunk_ptr = (char*) malloc(*size - (cant_bloques-1)*sizeof(EMUFS_TIPO1_REG_HEADER) + (cant_bloques-1)*2);
230                         if (registro == NULL) {
231                                 /* TODO Manejo de errores */
232                                 free(block);
233                                 PERR("No hay memoria");
234                                 *pos = 0;
235                                 *size = 0;
236                                 return NULL;
237                         }
238                         memcpy(registro, block, efs->tam_bloque);
239                         chunk_ptr += efs->tam_bloque;
240                         /* Copio los otros bloques, si los hay */
241                         free(block);
242                         for(i=1; i<cant_bloques; i++) {
243                                 err = 0;
244                                 block = (char*)emufs_tipo1_leer_bloque(efs, block_id+i, &err);
245                                 /* Solo grabo el header del primer pedazo! */
246                                 memcpy(chunk_ptr, "<>", 2);
247                                 chunk_ptr += 2;
248                                 memcpy(chunk_ptr, block+sizeof(EMUFS_TIPO1_REG_HEADER), efs->tam_bloque-sizeof(EMUFS_TIPO1_REG_HEADER));
249                                 chunk_ptr += efs->tam_bloque-sizeof(EMUFS_TIPO1_REG_HEADER);
250                                 free(block);
251                         }
252                         /* Todo listo! */
253                         break;
254                 }
255                 /* Desplazo el offset */
256                 offset += curr_reg_header.size;
257         } while (offset < efs->tam_bloque);
258
259         return registro;
260 }
261
262 void* emufs_tipo1_leer_bloque(EMUFS* efs, EMUFS_BLOCK_ID block_id, int *err)
263 {
264         FILE* file;
265         char* block; /* bloque leido (en donde está el registro a leer) */
266         char  name_f[255];
267
268         strcpy(name_f,efs->nombre);
269         strcat(name_f,".dat");
270
271         if ((file = fopen(name_f, "r")) == NULL) {
272                 PERR("No se puede abrir archivo");
273                 *err = 4; /* EMUFS_ERROR_CANT_OPEN_FILE */
274                 return NULL; /* FIXME ERROR */
275         }
276         emufs_tipo1_header_jump(file); /* salta cabeceras */
277         emufs_tipo1_block_jump(efs, file, block_id); /* salta bloques */
278         /* FIXME: verificar que no se pase de fin de archivo*/
279         block = (char*) malloc(efs->tam_bloque);
280         if (block == NULL) {
281                 /* TODO Manejo de errores */
282                 PERR("No hay memoria");
283                 *err = 2; /* EMUFS_ERROR_OUT_OF_MEMORY */
284                 return NULL;
285         }
286         if (fread(block, efs->tam_bloque, 1, file) != 1) {
287                 /* TODO Manejo de errores */
288                 free(block);
289                 PERR("Error al leer bloque");
290                 *err = 3; /* EMUFS_ERROR_FILE_READ */
291                 return NULL;
292         }
293         fclose(file);
294         return block;
295 }
296
297 EMUFS_REG_ID emufs_tipo1_grabar_registro(EMUFS* efs, void* reg, EMUFS_REG_SIZE reg_size, int* err)
298 {
299         EMUFS_TIPO1_REG_HEADER header; /* cabecera del registro a leer */
300         /* obtengo identificador que corresponderá al registro */
301         header.id = emufs_idx_get_new_id(efs, err);
302         if (*err) {
303                 PERR("no se pudo obtener un id para el registro nuevo");
304                 return EMUFS_NOT_FOUND;
305         }
306         header.size = reg_size; /* tamaño del registro */
307         if ((*err = emufs_tipo1_grabar_registro_con_id(efs, reg, header))) {
308                 PERR("error al grabar el registro");
309                 return EMUFS_NOT_FOUND;
310         }
311         return header.id;
312 }
313
314 int emufs_tipo1_borrar_registro(EMUFS* efs, EMUFS_REG_ID reg_id)
315 {
316         char* block; /* bloque leido (en donde está el registro a leer) */
317         EMUFS_BLOCK_ID block_id; /* id del bloque en donde esta el registro a leer */
318         EMUFS_BLOCK_SIZE offset; /* offset del bloque leído */
319         EMUFS_TIPO1_REG_HEADER curr_reg_header; /* cabecera del registro a leer */
320         int err = 0; /* para almacenar código de error */
321
322         block_id = emufs_idx_buscar_registro(efs, reg_id);
323         if (block_id == EMUFS_NOT_FOUND) {
324                 /* TODO Manejo de errores */
325                 PERR("Registro no encontrado");
326                 return EMUFS_NOT_FOUND;
327         }
328         if (!(block = (char*) emufs_tipo1_leer_bloque(efs, block_id, &err))) {
329                 /* TODO Manejo de errores */
330                 PERR("no se pudo reservar memoria");
331                 return err;
332         }
333
334         /* Busco secuencialmente en el bloque el registro a leer */
335         offset = 0;
336         do {
337                 /* Copio la cabecera del registro actual. */
338                 memcpy(&curr_reg_header, block + offset, sizeof(EMUFS_TIPO1_REG_HEADER));
339                 if (curr_reg_header.id == reg_id) {
340                         /* identificador del bloque actual */
341                         EMUFS_BLOCK_ID curr_block_id = block_id;
342                         /* tamaño máximo ultilizable para datos en un bloque */
343                         EMUFS_BLOCK_SIZE block_space
344                                         = efs->tam_bloque - sizeof(EMUFS_TIPO1_REG_HEADER);
345                         EMUFS_FREE fs; /* cantidad de espacio libre en el bloque */
346
347                         while (1) {
348                                 /* actualizo archivo de espacio libre por bloque */
349                                 fs = emufs_fsc_get_fs(efs, curr_block_id)
350                                         + MIN(curr_reg_header.size, block_space)
351                                         + sizeof(EMUFS_TIPO1_REG_HEADER);
352                                 if ((err = emufs_fsc_actualizar(efs, curr_block_id, fs))) {
353                                         /* TODO Manejo de errores */
354                                         PERR("no se pudo actualizar .fsc");
355                                         free(block);
356                                         return err;
357                                 }
358                                 /* falta liberar un bloque (o porción) */
359                                 if (curr_reg_header.size > block_space) {
360                                         free(block);
361                                         if (!(block = (char*) emufs_tipo1_leer_bloque(efs,
362                                                                         ++curr_block_id, &err))) {
363                                                 /* TODO Manejo de errores */
364                                                 PERR("no se pudo leer el bloque");
365                                                 return err;
366                                         }
367                                         /* copio la cabecera del primer registro (si ocupa más de un
368                                          * registro está en bloques contiguos) */
369                                         memcpy(&curr_reg_header, block,
370                                                         sizeof(EMUFS_TIPO1_REG_HEADER));
371                                 } else { /* se terminó de leer */
372                                         break;
373                                 }
374                         }
375
376                         /* actualizo archivo de identificadores de registros borrados */
377                         if ((err = emufs_did_agregar(efs, reg_id))) {
378                                 /* TODO Manejo de errores */
379                                 PERR("no se pudo actualizar .did");
380                                 free(block);
381                                 return err;
382                         }
383                         /*actualizo archivo .idx*/
384                         if ((err = emufs_idx_borrar(efs, reg_id))) {
385                                 /* TODO Manejo de errores */
386                                 PERR("no se pudo actualizar .did");
387                                 free(block);
388                                 return err;
389                         }
390
391                         /* desplazo registros a izquierda */
392                         {   /* offset del fin del registro a borrar */
393                                 EMUFS_BLOCK_SIZE offset_reg_end = offset
394                                         + sizeof(EMUFS_TIPO1_REG_HEADER) + curr_reg_header.size;
395                                 /* si es necesario desplazar */
396                                 if (offset < offset_reg_end) {
397                                         /* muevo la porción de bloque a izquierda */
398                                         memcpy(block + offset, block + offset_reg_end,
399                                                 efs->tam_bloque - offset_reg_end);
400                                 }
401                         }
402                         /* guardo el bloque en disco */
403                         emufs_tipo1_grabar_bloque(efs, block, curr_block_id, &err);
404                         if (err) {
405                                 /* TODO Manejo de errores */
406                                 PERR("no se pudo grabar bloque en disco");
407                                 free(block);
408                                 return err;
409                         }
410
411                         break; /* salgo del loop, ya hice todo lo que tenía que hacer */
412                 }
413                 /* desplazo el offset */
414                 offset += sizeof(EMUFS_TIPO1_REG_HEADER) + curr_reg_header.size;
415
416         /* esto no debería ser nunca false porque sé positivamente que el */
417         } while (offset < efs->tam_bloque); /* registro está en el bloque */
418
419         free(block);
420         return 0; /* EMUFS_OK */
421 }
422
423 EMUFS_Estadisticas emufs_tipo1_leer_estadisticas(EMUFS* efs)
424 {
425         int err = 0;
426         EMUFS_Estadisticas stats;
427         memset(&stats, 0, sizeof(EMUFS_Estadisticas));
428
429         stats.tam_archivo_bytes = emufs_tipo1_get_file_size(efs, &err);
430         if (err) {
431                 /* TODO manejo de errores */
432                 PERR("no se pudo obtener el tamaño del archivo");
433                 return stats;
434         }
435
436         /* obtengo cantidad de bloques */
437         stats.cant_bloques = (stats.tam_archivo_bytes - emufs_tipo1_header_size())
438                         / efs->tam_bloque;
439
440         /* obtengo la cantidad de registros en el archivo */
441         {
442                 EMUFS_REG_ID *tmp = emufs_idx_get(efs, &stats.tam_archivo);
443                 if (tmp) free(tmp); /* libera memoria innecesaria */
444         }
445
446         /* obtengo total de información de control que guarda el archivo */
447         stats.info_control = emufs_tipo1_header_size() /* cabecera del archivo */
448                         /* mas las cabeceras de todos los registros */
449                         + stats.tam_archivo * sizeof(EMUFS_TIPO1_REG_HEADER);
450
451         /* obtengo las estadísticas del archivo de espacio libre por bloque */
452         stats.total_fs = emufs_fsc_get_total_fs(efs);
453         stats.media_fs = emufs_fsc_get_media_fs(efs);
454         emufs_fsc_get_max_min_fs(efs, &stats.min_fs, &stats.max_fs);
455
456         return stats;   
457 }
458
459 void emufs_tipo1_compactar(EMUFS* efs)
460 {
461         EMUFS_REG_ID total_ids; /* cantidad total de registros en el array */
462         /* array con los identificadores de los registros */
463         EMUFS_REG_ID* reg_ids = emufs_idx_get(efs, &total_ids);
464         int i; /* índice de elemento actual del array */
465
466         /* recorro cada registro válido del archivo */
467         for (i = 0; i < total_ids; ++i) {
468                 EMUFS_FREE fs; /* espacio libre en el bloque */
469                 EMUFS_TIPO1_REG_HEADER header; /* cabecera del registro a leer */
470                 int err = 0; /* para almacenar código de error */
471                 /* bloque al que pertenece el registro */
472                 EMUFS_BLOCK_ID block_id = emufs_idx_buscar_registro(efs, reg_ids[i]);
473                 /* obtengo un bloque con espacio suficiente para almacenarlo */
474                 EMUFS_BLOCK_ID free_block_id = emufs_fsc_buscar_lugar(efs,
475                                 sizeof(EMUFS_TIPO1_REG_HEADER) + reg_ids[i], &fs);
476
477                 /* si el registro está borrado, continúo con el próximo */
478                 if (block_id == EMUFS_NOT_FOUND) {
479                         continue;
480                 }
481                 /* obtengo identificador del registro actual */
482                 header.id = reg_ids[i];
483                 /* TODO analizar caso de registros multibloque */
484                 /* si el bloque obtenido está antes del bloque actual */
485                 if (free_block_id < block_id) {
486                         /* leo el registro */
487                         char* reg = (char*) efs->leer_registro(efs, header.id,
488                                         &header.size, &err);
489                         if (err) {
490                                 PERR("error al leer registro");
491                                 return;
492                         }
493                         /* borro el registro */
494                         if ((err = efs->borrar_registro(efs, header.id))) {
495                                 PERR("error al borrar registro");
496                                 free(reg);
497                                 return;
498                         }
499                         /* lo inserto en la nueva posición */
500                         if ((err = emufs_tipo1_grabar_registro_con_id(efs, reg, header))) {
501                                 PERR("error al borrar registro");
502                                 free(reg);
503                                 return;
504                         }
505                         free(reg);
506                 }
507         }
508         free(reg_ids); /* libero lista de ids */
509
510         /* truncamos el archivo si hay bloques libres al final */
511         {
512                 EMUFS_FREE fs; /* espacio libre en el bloque */
513                 /* busco si hay algún bloque completo libre */
514                 EMUFS_BLOCK_ID block_id = emufs_fsc_buscar_lugar(efs, efs->tam_bloque
515                                 - sizeof(EMUFS_TIPO1_REG_HEADER), &fs);
516                 /* si hay, el resto del archivo tiene que estar vacío */
517                 if (block_id != EMUFS_NOT_FOUND) {
518                         long size = emufs_tipo1_header_size() /* cabecera del archivo */
519                                 + block_id * efs->tam_bloque; /* mas los bloques compactos */
520                         char filename[255];
521
522                         /* trunca archivo de datos */
523                         strcpy(filename, efs->nombre);
524                         strcat(filename, ".dat");
525                         truncate(filename, size);
526                         /* trunca archivo de de espacio libre */
527                         emufs_fsc_truncate(efs, block_id);
528                 }
529         }
530 }
531
532 EMUFS_BLOCK_ID emufs_tipo1_grabar_bloque(EMUFS *efs, void *block,
533                 EMUFS_BLOCK_ID block_id, int* err)
534 {
535         FILE* file;
536         char name_f[255];
537
538         strcpy(name_f,efs->nombre);
539         strcat(name_f,".dat");
540
541         if ((file = fopen(name_f, "r+b")) == NULL) {
542                 /* TODO Manejo de errores */
543                 PERR("Error al abrir archivo");
544                 *err = 4; /* EMUFS_ERROR_CANT_OPEN_FILE */
545                 return EMUFS_NOT_FOUND;
546         }
547         /* Si es NOT_FOUND tengo que agregar un bloque al final del archivo */
548         if (block_id == EMUFS_NOT_FOUND) {
549                 /* me paro al final del archivo */
550                 if (fseek(file, 0l, SEEK_END)) {
551                         /* TODO Manejo de errores */
552                         PERR("No se pudo hacer fseek()");
553                         fclose(file);
554                         *err = 8; /* EMUFS_ERROR_SEEK_FILE */
555                         return EMUFS_NOT_FOUND;
556                 }
557                 /* Obtengo ID del bloque nuevo */
558                 block_id = (ftell(file) - emufs_tipo1_header_size()) / efs->tam_bloque;
559         /* Si es un ID válido, salto hasta ese bloque. */
560         } else {
561                 /* Salta el header del archivo */
562                 if ((*err = emufs_tipo1_header_jump(file))) {
563                         PERR("no se pudo saltar la cabecera del archivo");
564                         fclose(file);
565                         return EMUFS_NOT_FOUND;
566                 }
567                 /* Salta bloques */
568                 if ((*err = emufs_tipo1_block_jump(efs, file, block_id))) {
569                         PERR("no se pudo saltar la cabecera del bloque");
570                         fclose(file);
571                         return EMUFS_NOT_FOUND;
572                 }
573         }
574         /* Grabo el bloque */
575         if (fwrite(block, efs->tam_bloque, 1, file) != 1) {
576                 PERR("No se pudo escribir el archivo");
577                 fclose(file);
578                 *err = 6; /* EMUFS_ERROR_WRITE_FILE */
579                 return EMUFS_NOT_FOUND;
580         }
581
582         fclose(file);
583         return block_id;
584 }
585
586 static int emufs_tipo1_grabar_registro_con_id(EMUFS* efs, void* reg,
587                 EMUFS_TIPO1_REG_HEADER header)
588 {
589         EMUFS_FREE     fs; /* espacio libre en el bloque */
590         EMUFS_BLOCK_ID block_id; /* identificador del 1er bloque */
591         char*          block; /* buffer del bloque a guardar en disco */
592         char           name_f[255];
593         int            err = 0;
594
595         strcpy(name_f, efs->nombre);
596         strcat(name_f, ".dat");
597
598         /* busco lugar para el registro en un bloque existente */
599         block_id = emufs_fsc_buscar_lugar(efs, sizeof(EMUFS_TIPO1_REG_HEADER)
600                         + header.size, &fs);
601         /* si no hay bloques con suficiente espacio creo un bloque nuevo */
602         if (block_id == EMUFS_NOT_FOUND) {
603                 /* tamaño máximo ultilizable para datos en un bloque */
604                 EMUFS_BLOCK_SIZE block_space = efs->tam_bloque - sizeof(EMUFS_TIPO1_REG_HEADER);
605                 /* identificador del bloque que se guarda */
606                 EMUFS_BLOCK_ID curr_block_id = EMUFS_NOT_FOUND;
607                 /* tamaño de la porción de registro que se guarda */
608                 EMUFS_REG_SIZE chunk_size = 0; 
609                 /* puntero a la poción del registro */
610                 char* chunk_ptr = reg; 
611
612                 /* crear un nuevo bloque en memoria */
613                 block = (char*) malloc(efs->tam_bloque);
614                 if (block == NULL) {
615                         /* TODO Manejo de errores */
616                         PERR("No hay memoria");
617                         return 2; /* EMUFS_ERROR_OUT_OF_MEMORY */
618                 }
619                 do {
620                         memset(block, 0, efs->tam_bloque); /* inicializa bloque */
621                         chunk_ptr += chunk_size; /* Avanzo para guardar prox chunk */
622                         header.size -= chunk_size; /* Resto lo que ya guardé */
623                         chunk_size = MIN(header.size, block_space);
624                         /* graba porción del registro en bloque */
625                         emufs_tipo1_escribir_reg_chunk_en_memoria(block, header, chunk_ptr, chunk_size);
626                         /* graba el bloque en el archivo */
627                         curr_block_id = emufs_tipo1_grabar_bloque(efs, block, EMUFS_NOT_FOUND, &err);
628                         if (err) {
629                                 PERR("error al grabar bloque");
630                                 free(block);
631                                 return err;
632                         }
633                         /* grabo el nuevo registro en el archivo de espacios libres */
634                         err = emufs_fsc_agregar(efs, curr_block_id, block_space - chunk_size);
635                         if (err) {
636                                 PERR("No se pudo agregar fsc");
637                                 free(block);
638                                 return err;
639                         }
640                         /* si es el primer id de bloque obtenido, lo guardo para
641                          * agregarlo después al archivo de índices. */
642                         if (block_id == EMUFS_NOT_FOUND) {
643                                 block_id = curr_block_id;
644                         }
645                 } while (header.size > block_space);
646                 free(block);
647
648         /* Encontró espacio en un bloque existente, graba registro ahí */
649         } else {
650                 /* cargo el bloque en block_id */
651                 if (!(block = (char*) emufs_tipo1_leer_bloque(efs, block_id, &err))) {
652                         /* TODO Manejo de errores */
653                         PERR("no se pudo leer el bloque");
654                         return err;
655                 }
656                 /* graba registro en bloque */
657                 emufs_tipo1_escribir_reg_en_memoria(block + efs->tam_bloque - fs,
658                                 header, reg);
659                 /* graba el bloque en el archivo */
660                 block_id = emufs_tipo1_grabar_bloque(efs, block, block_id, &err);
661                 if (err) {
662                         PERR("error al grabar bloque");
663                         free(block);
664                         return err;
665                 }
666                 free(block);
667                 /* actualizo el archivo de espacios libres */
668                 err = emufs_fsc_actualizar(efs, block_id, fs - header.size
669                                 - sizeof(EMUFS_TIPO1_REG_HEADER));
670                 if (err) {
671                         PERR("No se pudo actualizar fsc");
672                         return err;
673                 }
674         }
675                 
676         /* actualizo el indice de bloques y registros */
677         err = emufs_idx_agregar(efs, header.id, block_id);
678         if (err){
679                 PERR("No se pudo agregar idx");
680                 return err;
681         }
682         
683         return err;
684 }
685
686 EMUFS_REG_ID emufs_tipo1_modificar_registro(EMUFS *emu, EMUFS_REG_ID id,
687                 void *data, EMUFS_REG_SIZE size, int *error)
688 {
689         emufs_tipo1_borrar_registro(emu, id);
690         return emufs_tipo1_grabar_registro(emu, data, size, error);
691 }
692
693 size_t emufs_tipo1_header_size(void)
694 {
695         return sizeof(EMUFS_Tipo) + sizeof(EMUFS_BLOCK_SIZE);
696 }
697
698 int emufs_tipo1_header_jump(FILE* fp)
699 {
700         if (fseek(fp, emufs_tipo1_header_size(), SEEK_CUR)) {
701                 PERR("No se pudo hacer fseek()");
702                 return 8; /* EMUFS_ERROR_SEEK_FILE */
703         }
704         return 0; /* EMUFS_OK */
705 }
706
707 int emufs_tipo1_block_jump(EMUFS* efs, FILE* fp, EMUFS_BLOCK_ID block_count)
708 {
709         if (fseek(fp, block_count * efs->tam_bloque, SEEK_CUR)) {
710                 PERR("No se pudo hacer fseek()");
711                 return 8; /* EMUFS_ERROR_SEEK_FILE */
712         }
713         return 0; /* EMUFS_OK */
714 }
715
716 void emufs_tipo1_escribir_reg_en_memoria(char* dst, EMUFS_TIPO1_REG_HEADER header,
717                 char* reg)
718 {
719         emufs_tipo1_escribir_reg_chunk_en_memoria(dst, header, reg, header.size);
720 }
721
722 void emufs_tipo1_escribir_reg_chunk_en_memoria(char* dst,
723                 EMUFS_TIPO1_REG_HEADER header, char* reg, EMUFS_REG_SIZE reg_size)
724 {
725         /* grabo cabecera del registro en el bloque */
726         memcpy(dst, &header, sizeof(EMUFS_TIPO1_REG_HEADER));
727         /* incremento puntero de escritura */
728         dst += sizeof(EMUFS_TIPO1_REG_HEADER);
729         /* grabo el registro en el bloque */
730         memcpy(dst, reg, reg_size);
731 }
732
733 long emufs_tipo1_get_file_size(EMUFS* efs, int* err)
734 {
735         long  file_size;
736         FILE* file;
737         char  name_f[255];
738
739         strcpy(name_f, efs->nombre);
740         strcat(name_f, ".dat");
741         if ((file = fopen(name_f, "ab")) == NULL) {
742                 /* TODO Manejo de errores */
743                 PERR("Error al abrir archivo");
744                 *err = 4; /* EMUFS_ERROR_CANT_OPEN_FILE */
745                 return 0;
746         }
747         file_size = ftell(file);
748         fclose(file);
749         return file_size;
750 }
751