]> git.llucax.com Git - z.facultad/75.06/emufs.git/blob - emufs/fsc.c
Se arregla un pedin que hacia que no compile (y la identacion).
[z.facultad/75.06/emufs.git] / emufs / fsc.c
1 /* vim: set noexpandtab tabstop=4 shiftwidth=4:
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:17:50 ART 2004
22  * Autores: Nicolás Dimov <sagardua@uolsinectis.com.ar>
23  *          Leandro Lucarella <llucare@fi.uba.ar>
24  *----------------------------------------------------------------------------
25  *
26  * $Id$
27  *
28  */
29
30 /** \file
31  *
32  * Archivo para administrar el espacio libre disponible.
33  * 
34  * Implementación del archivo para administrar el espacio libre disponible.
35  *
36  */
37
38 #include "fsc.h"
39 #include <unistd.h>
40 #include <sys/types.h>
41 #include <string.h>
42
43 /* Crea un archivo de Gaps o Espacio Libre en Bloque */
44 int emufs_fsc_crear(EMUFS* efs)
45 {
46         return emufs_crear_archivo_auxiliar(efs->nombre, EMUFS_FSC_EXT);
47 }
48
49 /* Agrega un registro al archivo de espacio libre en bloque. */
50 int emufs_fsc_agregar(EMUFS *emu, EMUFS_BLOCK_ID marker, EMUFS_FREE freespace)
51 {
52         FILE *f_fsc;
53         EMUFS_FSC reg;
54         char name_f_fsc[255];
55         
56         strcpy(name_f_fsc,emu->nombre);
57         strcat(name_f_fsc, EMUFS_FSC_EXT);
58         
59         /* Lo guardo en el archivo al final "a+"*/
60         if ( (f_fsc = fopen(name_f_fsc,"r+"))==NULL ) return -1;
61         /* lo busco.. si esta lo modifico y si no lo agrego */
62         fseek(f_fsc,0,SEEK_SET);
63         while ( !feof(f_fsc) ){
64                 if ( fread(&reg,sizeof(EMUFS_FSC),1,f_fsc) != 1) continue;
65                 if ( reg.marker == marker ){
66                         fseek(f_fsc,-sizeof(EMUFS_FSC),SEEK_CUR);
67                         reg.freespace = freespace;
68                         fwrite(&reg,sizeof(EMUFS_FSC),1,f_fsc);
69                         fclose(f_fsc);
70                         return 0;
71                 }
72         }
73         /* Cargo el registro */
74         reg.marker = marker;
75         reg.freespace = freespace;
76         fwrite(&reg,sizeof(EMUFS_FSC),1,f_fsc);
77         fclose(f_fsc);
78         return 0;
79 }
80
81 /* Agrega un GAP en el archivo de Gaps para Filetype 2 */
82 int emufs_fsc_agregar_gap(EMUFS *emu, EMUFS_OFFSET marker, EMUFS_FREE freespace)
83 {
84         FILE *f_fsc;
85         EMUFS_FSC gap_aux,gap_before,gap_after,gap_new;
86         char name_f_fsc[255];
87         EMUFS_REG_ID pos_gap_before = 0, pos_gap_after = 0;
88         unsigned long source,destination,limit,file_size,reg_count = 0,cant_moved = 0;
89         char found = 0;
90
91         strcpy(name_f_fsc,emu->nombre);
92         strcat(name_f_fsc, EMUFS_FSC_EXT);
93
94         gap_before.marker = -1;
95         gap_after.marker = -1;
96
97         /* Busco si hay un GAP por delante y/o por detras del que se esta por crear */
98         /* para en dicho caso realizar un merge! */
99         if ( (f_fsc = fopen(name_f_fsc,"r+")) == NULL) return -1; 
100         while ( !feof(f_fsc) ){
101                 if ( fread(&gap_aux,sizeof(EMUFS_FSC),1,f_fsc) != 1) continue;
102
103                 /* Chequeo si es un gap justo anterior al nuestro */
104                 if (gap_aux.marker+gap_aux.freespace == marker) {
105                         gap_before.marker = gap_aux.marker;
106                         gap_before.freespace = gap_aux.freespace;
107                         pos_gap_before = reg_count;
108                 }
109
110                 /* Chequeo si es un gap justo posterior al nuestro */           
111                 if (gap_aux.marker == marker+freespace) {
112                         gap_after.marker = gap_aux.marker;
113                         gap_after.freespace = gap_aux.freespace;
114                         pos_gap_after = reg_count;
115                 }               
116                 reg_count += 1;
117         }
118
119         /* Si no encontre gaps ni por delante ni por detras */
120         if ((gap_before.marker == -1) && (gap_after.marker == -1)) {
121                 /* Lo guardo ordenado donde deba ir */
122                 gap_new.marker = marker;
123                 gap_new.freespace = freespace;
124                 /* Busco el gap que sucede a este */
125                 fseek(f_fsc,0,SEEK_SET);
126                 while (!feof(f_fsc)) {
127                         fread(&gap_aux,sizeof(EMUFS_FSC),1,f_fsc);
128                         if (gap_aux.marker > gap_new.marker) {
129                                 found = 1;
130                                 break;
131                         }
132                 }
133                 if (found == 1) {
134                         /* Movemos todos los gaps desde el sucesor hasta el final, una pos adelante */
135                         limit = ftell(f_fsc) - sizeof(EMUFS_FSC);
136                         fseek(f_fsc,0,SEEK_END);
137                         reg_count = (ftell(f_fsc) - limit) / sizeof(EMUFS_FSC);                 
138                         source = ftell(f_fsc) - sizeof(EMUFS_FSC);
139
140                         while (cant_moved < reg_count)
141                         {
142                                 fseek(f_fsc,source,SEEK_SET);
143                                 fread(&gap_aux,sizeof(EMUFS_FSC),1,f_fsc);
144                                 fwrite(&gap_aux,sizeof(EMUFS_FSC),1,f_fsc);
145                                 source -= sizeof(EMUFS_FSC);
146                                 ++cant_moved;
147                         }
148                         /* Agrego el nuevo registro */
149                         fseek(f_fsc,limit,SEEK_SET);
150                         fwrite(&gap_new,sizeof(EMUFS_FSC),1,f_fsc);
151                 }
152                 else {
153                         fseek(f_fsc,0,SEEK_END);
154                         fwrite(&gap_new,sizeof(EMUFS_FSC),1,f_fsc);
155                 }
156
157                 fclose(f_fsc);          
158         }
159
160         /* Si encuentro un GAP Justo por delante pero no por detras */
161         if ((gap_before.marker != -1) && (gap_after.marker == -1))
162         {
163                 /* Me posiciono en el registro que indica dicho gap y lo reescribo con */
164                 /* la suma de los espacios libres */      
165                 fseek(f_fsc,sizeof(EMUFS_FSC)*pos_gap_before,0);
166                 gap_new.marker = gap_before.marker;
167                 gap_new.freespace = gap_before.freespace + freespace;
168                 fwrite(&gap_new,sizeof(EMUFS_FSC),1,f_fsc);
169                 fclose(f_fsc);
170         }
171
172         /* Si encuentro un GAP Justo por detras pero no por delante */
173         if ((gap_before.marker == -1) && (gap_after.marker != -1))
174         {  
175                 /* Me posiciono en el registro que indica dicho gap y lo reescribo con */
176                 /* los datos actualizados de offset y espacio */
177                 fseek(f_fsc,sizeof(EMUFS_FSC)*pos_gap_after,0);
178                 gap_new.marker = gap_after.marker - freespace;
179                 gap_new.freespace = gap_after.freespace + freespace;
180                 fwrite(&gap_new,sizeof(EMUFS_FSC),1,f_fsc);
181                 fclose(f_fsc);
182         }
183
184         /* Finalmente, si encuentro Justo por delante y por detras..*/
185         if ((gap_before.marker != -1) && (gap_after.marker != -1))
186         { 
187                 /* Guardo el nuevo GAP que posee los tres espacios sumados */
188                 if (pos_gap_before < pos_gap_after) {
189                         fseek(f_fsc,sizeof(EMUFS_FSC)*pos_gap_before,0);
190                         destination = sizeof(EMUFS_FSC)*pos_gap_after;
191                 }
192                 else {
193                         fseek(f_fsc,sizeof(EMUFS_FSC)*pos_gap_after,0);
194                         destination = sizeof(EMUFS_FSC)*pos_gap_before;
195                 }
196                 gap_new.marker = gap_before.marker;
197                 gap_new.freespace = gap_before.freespace + freespace + gap_after.freespace;
198                 fwrite(&gap_new,sizeof(EMUFS_FSC),1,f_fsc);
199
200                 /* Preparo el escenario para la movida de registros */
201                 source = destination+sizeof(EMUFS_FSC); /* Salteo el gap que elimino! */
202                 fseek(f_fsc,0,SEEK_END);
203                 file_size = ftell(f_fsc);
204                 reg_count = (file_size - source) / sizeof(EMUFS_FSC);
205
206                 /* Comienzo a mover */
207                 while (cant_moved < reg_count) {
208                         fseek(f_fsc,source,0);
209                         fread(&gap_new,sizeof(EMUFS_FSC),1,f_fsc);
210                         fseek(f_fsc,-sizeof(EMUFS_FSC)*2,SEEK_CUR);
211                         fwrite(&gap_new,sizeof(EMUFS_FSC),1,f_fsc);
212                         source += sizeof(EMUFS_FSC);            
213                         ++cant_moved;
214                 }
215                 fclose(f_fsc);
216                 truncate(name_f_fsc, file_size - sizeof(EMUFS_FSC));
217         }       
218
219     return 0;
220 }
221
222 /* Elimina un registro GAP del archivo de espacios libres (gaps) */
223 int emufs_fsc_remove_gap(EMUFS *emu, EMUFS_OFFSET marker)
224 {
225         FILE *f_fsc;
226         EMUFS_FSC gap_aux;
227         char name_f_fsc[255];
228     unsigned long source,destination,file_size,reg_count = 0,cant_moved = 0;    
229                 
230         strcpy(name_f_fsc,emu->nombre);
231         strcat(name_f_fsc, EMUFS_FSC_EXT);
232         
233         /* Busco el Gap en el .fsc */
234     if ((f_fsc = fopen(name_f_fsc,"r+")) == NULL) return -1; 
235         while ( !feof(f_fsc) ){
236                 if ( fread(&gap_aux,sizeof(EMUFS_FSC),1,f_fsc) != 1) continue;
237                 if ( gap_aux.marker == marker ) break;
238         }
239         
240         /* Preparo el escenario para la movida de registros */
241         fseek(f_fsc,-sizeof(EMUFS_FSC),SEEK_CUR);
242         destination = ftell(f_fsc);
243         source = destination+sizeof(EMUFS_FSC); /* Salteo el gap a eliminar! */
244         fseek(f_fsc,0,SEEK_END);
245         file_size = ftell(f_fsc);
246         reg_count = (file_size - source) / sizeof(EMUFS_FSC);
247                 
248         /* Comienzo a mover */
249         while (cant_moved < reg_count) {
250           fseek(f_fsc,source,0);
251       fread(&gap_aux,sizeof(EMUFS_FSC),1,f_fsc);
252           fseek(f_fsc,-sizeof(EMUFS_FSC)*2,SEEK_CUR);
253           fwrite(&gap_aux,sizeof(EMUFS_FSC),1,f_fsc);
254           source += sizeof(EMUFS_FSC);          
255           ++cant_moved;
256         }
257         fclose(f_fsc);
258         truncate(name_f_fsc, file_size - sizeof(EMUFS_FSC));
259         
260         return 0;
261 }
262
263 /* Objetivo: Actualiza un registro de espacio libre de acorde al FType */
264 int emufs_fsc_actualizar(EMUFS *emu, EMUFS_BLOCK_ID marker, EMUFS_FREE freespace)
265 {
266         FILE *f_fsc;
267         EMUFS_FSC reg;
268         char name_f_fsc[255];
269         
270         strcpy(name_f_fsc,emu->nombre);
271         strcat(name_f_fsc, EMUFS_FSC_EXT);
272
273         /*busco el bloque o gap que modifique*/
274         if ( (f_fsc = fopen(name_f_fsc,"r+")) == NULL) return -1; 
275         while ( !feof(f_fsc) ){
276                 if ( fread(&reg,sizeof(EMUFS_FSC),1,f_fsc) != 1) continue;
277                 if ( reg.marker == marker ){
278                         reg.freespace = freespace;
279                         fseek(f_fsc,-sizeof(EMUFS_FSC),SEEK_CUR);
280                         fwrite(&reg,sizeof(EMUFS_FSC),1,f_fsc);
281                         break;
282                 }
283         }
284         fclose(f_fsc);
285         return 0;
286 }
287
288 /* Actualiza un registro de gap, en el archivo de Gaps en Disco */
289 int emufs_fsc_actualizar_gap(EMUFS *emu, EMUFS_OFFSET marker, EMUFS_FREE freespace)
290 {
291         FILE *f_fsc;
292         EMUFS_FSC gap_aux;
293         char name_f_fsc[255];
294         
295         strcpy(name_f_fsc,emu->nombre);
296         strcat(name_f_fsc, EMUFS_FSC_EXT);
297
298         /*busco el bloque o gap que modifique*/
299         if ( (f_fsc = fopen(name_f_fsc,"r+")) == NULL) return -1; 
300         while ( !feof(f_fsc) ){
301                 if ( fread(&gap_aux,sizeof(EMUFS_FSC),1,f_fsc) != 1) continue;
302                 if ( gap_aux.marker == marker ){
303                         gap_aux.marker = marker + gap_aux.freespace - freespace;
304                         gap_aux.freespace = freespace;
305                         fseek(f_fsc,-sizeof(EMUFS_FSC),SEEK_CUR);
306                         fwrite(&gap_aux,sizeof(EMUFS_FSC),1,f_fsc);
307                         break;
308                 }
309         }
310         fclose(f_fsc);
311         return 0;
312 }
313
314 /* Me devuelve el ID del bloque u Offset del Gap donde quepa un registro, y guarda en n_freespace el espacio libre actualizado */
315 EMUFS_BLOCK_ID emufs_fsc_buscar_lugar(EMUFS *emu, EMUFS_FREE reg_size, EMUFS_FREE *freespace)
316 {
317         FILE *f_fsc;
318         EMUFS_FSC reg;
319         char name_f_fsc[255];
320         char found = 0;
321         
322         strcpy(name_f_fsc,emu->nombre);
323         strcat(name_f_fsc, EMUFS_FSC_EXT);
324
325         if ( (f_fsc = fopen(name_f_fsc,"r"))==NULL ) return EMUFS_NOT_FOUND;
326
327         if ( emu->tam_reg > emu->tam_bloque-sizeof(EMUFS_REG_ID) ){
328                 fseek(f_fsc,0,SEEK_SET);
329                 while(!feof(f_fsc)){
330                         if (fread(&reg,sizeof(EMUFS_FSC),1,f_fsc) != 1) continue;
331                         if (reg.freespace == emu->tam_bloque) {
332                                 fclose(f_fsc);
333                                 *freespace = reg.freespace;
334                                 return reg.marker;
335                         }
336                 }
337         }       
338         /* Inicializamos la estructura para devolver algun valor en concreto */
339         /* en caso de que no se halle un espacio libre apropiado */
340         while(!feof(f_fsc)){
341                 if (fread(&reg,sizeof(EMUFS_FSC),1,f_fsc) != 1) continue;
342                 if (reg.freespace >= reg_size) {
343                         found = 1;
344                         break;
345                 }
346         }
347         
348         /* Si salio por error o por fin de archivo y no encontro space... */
349         if (!found) {
350           reg.marker = EMUFS_NOT_FOUND;
351           *freespace = emu->tam_bloque; 
352         }
353         else *freespace = reg.freespace;
354         
355         fclose(f_fsc);
356         return reg.marker;
357 }
358
359 /** Busca n lugares consecutivos devolviendo el id del primer bloque. */
360 EMUFS_BLOCK_ID emufs_fsc_buscar_n_lugares(EMUFS* efs, size_t n,
361                 EMUFS_FREE reg_size, EMUFS_FREE *freespace, int* err)
362 {
363         FILE *f_fsc;
364         EMUFS_FSC reg;
365         char name_f_fsc[255];
366
367         /* chequeo que al menos se busque un lugar */
368         if (!n) {
369                 PERR("Se debe buscar al menos un lugar");
370                 *err = 13; /* EMUFS_ERROR_WRONG_ARGUMENT */
371                 return EMUFS_NOT_FOUND;
372         }
373
374         /* abre archivo */
375         strcpy(name_f_fsc, efs->nombre);
376         strcat(name_f_fsc, EMUFS_FSC_EXT);
377         if (!(f_fsc = fopen(name_f_fsc, "rb"))) {
378                 PERR("No se puede abrir archivo");
379                 *err = 4; /* EMUFS_ERROR_CANT_OPEN_FILE */
380                 return EMUFS_NOT_FOUND;
381         }
382
383         /* busca el espacio libre */
384         while(!feof(f_fsc)) {
385                 if ((fread(&reg, sizeof(EMUFS_FSC), 1, f_fsc) != 1)) {
386                         if (feof(f_fsc)) break;
387                         PERR("No se puede leer el archivo");
388                         *err = 3; /* EMUFS_ERROR_FILE_READ */
389                         return EMUFS_NOT_FOUND;
390                 }
391                 if (reg.freespace >= reg_size) {
392                         int found = 1;
393                         EMUFS_BLOCK_ID first_id = reg.marker;
394                         *freespace = reg.freespace;
395                         while (--n) {
396                                 if (fread(&reg, sizeof(EMUFS_FSC), 1, f_fsc) != 1) {
397                                         if (feof(f_fsc)) break;
398                                         PERR("No se puede leer el archivo");
399                                         *err = 3; /* EMUFS_ERROR_FILE_READ */
400                                         return EMUFS_NOT_FOUND;
401                                 }
402                                 /* no hay otro lugar consecutivo */
403                                 if (reg.freespace < reg_size) {
404                                         found = 0;
405                                         break;
406                                 }
407                         }
408                         if (found) {
409                                 fclose(f_fsc);
410                                 return first_id;
411                         }
412                 }
413         }
414         /* no se encontró espacio libre */
415         *freespace = efs->tam_bloque;
416         return EMUFS_NOT_FOUND;
417 }
418
419 /* Devuelve el espacio libre de un Bloque o Gap dado */
420 EMUFS_FREE emufs_fsc_get_fs(EMUFS *emu, EMUFS_BLOCK_ID marker)
421 {
422         FILE *f_fsc;
423         EMUFS_FSC reg;
424         char name_f_fsc[255];
425         
426         strcpy(name_f_fsc,emu->nombre);
427         strcat(name_f_fsc, EMUFS_FSC_EXT);
428
429         /* Busco el Bloque o Gap pedido y obtengo su espacio libre */
430         if ( (f_fsc = fopen(name_f_fsc,"r"))==NULL ) return -1;
431         while ( !feof(f_fsc) ){
432                 if ( fread(&reg,sizeof(EMUFS_FSC),1,f_fsc) != 1 ) continue;
433                 if ( reg.marker == marker )
434                         break;
435         }
436                 
437         fclose(f_fsc);
438         return reg.freespace;
439 }
440
441 EMUFS_FREE emufs_fsc_get_total_fs(EMUFS *emu)
442 {
443         FILE *f_fsc;
444         EMUFS_FSC reg;
445         char name_f_fsc[255];
446         EMUFS_FREE total;
447         
448         strcpy(name_f_fsc,emu->nombre);
449         strcat(name_f_fsc, EMUFS_FSC_EXT);
450
451         if ( (f_fsc = fopen(name_f_fsc,"r"))==NULL ) return -1;
452         total = 0;
453         while ( !feof(f_fsc) ){
454                 if ( fread(&reg, sizeof(EMUFS_FSC), 1, f_fsc) != 1) continue;
455                 total += reg.freespace;
456         }
457         fclose(f_fsc);
458         return total;
459 }
460
461 int emufs_fsc_get_max_min_fs(EMUFS *emu, EMUFS_FREE *min, EMUFS_FREE *max)
462 {
463         FILE *f_fsc;
464         EMUFS_FSC reg;
465         char name_f_fsc[255];
466         
467         strcpy(name_f_fsc,emu->nombre);
468         strcat(name_f_fsc, EMUFS_FSC_EXT);
469
470         if ( (f_fsc = fopen(name_f_fsc,"r"))==NULL ) return -1;
471                 
472         /* Si el file esta vacio, devuelvo valores nulos */
473         fseek(f_fsc,0,SEEK_END);
474         if (ftell(f_fsc) == 0) {
475                 *min = 0;
476                 *max = 0;
477                 return 0;               
478         }
479         else
480         {
481                 /* Busco Min y Max */
482                 *min = ULONG_MAX;
483                 *max = 0;               
484                 fseek(f_fsc,0,SEEK_SET);                
485                 while ( !feof(f_fsc) ){
486                         if ( fread(&reg, sizeof(EMUFS_FSC), 1, f_fsc) != 1) continue;
487                         if (  reg.freespace < *min )
488                                 *min = reg.freespace;
489                         if ( reg.freespace > *max )
490                                 *max = reg.freespace;
491                 }
492                 fclose(f_fsc);
493                 return 0;               
494         }
495 }
496
497 EMUFS_FREE emufs_fsc_get_media_fs(EMUFS *emu)
498 {
499         FILE *f_fsc;
500         EMUFS_FSC reg;
501         char name_f_fsc[255];
502         EMUFS_FREE total_fs = 0;
503         EMUFS_REG_ID gap_count = 0;
504         
505         strcpy(name_f_fsc,emu->nombre);
506         strcat(name_f_fsc, EMUFS_FSC_EXT);
507
508         if ( (f_fsc = fopen(name_f_fsc,"r"))==NULL ) return -1;
509         
510         while ( !feof(f_fsc) ){
511                 if ( fread(&reg, sizeof(EMUFS_FSC), 1, f_fsc) != 1) continue;           
512                 total_fs += reg.freespace;
513                 ++gap_count;
514         }
515
516         fclose(f_fsc);
517         
518         if (gap_count > 0) return total_fs/gap_count;
519         else return 0;
520 }
521
522 EMUFS_BLOCK_ID emufs_fsc_get_cant_bloques_vacios(EMUFS *emu)
523 {
524         FILE *f_fsc;
525         EMUFS_FSC reg;
526         char name_f_fsc[255];
527         EMUFS_BLOCK_ID cant=0;
528         
529         strcpy(name_f_fsc,emu->nombre);
530         strcat(name_f_fsc, EMUFS_FSC_EXT);
531
532         if ( (f_fsc = fopen(name_f_fsc,"r"))==NULL ) return -1;
533         while ( !feof(f_fsc) ){
534                 fread(&reg, sizeof(EMUFS_FSC), 1, f_fsc);
535                 if ( reg.freespace == emu->tam_bloque )
536                         cant++;
537         }
538                 
539         fclose(f_fsc);
540         return cant;
541 }
542
543 int emufs_fsc_truncate(EMUFS* efs, EMUFS_BLOCK_ID blocks)
544 {
545         char name_f_fsc[255];
546
547         strcpy(name_f_fsc, efs->nombre);
548         strcat(name_f_fsc, EMUFS_FSC_EXT);
549         return truncate(name_f_fsc, blocks * sizeof(EMUFS_FSC));
550 }
551
552 EMUFS_BLOCK_ID emufs_fsc_get_num_blocks(EMUFS* efs)
553 {
554         FILE *f_fsc;
555         char name_f_fsc[255];
556         EMUFS_BLOCK_ID cant = 0;
557         
558         strcpy(name_f_fsc, efs->nombre);
559         strcat(name_f_fsc, EMUFS_FSC_EXT);
560
561         if (!(f_fsc = fopen(name_f_fsc, "ab"))) {
562                 PERR("error al abrir archivo .fsc");
563                 return EMUFS_NOT_FOUND;
564         }
565         cant = ftell(f_fsc) / sizeof(EMUFS_FSC);
566         fclose(f_fsc);
567         return cant;
568 }
569