1 /*
2 * Copyright (C) 2021 Jakub Kruszona-Zawadzki, Core Technology Sp. z o.o.
3 *
4 * This file is part of MooseFS.
5 *
6 * MooseFS is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 2 (only).
9 *
10 * MooseFS is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with MooseFS; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA
18 * or visit http://www.gnu.org/licenses/gpl-2.0.html
19 */
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25
26 #include "datapack.h"
27 #include "MFSCommunication.h"
28 #include "liset64.h"
29 #include "idstr.h"
30
31 // #define DEBUG 1
32
33 #define PROGRESS_PRINT 100000
34
35 //static uint8_t humode=0;
36 //static uint8_t numbermode=0;
37
38 enum {
39 STATUS_OK = 0,
40 STATUS_ENOENT = 1,
41 STATUS_ANY = 2
42 };
43
44 #define PHN_USESI 0x01
45 #define PHN_USEIEC 0x00
mfs_humanize_number(uint64_t number,uint8_t flags)46 char* mfs_humanize_number(uint64_t number,uint8_t flags) {
47 static char numbuf[6]; // [ "xxx" , "xx" , "x" , "x.x" ] + ["" , "X" , "Xi"]
48 uint64_t divisor;
49 uint16_t b;
50 uint8_t i;
51 uint8_t scale;
52
53 if (flags & PHN_USESI) {
54 divisor = 1000;
55 } else {
56 divisor = 1024;
57 }
58 if (number>(UINT64_MAX/100)) {
59 number /= divisor;
60 number *= 100;
61 scale = 1;
62 } else {
63 number *= 100;
64 scale = 0;
65 }
66 while (number>=99950) {
67 number /= divisor;
68 scale+=1;
69 }
70 i=0;
71 if (number<995 && scale>0) {
72 b = ((uint32_t)number + 5) / 10;
73 numbuf[i++]=(b/10)+'0';
74 numbuf[i++]='.';
75 numbuf[i++]=(b%10)+'0';
76 } else {
77 b = ((uint32_t)number + 50) / 100;
78 if (b>=100) {
79 numbuf[i++]=(b/100)+'0';
80 b%=100;
81 }
82 if (b>=10 || i>0) {
83 numbuf[i++]=(b/10)+'0';
84 b%=10;
85 }
86 numbuf[i++]=b+'0';
87 }
88 if (scale>0) {
89 if (flags&PHN_USESI) {
90 numbuf[i++]="-kMGTPE"[scale];
91 } else {
92 numbuf[i++]="-KMGTPE"[scale];
93 numbuf[i++]='i';
94 }
95 }
96 numbuf[i++]='\0';
97 return numbuf;
98 }
99
100 typedef struct _metasection {
101 off_t offset;
102 uint64_t length;
103 uint8_t mver;
104 } metasection;
105
106 typedef struct _stats {
107 uint32_t files;
108 uint32_t dirs;
109 uint64_t kchunks;
110 uint64_t achunks;
111 uint64_t length;
112 uint64_t size;
113 uint64_t keeprsize;
114 uint64_t archrsize;
115 uint64_t rsize;
116 } stats;
117
118 typedef struct _dirinfostate {
119 stats s;
120 int inode_liset;
121 int chunk_liset;
122 char *path;
123 const char *pptr;
124 uint32_t inode;
125 uint16_t nleng;
126 uint8_t wantchunks;
127 uint8_t pathscan;
128 uint8_t parent_found;
129 uint8_t nextsection;
130 uint8_t status;
131 struct _dirinfostate *next;
132 } dirinfostate;
133
134 static dirinfostate *dishead;
135
136 static int achunk_liset;
137
138 static uint8_t sclass_keep_factor[256];
139 static uint8_t sclass_arch_factor[256];
140
scan_sclass(FILE * fd,metasection * sdata)141 int scan_sclass(FILE *fd,metasection *sdata) {
142 uint8_t buff[11];
143 const uint8_t *rptr;
144 uint8_t i,l;
145 uint8_t ogroup;
146 uint8_t nleng;
147 uint16_t sclassid;
148 uint8_t createcnt;
149 uint8_t keepcnt;
150 uint8_t archcnt;
151 uint32_t chunkcount;
152
153 fseeko(fd,sdata->offset,SEEK_SET);
154
155 for (sclassid=0 ; sclassid<256 ; sclassid++) {
156 sclass_keep_factor[sclassid]=(sclassid<10)?sclassid:0;
157 sclass_arch_factor[sclassid]=(sclassid<10)?sclassid:0;
158 }
159
160 if (sdata->mver>0x16) {
161 fprintf(stderr,"unsupported sclass format\n");
162 return -1;
163 }
164 if (sdata->mver<0x15) {
165 for (i=0 ; i<26 ; i++) {
166 l = fgetc(fd);
167 if (l>128) {
168 fprintf(stderr,"sclass section malformed\n");
169 return -1;
170 }
171 fseeko(fd,l,SEEK_CUR);
172 }
173 }
174 if (sdata->mver==0x10) {
175 ogroup = 1;
176 } else {
177 ogroup = fgetc(fd);
178 }
179 if (ogroup<1) {
180 fprintf(stderr,"sclass section malformed\n");
181 return -1;
182 }
183 l = (sdata->mver==0x12)?11:(sdata->mver<=0x13)?3:(sdata->mver<=0x14)?5:(sdata->mver<=0x15)?8:10;
184 while (1) {
185 if (fread(buff,1,l,fd)!=l) {
186 fprintf(stderr,"loading labelset: read error\n");
187 return -1;
188 }
189 rptr = buff;
190 sclassid = get16bit(&rptr);
191 if (sdata->mver>0x15) {
192 nleng = get8bit(&rptr);
193 rptr+=4; // skip admin_only,create_mode,arch_delay
194 createcnt = get8bit(&rptr);
195 keepcnt = get8bit(&rptr);
196 archcnt = get8bit(&rptr);
197 chunkcount = 0;
198 } else if (sdata->mver>0x14) {
199 nleng = 0;
200 rptr+=2; // skip create_mode,arch_delay
201 createcnt = get8bit(&rptr);
202 keepcnt = get8bit(&rptr);
203 archcnt = get8bit(&rptr);
204 chunkcount = 0;
205 } else if (sdata->mver>0x13) {
206 nleng = 0;
207 rptr++; // skip create_mode
208 createcnt = get8bit(&rptr);
209 keepcnt = get8bit(&rptr);
210 archcnt = keepcnt;
211 chunkcount = 0;
212 } else {
213 nleng = 0;
214 createcnt = get8bit(&rptr);
215 keepcnt = createcnt;
216 archcnt = createcnt;
217 if (sdata->mver==0x12) {
218 chunkcount = get32bit(&rptr);
219 } else {
220 chunkcount = 0;
221 }
222 }
223 if (nleng>0) {
224 fseeko(fd,nleng,SEEK_CUR);
225 }
226 if (sclassid==0 && createcnt==0 && keepcnt==0 && archcnt==0) {
227 break;
228 }
229 if (keepcnt==0 || keepcnt>9 || archcnt==0 || archcnt>9) {
230 fprintf(stderr,"sclass section malformed\n");
231 return -1;
232 }
233 if (sdata->mver>0x14) {
234 fseeko(fd,(createcnt+keepcnt+archcnt)*4*ogroup,SEEK_CUR);
235 } else if (sdata->mver>0x13) {
236 fseeko(fd,(createcnt+keepcnt)*4*ogroup,SEEK_CUR);
237 } else {
238 fseeko(fd,createcnt*4*ogroup,SEEK_CUR);
239 }
240 if (chunkcount>0) {
241 fseeko(fd,chunkcount*8,SEEK_CUR);
242 }
243 sclass_keep_factor[sclassid] = keepcnt;
244 sclass_arch_factor[sclassid] = archcnt;
245 }
246 return 0;
247 }
248
249 // mver 0x13 and 0x14
scan_nodes(FILE * fd,metasection * sdata)250 int scan_nodes(FILE *fd,metasection *sdata) {
251 uint8_t buff[14];
252 const uint8_t *rptr;
253 uint8_t type;
254 uint32_t inode;
255 uint32_t ch;
256 uint16_t sessions;
257 uint64_t length,l;
258 uint8_t sclass;
259 uint8_t wantchunks;
260 uint32_t progresscnt;
261 dirinfostate *dis;
262
263 fseeko(fd,sdata->offset,SEEK_SET);
264 fseeko(fd,8,SEEK_CUR); // skip maxnodeid,hashelements
265 progresscnt = 0;
266 while (1) {
267 progresscnt++;
268 if (progresscnt>=PROGRESS_PRINT) {
269 progresscnt = 0;
270 fprintf(stderr,"node scan: %.2lf%%\r",100.0*(ftello(fd)-sdata->offset)/sdata->length);
271 fflush(stderr);
272 }
273 type = fgetc(fd);
274 if (type==0) { // end of section
275 fprintf(stderr,"node scan: 100.00%%\n");
276 return 0; // ok
277 }
278 if (fread(buff,1,5,fd)!=5) {
279 fprintf(stderr,"error reading metadata file\n");
280 return -1;
281 }
282 rptr = buff;
283 inode = get32bit(&rptr);
284 sclass = get8bit(&rptr);
285 if (sdata->mver<=0x13) {
286 fseeko(fd,27,SEEK_CUR); // 27 = 1+2+6*4 - flags,mode,uid,gid,atime,mtime,ctime,trashtime
287 } else {
288 fseeko(fd,26,SEEK_CUR); // 26 = 1+1+2+5*4+2 - flags,winattr,mode,uid,gid,atime,mtime,ctime,trashretention
289 }
290 if (type==TYPE_BLOCKDEV || type==TYPE_CHARDEV) {
291 fseeko(fd,4,SEEK_CUR);
292 } else if (type==TYPE_SYMLINK) {
293 uint32_t pleng;
294 if (fread(buff,1,4,fd)!=4) {
295 fprintf(stderr,"error reading metadata file\n");
296 return -1;
297 }
298 rptr = buff;
299 pleng = get32bit(&rptr);
300 fseeko(fd,pleng,SEEK_CUR);
301 } else if (type==TYPE_FILE || type==TYPE_TRASH || type==TYPE_SUSTAINED) {
302 if (sdata->mver<=0x13) {
303 if (fread(buff,1,14,fd)!=14) {
304 fprintf(stderr,"error reading metadata file\n");
305 return -1;
306 }
307 } else {
308 if (fread(buff,1,12,fd)!=12) {
309 fprintf(stderr,"error reading metadata file\n");
310 return -1;
311 }
312 }
313 rptr = buff;
314 length = get64bit(&rptr);
315 ch = get32bit(&rptr);
316 if (sdata->mver<0x13) {
317 sessions = get16bit(&rptr);
318 } else {
319 sessions = 0;
320 }
321 wantchunks = 0;
322 for (dis=dishead ; dis!=NULL ; dis=dis->next) {
323 if ((dis->status==STATUS_OK || dis->status==STATUS_ANY) && liset_check(dis->inode_liset,inode)) {
324 dis->s.length += length;
325 dis->s.files++;
326 wantchunks = 1;
327 dis->wantchunks = 1;
328 } else {
329 dis->wantchunks = 0;
330 }
331 }
332 if (wantchunks) {
333 l = length;
334 while (ch) {
335 uint64_t chunkid;
336 uint32_t chunksize;
337 if (fread(buff,1,8,fd)!=8) {
338 fprintf(stderr,"error reading metadata file\n");
339 return -1;
340 }
341 rptr = buff;
342 chunkid = get64bit(&rptr);
343 if (chunkid>0) {
344 if (l>MFSCHUNKSIZE) {
345 chunksize = MFSCHUNKSIZE + MFSHDRSIZE;
346 l -= MFSCHUNKSIZE;
347 } else if (l>0) {
348 chunksize = ((((l-1)&MFSCHUNKMASK)+MFSBLOCKSIZE)&MFSBLOCKNEGMASK)+MFSHDRSIZE;
349 l = 0;
350 } else {
351 chunksize = MFSHDRSIZE;
352 }
353 for (dis=dishead ; dis!=NULL ; dis=dis->next) {
354 if (dis->wantchunks && liset_check(dis->chunk_liset,chunkid)==0) {
355 dis->s.size += chunksize;
356 dis->s.keeprsize += chunksize * sclass_keep_factor[sclass];
357 dis->s.archrsize += chunksize * sclass_arch_factor[sclass];
358 if (liset_check(achunk_liset,chunkid)) {
359 dis->s.achunks++;
360 dis->s.rsize += chunksize * sclass_arch_factor[sclass];
361 } else {
362 dis->s.kchunks++;
363 dis->s.rsize += chunksize * sclass_keep_factor[sclass];
364 }
365 liset_addval(dis->chunk_liset,chunkid);
366 }
367 }
368 }
369 ch--;
370 }
371 } else {
372 fseeko(fd,ch*8,SEEK_CUR);
373 }
374 fseeko(fd,sessions*4U,SEEK_CUR);
375 } else if (type==TYPE_DIRECTORY) {
376 for (dis=dishead ; dis!=NULL ; dis=dis->next) {
377 if ((dis->status==STATUS_OK || dis->status==STATUS_ANY) && liset_check(dis->inode_liset,inode)) {
378 dis->s.dirs++;
379 }
380 }
381 }
382 }
383 return -1;
384 }
385
scan_edges(FILE * fd,metasection * sdata)386 int scan_edges(FILE *fd,metasection *sdata) {
387 uint8_t edgebuff[18];
388 uint8_t name[256];
389 const uint8_t *rptr;
390 uint16_t nleng;
391 uint32_t parent,child;
392 uint32_t progresscnt;
393 dirinfostate *dis;
394
395 for (dis=dishead ; dis!=NULL ; dis=dis->next) {
396 dis->pptr = dis->path;
397 dis->inode = MFS_ROOT_ID;
398 dis->parent_found = 0;
399 dis->pathscan = 1;
400 dis->nextsection = 1;
401 }
402
403 fseeko(fd,sdata->offset,SEEK_SET);
404 fseeko(fd,8,SEEK_CUR); // skip 'nextedgeid'
405 progresscnt = 0;
406 while (1) {
407 progresscnt++;
408 if (progresscnt>=PROGRESS_PRINT) {
409 progresscnt = 0;
410 fprintf(stderr,"edge scan: %.2lf%%\r",100.0*(ftello(fd)-sdata->offset)/sdata->length);
411 fflush(stderr);
412 }
413 for (dis=dishead ; dis!=NULL ; dis=dis->next) {
414 if (dis->status==STATUS_OK && dis->nextsection) {
415 while (*dis->pptr=='/') {
416 dis->pptr++;
417 }
418 dis->nleng = 0;
419 while (dis->pptr[dis->nleng] && dis->pptr[dis->nleng]!='/') {
420 dis->nleng++;
421 }
422 if (dis->nleng>255) {
423 dis->status = STATUS_ENOENT;
424 }
425 if (dis->nleng==0) {
426 #ifdef DEBUG
427 printf("path %s : inode found: %"PRIu32"\n",dis->path,dis->inode);
428 #endif
429 liset_addval(dis->inode_liset,dis->inode);
430 if (dishead->status==STATUS_ANY) { // all node
431 liset_addval(dishead->inode_liset,dis->inode);
432 }
433 dis->pathscan = 0;
434 }
435 dis->nextsection=0;
436 } // section: pptr,snleng
437 }
438 if (fread(edgebuff,1,18,fd)!=18) {
439 fprintf(stderr,"error reading metadata file\n");
440 return -1;
441 }
442 rptr = edgebuff;
443 parent = get32bit(&rptr);
444 child = get32bit(&rptr);
445 rptr += 8; // skip 'edgeid'
446 nleng = get16bit(&rptr);
447 if (parent==0 && child==0) {
448 fprintf(stderr,"edge scan: 100.00%%\n");
449 return 0;
450 }
451 if (parent==0 && nleng>MFS_PATH_MAX) {
452 fprintf(stderr,"path name too long (%"PRIu16")\n",nleng);
453 return -1;
454 }
455 if (parent!=0 && nleng>MFS_NAME_MAX) {
456 fprintf(stderr,"name name too long (%"PRIu16")\n",nleng);
457 return -1;
458 }
459 if (parent==0) {
460 fseeko(fd,nleng,SEEK_CUR);
461 } else {
462 if (fread(name,1,nleng,fd)!=nleng) {
463 fprintf(stderr,"error reading metadata file\n");
464 return -1;
465 }
466 for (dis=dishead ; dis!=NULL ; dis=dis->next) {
467 if (dis->status==STATUS_OK) {
468 if (dis->pathscan) {
469 if (parent==dis->inode) {
470 if (nleng==dis->nleng && memcmp(name,dis->pptr,nleng)==0) {
471 dis->inode = child;
472 dis->nextsection = 1;
473 dis->parent_found = 0;
474 #ifdef DEBUG
475 name[nleng]=0;
476 printf("found edge %u -> %u (%s)\n",parent,child,name);
477 #endif
478 dis->pptr += nleng;
479 } else {
480 dis->parent_found = 1;
481 }
482 } else {
483 if (dis->parent_found) {
484 dis->status = STATUS_ENOENT;
485 }
486 }
487 } else {
488 if (liset_check(dis->inode_liset,parent)) {
489 liset_addval(dis->inode_liset,child);
490 if (dishead->status==STATUS_ANY) { // all node
491 liset_addval(dishead->inode_liset,child);
492 }
493 }
494 }
495 }
496 }
497 }
498 }
499 }
500
scan_chunks(FILE * fd,metasection * sdata)501 int scan_chunks(FILE *fd,metasection *sdata) {
502 uint8_t buff[17];
503 const uint8_t *rptr;
504 uint64_t chunkid;
505 uint8_t flags;
506 uint32_t progresscnt;
507
508 if (sdata->mver>0x11) {
509 fprintf(stderr,"loading chunks: unsupported format\n");
510 return -1;
511 }
512 if (sdata->mver==0x10) { // no archive bit - just do nothhing
513 return 0;
514 }
515 fseeko(fd,sdata->offset,SEEK_SET);
516 fseeko(fd,8,SEEK_CUR); // skip nextchunkid
517 progresscnt = 0;
518 while (1) {
519 progresscnt++;
520 if (progresscnt>=PROGRESS_PRINT) {
521 progresscnt = 0;
522 fprintf(stderr,"chunk scan: %.2lf%%\r",100.0*(ftello(fd)-sdata->offset)/sdata->length);
523 fflush(stderr);
524 }
525 if (fread(buff,1,17,fd)!=17) {
526 fprintf(stderr,"error reading metadata file\n");
527 return -1;
528 }
529 rptr = buff;
530 chunkid = get64bit(&rptr);
531 rptr+=8; // skip version,lockedto
532 flags = get8bit(&rptr);
533 if (chunkid==0) {
534 fprintf(stderr,"chunk scan: 100.00%%\n");
535 return 0;
536 }
537 if (flags!=0) { // arch
538 liset_addval(achunk_liset,chunkid);
539 }
540 }
541 }
542
calc_dirinfos(FILE * fd)543 int calc_dirinfos(FILE *fd) {
544 uint8_t hdr[16];
545 uint8_t mver;
546 const uint8_t *rptr;
547 uint64_t sleng;
548 metasection edge,node,scla,chnk;
549 dirinfostate *dis;
550
551 fseeko(fd,8+16,SEEK_SET);
552 edge.offset = 0;
553 node.offset = 0;
554 scla.offset = 0;
555 chnk.offset = 0;
556
557 // find metadata file sections
558 while (1) {
559 if (fread(hdr,1,16,fd)!=16) {
560 printf("can't read section header\n");
561 return -1;
562 }
563 if (memcmp(hdr,"[MFS EOF MARKER]",16)==0) {
564 break;
565 }
566 mver = (((hdr[5]-'0')&0xF)<<4)+(hdr[7]&0xF);
567 rptr = hdr+8;
568 sleng = get64bit(&rptr);
569 #if DEBUG>1
570 printf("section %c%c%c%c ; version: %c.%c ; %"PRIu64" bytes\n",hdr[0],hdr[1],hdr[2],hdr[3],hdr[5],hdr[7],sleng);
571 #endif
572 if (memcmp(hdr,"EDGE",4)==0) {
573 edge.offset = ftello(fd);
574 edge.length = sleng;
575 edge.mver = mver;
576 #ifdef DEBUG
577 printf("found EDGE section (%.2lf GiB)\n",sleng/(1024.0*1024.0*1024.0));
578 #endif
579 } else if (memcmp(hdr,"NODE",4)==0) {
580 node.offset = ftello(fd);
581 node.length = sleng;
582 node.mver = mver;
583 #ifdef DEBUG
584 printf("found NODE section (%.2lf GiB)\n",sleng/(1024.0*1024.0*1024.0));
585 #endif
586 } else if (memcmp(hdr,"SCLA",4)==0 || memcmp(hdr,"LABS",4)==0) {
587 scla.offset = ftello(fd);
588 scla.length = sleng;
589 scla.mver = mver;
590 #ifdef DEBUG
591 printf("found SCLA section (%.2lf kB)\n",sleng/1024.0);
592 #endif
593 } else if (memcmp(hdr,"CHNK",4)==0) {
594 chnk.offset = ftello(fd);
595 chnk.length = sleng;
596 chnk.mver = mver;
597 #ifdef DEBUG
598 printf("found CHNK section (%.2lf GiB)\n",sleng/(1024.0*1024.0*1024.0));
599 #endif
600 }
601 fseeko(fd,sleng,SEEK_CUR);
602 }
603
604 if (edge.offset==0) {
605 fprintf(stderr,"can't find EDGE section in metadata file\n");
606 return -1;
607 }
608 if (node.offset==0) {
609 fprintf(stderr,"can't find NODE section in metadata file\n");
610 return -1;
611 }
612 if (scla.offset==0) {
613 fprintf(stderr,"can't find SCLA(SS) section in metadata file\n");
614 return -1;
615 }
616 if (chnk.offset==0) {
617 fprintf(stderr,"can't find CH(U)NK section in metadata file\n");
618 return -1;
619 }
620 if (node.mver<0x13 || node.mver>0x14 || edge.mver!=0x11 || chnk.mver!=0x11) { // MFS 3.x
621 fprintf(stderr,"unsupported metadata format (MFS 3.x needed)\n");
622 return -1;
623 }
624
625 // find keep/arch factors
626 fseeko(fd,scla.offset,SEEK_SET);
627 if (scan_sclass(fd,&scla)<0) {
628 return -1;
629 }
630
631 // build inode set
632 if (scan_edges(fd,&edge)<0) {
633 return -1;
634 }
635
636 for (dis = dishead ; dis!=NULL ; dis=dis->next) {
637 if (dis->status==STATUS_ENOENT) {
638 fprintf(stderr,"path '%s': no such file or directory\n",dis->path);
639 }
640 }
641
642 // add to achunk_liset chunks in arch mode
643 if (scan_chunks(fd,&chnk)<0) {
644 return -1;
645 }
646
647 // build chunk set / calculate results
648 if (scan_nodes(fd,&node)<0) {
649 return -1;
650 }
651
652 return 0;
653 }
654
print_result_plain(FILE * ofd)655 void print_result_plain(FILE *ofd) {
656 dirinfostate *dis;
657 fprintf(ofd,"------------------------------\n");
658 for (dis = dishead ; dis!=NULL ; dis=dis->next) {
659 fprintf(ofd,"path: %s\n",dis->path);
660 if (dis->status!=STATUS_ENOENT) {
661 fprintf(ofd,"inodes: %"PRIu64"\n",liset_card(dis->inode_liset));
662 fprintf(ofd," files: %"PRIu32"\n",dis->s.files);
663 fprintf(ofd," dirs: %"PRIu32"\n",dis->s.dirs);
664 fprintf(ofd,"chunks: %"PRIu64"\n",liset_card(dis->chunk_liset));
665 fprintf(ofd," keep chunks: %"PRIu64"\n",dis->s.kchunks);
666 fprintf(ofd," arch chunks: %"PRIu64"\n",dis->s.achunks);
667 fprintf(ofd,"length: %"PRIu64" = %5sB\n",dis->s.length,mfs_humanize_number(dis->s.length,PHN_USEIEC));
668 fprintf(ofd,"size: %"PRIu64" = %5sB\n",dis->s.size,mfs_humanize_number(dis->s.size,PHN_USEIEC));
669 fprintf(ofd,"keep size: %"PRIu64" = %5sB\n",dis->s.keeprsize,mfs_humanize_number(dis->s.keeprsize,PHN_USEIEC));
670 fprintf(ofd,"arch size: %"PRIu64" = %5sB\n",dis->s.archrsize,mfs_humanize_number(dis->s.archrsize,PHN_USEIEC));
671 fprintf(ofd,"real size: %"PRIu64" = %5sB\n",dis->s.rsize,mfs_humanize_number(dis->s.rsize,PHN_USEIEC));
672 } else {
673 fprintf(ofd,"path not found !!!\n");
674 }
675 fprintf(ofd,"------------------------------\n");
676 }
677 }
678
print_result_json(FILE * ofd)679 void print_result_json(FILE *ofd) {
680 dirinfostate *dis;
681 char c;
682 int j;
683
684 fprintf(ofd,"{\n");
685 for (dis = dishead ; dis!=NULL ; dis=dis->next) {
686 fprintf(ofd,"\t\"");
687 for (j=0 ; dis->path[j]!=0 ; j++) {
688 c = dis->path[j];
689 switch (c) {
690 case '\b':
691 fprintf(ofd,"\\b");
692 break;
693 case '\n':
694 fprintf(ofd,"\\n");
695 break;
696 case '\r':
697 fprintf(ofd,"\\r");
698 break;
699 case '\f':
700 fprintf(ofd,"\\f");
701 break;
702 case '\t':
703 fprintf(ofd,"\\t");
704 break;
705 case '"':
706 fprintf(ofd,"\\\"");
707 break;
708 case '\\':
709 fprintf(ofd,"\\\\");
710 break;
711 default:
712 fputc(c,ofd);
713 }
714 }
715 fprintf(ofd,"\": {\n");
716 fprintf(ofd,"\t\t\"inodes\": %"PRIu64",\n",liset_card(dis->inode_liset));
717 fprintf(ofd,"\t\t\"files\": %"PRIu32",\n",dis->s.files);
718 fprintf(ofd,"\t\t\"dirs\": %"PRIu32",\n",dis->s.dirs);
719 fprintf(ofd,"\t\t\"chunks\": %"PRIu64",\n",liset_card(dis->chunk_liset));
720 fprintf(ofd,"\t\t\"kchunks\": %"PRIu64",\n",dis->s.kchunks);
721 fprintf(ofd,"\t\t\"achunks\": %"PRIu64",\n",dis->s.achunks);
722 fprintf(ofd,"\t\t\"length\": %"PRIu64",\n",dis->s.length);
723 fprintf(ofd,"\t\t\"size\": %"PRIu64",\n",dis->s.size);
724 fprintf(ofd,"\t\t\"rsize\": %"PRIu64",\n",dis->s.rsize);
725 fprintf(ofd,"\t\t\"ksize\": %"PRIu64",\n",dis->s.keeprsize);
726 fprintf(ofd,"\t\t\"asize\": %"PRIu64",\n",dis->s.archrsize);
727 fprintf(ofd,"\t\t\"error\": %u\n",(dis->status!=STATUS_ENOENT)?0:1);
728 fprintf(ofd,"\t}%s\n",(dis->next!=NULL)?",":"");
729 }
730 fprintf(ofd,"}\n");
731 }
732
print_result_csv(FILE * ofd,char s)733 void print_result_csv(FILE *ofd,char s) {
734 dirinfostate *dis;
735 uint8_t i;
736 char c;
737 int j;
738
739 fprintf(ofd,"path%cinodes%cfiles%cdirs%cchunks%ckeep_chunks%carch_chunks%clength%csize%creal_size%ckeep_size%carch_size\n",s,s,s,s,s,s,s,s,s,s,s);
740 for (dis = dishead ; dis!=NULL ; dis=dis->next) {
741 fputc('\"',ofd);
742 for (j=0 ; dis->path[j]!=0 ; j++) {
743 c = dis->path[j];
744 switch (c) {
745 case '\b':
746 fprintf(ofd,"\\b");
747 break;
748 case '\n':
749 fprintf(ofd,"\\n");
750 break;
751 case '\r':
752 fprintf(ofd,"\\r");
753 break;
754 case '\f':
755 fprintf(ofd,"\\f");
756 break;
757 case '\t':
758 fprintf(ofd,"\\t");
759 break;
760 case '"':
761 fprintf(ofd,"\\\"");
762 break;
763 case '\\':
764 fprintf(ofd,"\\\\");
765 break;
766 default:
767 fputc(c,ofd);
768 }
769 }
770 fputc('\"',ofd);
771 if (dis->status!=STATUS_ENOENT) {
772 fprintf(ofd,"%c%"PRIu64,s,liset_card(dis->inode_liset));
773 fprintf(ofd,"%c%"PRIu32,s,dis->s.files);
774 fprintf(ofd,"%c%"PRIu32,s,dis->s.dirs);
775 fprintf(ofd,"%c%"PRIu64,s,liset_card(dis->chunk_liset));
776 fprintf(ofd,"%c%"PRIu64,s,dis->s.kchunks);
777 fprintf(ofd,"%c%"PRIu64,s,dis->s.achunks);
778 fprintf(ofd,"%c%"PRIu64,s,dis->s.length);
779 fprintf(ofd,"%c%"PRIu64,s,dis->s.size);
780 fprintf(ofd,"%c%"PRIu64,s,dis->s.rsize);
781 fprintf(ofd,"%c%"PRIu64,s,dis->s.keeprsize);
782 fprintf(ofd,"%c%"PRIu64,s,dis->s.archrsize);
783 } else {
784 for (i=0 ; i<11 ; i++) {
785 fprintf(ofd,"%cerror",s);
786 }
787 }
788 fprintf(ofd,"\n");
789 }
790 }
791
usage(const char * appname)792 void usage(const char *appname) {
793 printf("usage: %s [-f J|C[separator]] [-o outputfile] [-a sum_name] metadata.mfs PATH ...\n",appname);
794 exit(1);
795 }
796
main(int argc,char * argv[])797 int main(int argc,char *argv[]) {
798 uint8_t hdr[8];
799 uint8_t fver;
800 int indx,ret;
801 dirinfostate *dis,**disp;
802 FILE *fd;
803 FILE *ofd;
804 char *outfname;
805 char *allname;
806 const char *appname;
807 char sep;
808 int ch;
809 uint8_t format;
810
811 appname = argv[0];
812 outfname = NULL;
813 allname = NULL;
814 sep = ',';
815 format = 0;
816 while ((ch=getopt(argc,argv,"f:o:a:"))>=0) {
817 switch(ch) {
818 case 'f':
819 if (optarg[0]=='j' || optarg[0]=='J') {
820 format = 2;
821 } else if (optarg[0]=='c' || optarg[0]=='C') {
822 format = 1;
823 if (optarg[1]!=0) {
824 sep = optarg[1];
825 }
826 } else {
827 fprintf(stderr,"unrecognized format - use 'j' for JSON and 'c' for CSV\n");
828 usage(appname);
829 return 1;
830 }
831 break;
832 case 'o':
833 if (outfname!=NULL) {
834 usage(appname);
835 return 1;
836 }
837 outfname = strdup(optarg);
838 break;
839 case 'a':
840 if (allname!=NULL) {
841 usage(appname);
842 return 1;
843 }
844 allname = strdup(optarg);
845 break;
846 default:
847 usage(appname);
848 return 1;
849 }
850 }
851 argc -= optind;
852 argv += optind;
853
854 if (argc<1) {
855 usage(appname);
856 return 1;
857 }
858
859 ret = 1;
860 ofd = NULL;
861 fd = NULL;
862
863 if (outfname!=NULL) {
864 ofd = fopen(outfname,"w");
865 if (ofd==NULL) {
866 fprintf(stderr,"error opening output file '%s'\n",outfname);
867 goto error;
868 }
869 } else {
870 ofd = stdout;
871 }
872
873 fd = fopen(argv[0],"rb");
874 if (fd==NULL) {
875 fprintf(stderr,"error opening metadata file '%s'\n",argv[0]);
876 goto error;
877 }
878 if (fread(hdr,1,8,fd)!=8) {
879 fprintf(stderr,"%s: can't read metadata header\n",argv[0]);
880 goto error;
881 }
882 if (memcmp(hdr,"MFSM NEW",8)==0) {
883 fprintf(stderr,"%s: empty file\n",argv[0]);
884 goto error;
885 }
886 if (memcmp(hdr,MFSSIGNATURE "M ",5)==0 && hdr[5]>='1' && hdr[5]<='9' && hdr[6]=='.' && hdr[7]>='0' && hdr[7]<='9') {
887 fver = ((hdr[5]-'0')<<4)+(hdr[7]-'0');
888 } else {
889 fprintf(stderr,"%s: unrecognized file format\n",argv[0]);
890 goto error;
891 }
892 if (fver<0x20) {
893 fprintf(stderr,"%s: metadata file format too old %u.%u\n",argv[0],(unsigned)(fver>>4),(unsigned)(fver&0xF));
894 goto error;
895 }
896 dishead = NULL;
897 disp = &dishead;
898 if (allname!=NULL) {
899 dis = malloc(sizeof(dirinfostate));
900 memset(dis,0,sizeof(dirinfostate));
901 dis->inode_liset = liset_new();
902 dis->chunk_liset = liset_new();
903 dis->path = strdup(allname);
904 dis->next = NULL;
905 dis->status = STATUS_ANY;
906 *disp = dis;
907 disp = &(dis->next);
908 }
909 for (indx=1 ; indx<argc ; indx++) {
910 dis = malloc(sizeof(dirinfostate));
911 memset(dis,0,sizeof(dirinfostate));
912 dis->inode_liset = liset_new();
913 dis->chunk_liset = liset_new();
914 dis->path = strdup(argv[indx]);
915 dis->next = NULL;
916 *disp = dis;
917 disp = &(dis->next);
918 }
919 achunk_liset = liset_new();
920 ret = 0;
921 if (calc_dirinfos(fd)<0) {
922 ret = 1;
923 }
924 fclose(fd);
925 fd = NULL;
926 if (ret==0) {
927 if (format==2) {
928 print_result_json(ofd);
929 } else if (format==1) {
930 print_result_csv(ofd,sep);
931 } else {
932 print_result_plain(ofd);
933 }
934 }
935 liset_remove(achunk_liset);
936 while (dishead!=NULL) {
937 dis = dishead;
938 liset_remove(dis->inode_liset);
939 liset_remove(dis->chunk_liset);
940 free(dis->path);
941 dishead = dis->next;
942 free(dis);
943 }
944 error:
945 if (fd!=NULL) {
946 fclose(fd);
947 }
948 if (outfname!=NULL) {
949 if (ofd!=NULL) {
950 fclose(ofd);
951 }
952 free(outfname);
953 }
954 if (allname!=NULL) {
955 free(allname);
956 }
957 return ret;
958 }
959