]> git.llucax.com Git - z.facultad/75.06/emufs.git/blob - emufs/tipo1.c
596f69abb375babcebb2214fdc75feeef79bb364
[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));
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(registro, block+sizeof(EMUFS_TIPO1_REG_HEADER), efs->tam_bloque);
247                                 chunk_ptr += efs->tam_bloque;
248                                 free(block);
249                         }
250                         /* Todo listo! */
251                         break;
252                 }
253                 /* Desplazo el offset */
254                 offset += curr_reg_header.size;
255         } while (offset < efs->tam_bloque);
256
257         return registro;
258 }
259
260 void* emufs_tipo1_leer_bloque(EMUFS* efs, EMUFS_BLOCK_ID block_id, int *err)
261 {
262         FILE* file;
263         char* block; /* bloque leido (en donde está el registro a leer) */
264         char  name_f[255];
265
266         strcpy(name_f,efs->nombre);
267         strcat(name_f,".dat");
268
269         if ((file = fopen(name_f, "r")) == NULL) {
270                 PERR("No se puede abrir archivo");
271                 *err = 4; /* EMUFS_ERROR_CANT_OPEN_FILE */
272                 return NULL; /* FIXME ERROR */
273         }
274         emufs_tipo1_header_jump(file); /* salta cabeceras */
275         emufs_tipo1_block_jump(efs, file, block_id); /* salta bloques */
276         /* FIXME: verificar que no se pase de fin de archivo*/
277         block = (char*) malloc(efs->tam_bloque);
278         if (block == NULL) {
279                 /* TODO Manejo de errores */
280                 PERR("No hay memoria");
281                 *err = 2; /* EMUFS_ERROR_OUT_OF_MEMORY */
282                 return NULL;
283         }
284         if (fread(block, efs->tam_bloque, 1, file) != 1) {
285                 /* TODO Manejo de errores */
286                 free(block);
287                 PERR("Error al leer bloque");
288                 *err = 3; /* EMUFS_ERROR_FILE_READ */
289                 return NULL;
290         }
291         fclose(file);
292         return block;
293 }
294
295 EMUFS_REG_ID emufs_tipo1_grabar_registro(EMUFS* efs, void* reg, EMUFS_REG_SIZE reg_size, int* err)
296 {
297         EMUFS_TIPO1_REG_HEADER header; /* cabecera del registro a leer */
298         /* obtengo identificador que corresponderá al registro */
299         header.id = emufs_idx_get_new_id(efs, err);
300         if (*err) {
301                 PERR("no se pudo obtener un id para el registro nuevo");
302                 return EMUFS_NOT_FOUND;
303         }
304         header.size = reg_size; /* tamaño del registro */
305         if ((*err = emufs_tipo1_grabar_registro_con_id(efs, reg, header))) {
306                 PERR("error al grabar el registro");
307                 return EMUFS_NOT_FOUND;
308         }
309         return header.id;
310 }
311
312 int emufs_tipo1_borrar_registro(EMUFS* efs, EMUFS_REG_ID reg_id)
313 {
314         char* block; /* bloque leido (en donde está el registro a leer) */
315         EMUFS_BLOCK_ID block_id; /* id del bloque en donde esta el registro a leer */
316         EMUFS_BLOCK_SIZE offset; /* offset del bloque leído */
317         EMUFS_TIPO1_REG_HEADER curr_reg_header; /* cabecera del registro a leer */
318         int err = 0; /* para almacenar código de error */
319
320         block_id = emufs_idx_buscar_registro(efs, reg_id);
321         if (block_id == EMUFS_NOT_FOUND) {
322                 /* TODO Manejo de errores */
323                 PERR("Registro no encontrado");
324                 return EMUFS_NOT_FOUND;
325         }
326         if (!(block = (char*) emufs_tipo1_leer_bloque(efs, block_id, &err))) {
327                 /* TODO Manejo de errores */
328                 PERR("no se pudo reservar memoria");
329                 return err;
330         }
331
332         /* Busco secuencialmente en el bloque el registro a leer */
333         offset = 0;
334         do {
335                 /* Copio la cabecera del registro actual. */
336                 memcpy(&curr_reg_header, block + offset, sizeof(EMUFS_TIPO1_REG_HEADER));
337                 if (curr_reg_header.id == reg_id) {
338                         /* identificador del bloque actual */
339                         EMUFS_BLOCK_ID curr_block_id = block_id;
340                         /* tamaño máximo ultilizable para datos en un bloque */
341                         EMUFS_BLOCK_SIZE block_space
342                                         = efs->tam_bloque - sizeof(EMUFS_TIPO1_REG_HEADER);
343                         EMUFS_FREE fs; /* cantidad de espacio libre en el bloque */
344
345                         while (1) {
346                                 /* actualizo archivo de espacio libre por bloque */
347                                 fs = emufs_fsc_get_fs(efs, curr_block_id)
348                                         + MIN(curr_reg_header.size, block_space)
349                                         + sizeof(EMUFS_TIPO1_REG_HEADER);
350                                 if ((err = emufs_fsc_actualizar(efs, curr_block_id, fs))) {
351                                         /* TODO Manejo de errores */
352                                         PERR("no se pudo actualizar .fsc");
353                                         free(block);
354                                         return err;
355                                 }
356                                 /* falta liberar un bloque (o porción) */
357                                 if (curr_reg_header.size > block_space) {
358                                         free(block);
359                                         if (!(block = (char*) emufs_tipo1_leer_bloque(efs,
360                                                                         ++curr_block_id, &err))) {
361                                                 /* TODO Manejo de errores */
362                                                 PERR("no se pudo leer el bloque");
363                                                 return err;
364                                         }
365                                         /* copio la cabecera del primer registro (si ocupa más de un
366                                          * registro está en bloques contiguos) */
367                                         memcpy(&curr_reg_header, block,
368                                                         sizeof(EMUFS_TIPO1_REG_HEADER));
369                                 } else { /* se terminó de leer */
370                                         break;
371                                 }
372                         }
373
374                         /* actualizo archivo de identificadores de registros borrados */
375                         if ((err = emufs_did_agregar(efs, reg_id))) {
376                                 /* TODO Manejo de errores */
377                                 PERR("no se pudo actualizar .did");
378                                 free(block);
379                                 return err;
380                         }
381                         /*actualizo archivo .idx*/
382                         if ((err = emufs_idx_borrar(efs, reg_id))) {
383                                 /* TODO Manejo de errores */
384                                 PERR("no se pudo actualizar .did");
385                                 free(block);
386                                 return err;
387                         }
388
389                         /* desplazo registros a izquierda */
390                         {   /* offset del fin del registro a borrar */
391                                 EMUFS_BLOCK_SIZE offset_reg_end = offset
392                                         + sizeof(EMUFS_TIPO1_REG_HEADER) + curr_reg_header.size;
393                                 /* si es necesario desplazar */
394                                 if (offset < offset_reg_end) {
395                                         /* muevo la porción de bloque a izquierda */
396                                         memcpy(block + offset, block + offset_reg_end,
397                                                 efs->tam_bloque - offset_reg_end);
398                                 }
399                         }
400                         /* guardo el bloque en disco */
401                         emufs_tipo1_grabar_bloque(efs, block, curr_block_id, &err);
402                         if (err) {
403                                 /* TODO Manejo de errores */
404                                 PERR("no se pudo grabar bloque en disco");
405                                 free(block);
406                                 return err;
407                         }
408
409                         break; /* salgo del loop, ya hice todo lo que tenía que hacer */
410                 }
411                 /* desplazo el offset */
412                 offset += sizeof(EMUFS_TIPO1_REG_HEADER) + curr_reg_header.size;
413
414         /* esto no debería ser nunca false porque sé positivamente que el */
415         } while (offset < efs->tam_bloque); /* registro está en el bloque */
416
417         free(block);
418         return 0; /* EMUFS_OK */
419 }
420
421 EMUFS_Estadisticas emufs_tipo1_leer_estadisticas(EMUFS* efs)
422 {
423         int err = 0;
424         EMUFS_Estadisticas stats;
425         memset(&stats, 0, sizeof(EMUFS_Estadisticas));
426
427         stats.tam_archivo_bytes = emufs_tipo1_get_file_size(efs, &err);
428         if (err) {
429                 /* TODO manejo de errores */
430                 PERR("no se pudo obtener el tamaño del archivo");
431                 return stats;
432         }
433
434         /* obtengo cantidad de bloques */
435         stats.cant_bloques = (stats.tam_archivo_bytes - emufs_tipo1_header_size())
436                         / efs->tam_bloque;
437
438         /* obtengo la cantidad de registros en el archivo */
439         {
440                 EMUFS_REG_ID *tmp = emufs_idx_get(efs, &stats.tam_archivo);
441                 if (tmp) free(tmp); /* libera memoria innecesaria */
442         }
443
444         /* obtengo total de información de control que guarda el archivo */
445         stats.info_control = emufs_tipo1_header_size() /* cabecera del archivo */
446                         /* mas las cabeceras de todos los registros */
447                         + stats.tam_archivo * sizeof(EMUFS_TIPO1_REG_HEADER);
448
449         /* obtengo las estadísticas del archivo de espacio libre por bloque */
450         stats.total_fs = emufs_fsc_get_total_fs(efs);
451         stats.media_fs = emufs_fsc_get_media_fs(efs);
452         emufs_fsc_get_max_min_fs(efs, &stats.min_fs, &stats.max_fs);
453
454         return stats;   
455 }
456
457 void emufs_tipo1_compactar(EMUFS* efs)
458 {
459         EMUFS_REG_ID total_ids; /* cantidad total de registros en el array */
460         /* array con los identificadores de los registros */
461         EMUFS_REG_ID* reg_ids = emufs_idx_get(efs, &total_ids);
462         int i; /* índice de elemento actual del array */
463
464         /* recorro cada registro válido del archivo */
465         for (i = 0; i < total_ids; ++i) {
466                 EMUFS_FREE fs; /* espacio libre en el bloque */
467                 EMUFS_TIPO1_REG_HEADER header; /* cabecera del registro a leer */
468                 int err = 0; /* para almacenar código de error */
469                 /* bloque al que pertenece el registro */
470                 EMUFS_BLOCK_ID block_id = emufs_idx_buscar_registro(efs, reg_ids[i]);
471                 /* obtengo un bloque con espacio suficiente para almacenarlo */
472                 EMUFS_BLOCK_ID free_block_id = emufs_fsc_buscar_lugar(efs,
473                                 sizeof(EMUFS_TIPO1_REG_HEADER) + reg_ids[i], &fs);
474
475                 /* si el registro está borrado, continúo con el próximo */
476                 if (block_id == EMUFS_NOT_FOUND) {
477                         continue;
478                 }
479                 /* obtengo identificador del registro actual */
480                 header.id = reg_ids[i];
481                 /* TODO analizar caso de registros multibloque */
482                 /* si el bloque obtenido está antes del bloque actual */
483                 if (free_block_id < block_id) {
484                         /* leo el registro */
485                         char* reg = (char*) efs->leer_registro(efs, header.id,
486                                         &header.size, &err);
487                         if (err) {
488                                 PERR("error al leer registro");
489                                 return;
490                         }
491                         /* borro el registro */
492                         if ((err = efs->borrar_registro(efs, header.id))) {
493                                 PERR("error al borrar registro");
494                                 free(reg);
495                                 return;
496                         }
497                         /* lo inserto en la nueva posición */
498                         if ((err = emufs_tipo1_grabar_registro_con_id(efs, reg, header))) {
499                                 PERR("error al borrar registro");
500                                 free(reg);
501                                 return;
502                         }
503                         free(reg);
504                 }
505         }
506         free(reg_ids); /* libero lista de ids */
507
508         /* truncamos el archivo si hay bloques libres al final */
509         {
510                 EMUFS_FREE fs; /* espacio libre en el bloque */
511                 /* busco si hay algún bloque completo libre */
512                 EMUFS_BLOCK_ID block_id = emufs_fsc_buscar_lugar(efs, efs->tam_bloque
513                                 - sizeof(EMUFS_TIPO1_REG_HEADER), &fs);
514                 /* si hay, el resto del archivo tiene que estar vacío */
515                 if (block_id != EMUFS_NOT_FOUND) {
516                         long size = emufs_tipo1_header_size() /* cabecera del archivo */
517                                 + block_id * efs->tam_bloque; /* mas los bloques compactos */
518                         char filename[255];
519
520                         /* trunca archivo de datos */
521                         strcpy(filename, efs->nombre);
522                         strcat(filename, ".dat");
523                         truncate(filename, size);
524                         /* trunca archivo de de espacio libre */
525                         emufs_fsc_truncate(efs, block_id);
526                 }
527         }
528 }
529
530 EMUFS_BLOCK_ID emufs_tipo1_grabar_bloque(EMUFS *efs, void *block,
531                 EMUFS_BLOCK_ID block_id, int* err)
532 {
533         FILE* file;
534         char name_f[255];
535
536         strcpy(name_f,efs->nombre);
537         strcat(name_f,".dat");
538
539         if ((file = fopen(name_f, "r+b")) == NULL) {
540                 /* TODO Manejo de errores */
541                 PERR("Error al abrir archivo");
542                 *err = 4; /* EMUFS_ERROR_CANT_OPEN_FILE */
543                 return EMUFS_NOT_FOUND;
544         }
545         /* Si es NOT_FOUND tengo que agregar un bloque al final del archivo */
546         if (block_id == EMUFS_NOT_FOUND) {
547                 /* me paro al final del archivo */
548                 if (fseek(file, 0l, SEEK_END)) {
549                         /* TODO Manejo de errores */
550                         PERR("No se pudo hacer fseek()");
551                         fclose(file);
552                         *err = 8; /* EMUFS_ERROR_SEEK_FILE */
553                         return EMUFS_NOT_FOUND;
554                 }
555                 /* Obtengo ID del bloque nuevo */
556                 block_id = (ftell(file) - emufs_tipo1_header_size()) / efs->tam_bloque;
557         /* Si es un ID válido, salto hasta ese bloque. */
558         } else {
559                 /* Salta el header del archivo */
560                 if ((*err = emufs_tipo1_header_jump(file))) {
561                         PERR("no se pudo saltar la cabecera del archivo");
562                         fclose(file);
563                         return EMUFS_NOT_FOUND;
564                 }
565                 /* Salta bloques */
566                 if ((*err = emufs_tipo1_block_jump(efs, file, block_id))) {
567                         PERR("no se pudo saltar la cabecera del bloque");
568                         fclose(file);
569                         return EMUFS_NOT_FOUND;
570                 }
571         }
572         /* Grabo el bloque */
573         if (fwrite(block, efs->tam_bloque, 1, file) != 1) {
574                 PERR("No se pudo escribir el archivo");
575                 fclose(file);
576                 *err = 6; /* EMUFS_ERROR_WRITE_FILE */
577                 return EMUFS_NOT_FOUND;
578         }
579
580         fclose(file);
581         return block_id;
582 }
583
584 static int emufs_tipo1_grabar_registro_con_id(EMUFS* efs, void* reg,
585                 EMUFS_TIPO1_REG_HEADER header)
586 {
587         EMUFS_FREE     fs; /* espacio libre en el bloque */
588         EMUFS_BLOCK_ID block_id; /* identificador del 1er bloque */
589         char*          block; /* buffer del bloque a guardar en disco */
590         char           name_f[255];
591         int            err = 0;
592
593         strcpy(name_f, efs->nombre);
594         strcat(name_f, ".dat");
595
596         /* busco lugar para el registro en un bloque existente */
597         block_id = emufs_fsc_buscar_lugar(efs, sizeof(EMUFS_TIPO1_REG_HEADER)
598                         + header.size, &fs);
599         /* si no hay bloques con suficiente espacio creo un bloque nuevo */
600         if (block_id == EMUFS_NOT_FOUND) {
601                 /* tamaño máximo ultilizable para datos en un bloque */
602                 EMUFS_BLOCK_SIZE block_space = efs->tam_bloque - sizeof(EMUFS_TIPO1_REG_HEADER);
603                 /* identificador del bloque que se guarda */
604                 EMUFS_BLOCK_ID curr_block_id = EMUFS_NOT_FOUND;
605                 /* tamaño de la porción de registro que se guarda */
606                 EMUFS_REG_SIZE chunk_size = 0; 
607                 /* puntero a la poción del registro */
608                 char* chunk_ptr = reg; 
609
610                 /* crear un nuevo bloque en memoria */
611                 block = (char*) malloc(efs->tam_bloque);
612                 if (block == NULL) {
613                         /* TODO Manejo de errores */
614                         PERR("No hay memoria");
615                         return 2; /* EMUFS_ERROR_OUT_OF_MEMORY */
616                 }
617                 do {
618                         memset(block, 0, efs->tam_bloque); /* inicializa bloque */
619                         chunk_ptr += chunk_size; /* Avanzo para guardar prox chunk */
620                         header.size -= chunk_size; /* Resto lo que ya guardé */
621                         chunk_size = MIN(header.size, block_space);
622                         /* graba porción del registro en bloque */
623                         emufs_tipo1_escribir_reg_chunk_en_memoria(block, header, chunk_ptr, chunk_size);
624                         /* graba el bloque en el archivo */
625                         curr_block_id = emufs_tipo1_grabar_bloque(efs, block, EMUFS_NOT_FOUND, &err);
626                         if (err) {
627                                 PERR("error al grabar bloque");
628                                 free(block);
629                                 return err;
630                         }
631                         /* grabo el nuevo registro en el archivo de espacios libres */
632                         err = emufs_fsc_agregar(efs, curr_block_id, block_space - chunk_size);
633                         if (err) {
634                                 PERR("No se pudo agregar fsc");
635                                 free(block);
636                                 return err;
637                         }
638                         /* si es el primer id de bloque obtenido, lo guardo para
639                          * agregarlo después al archivo de índices. */
640                         if (block_id == EMUFS_NOT_FOUND) {
641                                 block_id = curr_block_id;
642                         }
643                 } while (header.size > block_space);
644                 free(block);
645
646         /* Encontró espacio en un bloque existente, graba registro ahí */
647         } else {
648                 /* cargo el bloque en block_id */
649                 if (!(block = (char*) emufs_tipo1_leer_bloque(efs, block_id, &err))) {
650                         /* TODO Manejo de errores */
651                         PERR("no se pudo leer el bloque");
652                         return err;
653                 }
654                 /* graba registro en bloque */
655                 emufs_tipo1_escribir_reg_en_memoria(block + efs->tam_bloque - fs,
656                                 header, reg);
657                 /* graba el bloque en el archivo */
658                 block_id = emufs_tipo1_grabar_bloque(efs, block, block_id, &err);
659                 if (err) {
660                         PERR("error al grabar bloque");
661                         free(block);
662                         return err;
663                 }
664                 free(block);
665                 /* actualizo el archivo de espacios libres */
666                 err = emufs_fsc_actualizar(efs, block_id, fs - header.size
667                                 - sizeof(EMUFS_TIPO1_REG_HEADER));
668                 if (err) {
669                         PERR("No se pudo actualizar fsc");
670                         return err;
671                 }
672         }
673                 
674         /* actualizo el indice de bloques y registros */
675         err = emufs_idx_agregar(efs, header.id, block_id);
676         if (err){
677                 PERR("No se pudo agregar idx");
678                 return err;
679         }
680         
681         return err;
682 }
683
684 EMUFS_REG_ID emufs_tipo1_modificar_registro(EMUFS *emu, EMUFS_REG_ID id,
685                 void *data, EMUFS_REG_SIZE size, int *error)
686 {
687         emufs_tipo1_borrar_registro(emu, id);
688         return emufs_tipo1_grabar_registro(emu, data, size, error);
689 }
690
691 size_t emufs_tipo1_header_size(void)
692 {
693         return sizeof(EMUFS_Tipo) + sizeof(EMUFS_BLOCK_SIZE);
694 }
695
696 int emufs_tipo1_header_jump(FILE* fp)
697 {
698         if (fseek(fp, emufs_tipo1_header_size(), SEEK_CUR)) {
699                 PERR("No se pudo hacer fseek()");
700                 return 8; /* EMUFS_ERROR_SEEK_FILE */
701         }
702         return 0; /* EMUFS_OK */
703 }
704
705 int emufs_tipo1_block_jump(EMUFS* efs, FILE* fp, EMUFS_BLOCK_ID block_count)
706 {
707         if (fseek(fp, block_count * efs->tam_bloque, SEEK_CUR)) {
708                 PERR("No se pudo hacer fseek()");
709                 return 8; /* EMUFS_ERROR_SEEK_FILE */
710         }
711         return 0; /* EMUFS_OK */
712 }
713
714 void emufs_tipo1_escribir_reg_en_memoria(char* dst, EMUFS_TIPO1_REG_HEADER header,
715                 char* reg)
716 {
717         emufs_tipo1_escribir_reg_chunk_en_memoria(dst, header, reg, header.size);
718 }
719
720 void emufs_tipo1_escribir_reg_chunk_en_memoria(char* dst,
721                 EMUFS_TIPO1_REG_HEADER header, char* reg, EMUFS_REG_SIZE reg_size)
722 {
723         /* grabo cabecera del registro en el bloque */
724         memcpy(dst, &header, sizeof(EMUFS_TIPO1_REG_HEADER));
725         /* incremento puntero de escritura */
726         dst += sizeof(EMUFS_TIPO1_REG_HEADER);
727         /* grabo el registro en el bloque */
728         memcpy(dst, reg, reg_size);
729 }
730
731 long emufs_tipo1_get_file_size(EMUFS* efs, int* err)
732 {
733         long  file_size;
734         FILE* file;
735         char  name_f[255];
736
737         strcpy(name_f, efs->nombre);
738         strcat(name_f, ".dat");
739         if ((file = fopen(name_f, "ab")) == NULL) {
740                 /* TODO Manejo de errores */
741                 PERR("Error al abrir archivo");
742                 *err = 4; /* EMUFS_ERROR_CANT_OPEN_FILE */
743                 return 0;
744         }
745         file_size = ftell(file);
746         fclose(file);
747         return file_size;
748 }
749