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