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