tizen 2.3 release
[framework/system/deviced.git] / src / fsck-msdos / dir.c
1 /*
2  * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
3  * Copyright (c) 1995 Martin Husemann
4  * Some structure declaration borrowed from Paul Popelka
5  * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Martin Husemann
18  *      and Wolfgang Solfrank.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <stdio.h>
41 #include <unistd.h>
42 #include <time.h>
43
44 #include <sys/param.h>
45
46 #include "ext.h"
47 #include "fsutil.h"
48
49 #define SLOT_EMPTY      0x00            /* slot has never been used */
50 #define SLOT_E5         0x05            /* the real value is 0xe5 */
51 #define SLOT_DELETED    0xe5            /* file in this slot deleted */
52
53 #define ATTR_NORMAL     0x00            /* normal file */
54 #define ATTR_READONLY   0x01            /* file is readonly */
55 #define ATTR_HIDDEN     0x02            /* file is hidden */
56 #define ATTR_SYSTEM     0x04            /* file is a system file */
57 #define ATTR_VOLUME     0x08            /* entry is a volume label */
58 #define ATTR_DIRECTORY  0x10            /* entry is a directory name */
59 #define ATTR_ARCHIVE    0x20            /* file is new or modified */
60
61 #define ATTR_WIN95      0x0f            /* long name record */
62
63 /*
64  * This is the format of the contents of the deTime field in the direntry
65  * structure.
66  * We don't use bitfields because we don't know how compilers for
67  * arbitrary machines will lay them out.
68  */
69 #define DT_2SECONDS_MASK        0x1F    /* seconds divided by 2 */
70 #define DT_2SECONDS_SHIFT       0
71 #define DT_MINUTES_MASK         0x7E0   /* minutes */
72 #define DT_MINUTES_SHIFT        5
73 #define DT_HOURS_MASK           0xF800  /* hours */
74 #define DT_HOURS_SHIFT          11
75
76 /*
77  * This is the format of the contents of the deDate field in the direntry
78  * structure.
79  */
80 #define DD_DAY_MASK             0x1F    /* day of month */
81 #define DD_DAY_SHIFT            0
82 #define DD_MONTH_MASK           0x1E0   /* month */
83 #define DD_MONTH_SHIFT          5
84 #define DD_YEAR_MASK            0xFE00  /* year - 1980 */
85 #define DD_YEAR_SHIFT           9
86
87
88 /* dir.c */
89 static struct dosDirEntry *newDosDirEntry(void);
90 static void freeDosDirEntry(struct dosDirEntry *);
91 static struct dirTodoNode *newDirTodo(void);
92 static void freeDirTodo(struct dirTodoNode *);
93 static char *fullpath(struct dosDirEntry *);
94 static u_char calcShortSum(u_char *);
95 static int delete(int, struct bootblock *, struct fatEntry *, cl_t, int,
96     cl_t, int, int);
97 static int removede(int, struct bootblock *, struct fatEntry *, u_char *,
98     u_char *, cl_t, cl_t, cl_t, char *, int);
99 static int checksize(struct bootblock *, struct fatEntry *, u_char *,
100     struct dosDirEntry *);
101 static int readDosDirSection(int, struct bootblock *, struct fatEntry *,
102     struct dosDirEntry *);
103
104 /*
105  * Manage free dosDirEntry structures.
106  */
107 static struct dosDirEntry *freede;
108
109 static struct dosDirEntry *
110 newDosDirEntry(void)
111 {
112         struct dosDirEntry *de;
113
114         if (!(de = freede)) {
115                 if (!(de = (struct dosDirEntry *)malloc(sizeof *de)))
116                         return 0;
117         } else
118                 freede = de->next;
119         return de;
120 }
121
122 static void
123 freeDosDirEntry(struct dosDirEntry *de)
124 {
125         de->next = freede;
126         freede = de;
127 }
128
129 /*
130  * The same for dirTodoNode structures.
131  */
132 static struct dirTodoNode *freedt;
133
134 static struct dirTodoNode *
135 newDirTodo(void)
136 {
137         struct dirTodoNode *dt;
138
139         if (!(dt = freedt)) {
140                 if (!(dt = (struct dirTodoNode *)malloc(sizeof *dt)))
141                         return 0;
142         } else
143                 freedt = dt->next;
144         return dt;
145 }
146
147 static void
148 freeDirTodo(struct dirTodoNode *dt)
149 {
150         dt->next = freedt;
151         freedt = dt;
152 }
153
154 /*
155  * The stack of unread directories
156  */
157 struct dirTodoNode *pendingDirectories = NULL;
158
159 /*
160  * Return the full pathname for a directory entry.
161  */
162 static char *
163 fullpath(struct dosDirEntry *dir)
164 {
165         static char namebuf[MAXPATHLEN + 1];
166         char *cp, *np;
167         int nl;
168
169         cp = namebuf + sizeof namebuf - 1;
170         *cp = '\0';
171         do {
172                 np = dir->lname[0] ? dir->lname : dir->name;
173                 nl = strlen(np);
174                 if ((cp -= nl) <= namebuf + 1)
175                         break;
176                 memcpy(cp, np, nl);
177                 *--cp = '/';
178         } while ((dir = dir->parent) != NULL);
179         if (dir)
180                 *--cp = '?';
181         else
182                 cp++;
183         return cp;
184 }
185
186 /*
187  * Calculate a checksum over an 8.3 alias name
188  */
189 static u_char
190 calcShortSum(u_char *p)
191 {
192         u_char sum = 0;
193         int i;
194
195         for (i = 0; i < 11; i++) {
196                 sum = (sum << 7)|(sum >> 1);    /* rotate right */
197                 sum += p[i];
198         }
199
200         return sum;
201 }
202
203 /*
204  * Global variables temporarily used during a directory scan
205  */
206 static char longName[DOSLONGNAMELEN] = "";
207 static u_char *buffer = NULL;
208 static u_char *delbuf = NULL;
209
210 struct dosDirEntry *rootDir;
211 static struct dosDirEntry *lostDir;
212
213 /*
214  * Init internal state for a new directory scan.
215  */
216 int
217 resetDosDirSection(struct bootblock *boot, struct fatEntry *fat)
218 {
219         int b1, b2;
220         cl_t cl;
221         int ret = FSOK;
222
223         b1 = boot->RootDirEnts * 32;
224         b2 = boot->SecPerClust * boot->BytesPerSec;
225
226         if (!(buffer = malloc(b1 > b2 ? b1 : b2))
227             || !(delbuf = malloc(b2))
228             || !(rootDir = newDosDirEntry())) {
229                 perror("No space for directory");
230                 return FSFATAL;
231         }
232         memset(rootDir, 0, sizeof *rootDir);
233         if (boot->flags & FAT32) {
234                 if (boot->RootCl < CLUST_FIRST || boot->RootCl >= boot->NumClusters) {
235                         pfatal("Root directory starts with cluster out of range(%u)",
236                                boot->RootCl);
237                         return FSFATAL;
238                 }
239                 cl = fat[boot->RootCl].next;
240                 if (cl < CLUST_FIRST
241                     || (cl >= CLUST_RSRVD && cl< CLUST_EOFS)
242                     || fat[boot->RootCl].head != boot->RootCl) {
243                         if (cl == CLUST_FREE)
244                                 pwarn("Root directory starts with free cluster\n");
245                         else if (cl >= CLUST_RSRVD)
246                                 pwarn("Root directory starts with cluster marked %s\n",
247                                       rsrvdcltype(cl));
248                         else {
249                                 pfatal("Root directory doesn't start a cluster chain");
250                                 return FSFATAL;
251                         }
252                         if (ask(1, "Fix")) {
253                                 fat[boot->RootCl].next = CLUST_FREE;
254                                 ret = FSFATMOD;
255                         } else
256                                 ret = FSFATAL;
257                 }
258
259                 fat[boot->RootCl].flags |= FAT_USED;
260                 rootDir->head = boot->RootCl;
261         }
262
263         return ret;
264 }
265
266 /*
267  * Cleanup after a directory scan
268  */
269 void
270 finishDosDirSection(void)
271 {
272         struct dirTodoNode *p, *np;
273         struct dosDirEntry *d, *nd;
274
275         for (p = pendingDirectories; p; p = np) {
276                 np = p->next;
277                 freeDirTodo(p);
278         }
279         pendingDirectories = 0;
280         for (d = rootDir; d; d = nd) {
281                 if ((nd = d->child) != NULL) {
282                         d->child = 0;
283                         continue;
284                 }
285                 if (!(nd = d->next))
286                         nd = d->parent;
287                 freeDosDirEntry(d);
288         }
289         rootDir = lostDir = NULL;
290         free(buffer);
291         free(delbuf);
292         buffer = NULL;
293         delbuf = NULL;
294 }
295
296 /*
297  * Delete directory entries between startcl, startoff and endcl, endoff.
298  */
299 static int
300 delete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl,
301     int startoff, cl_t endcl, int endoff, int notlast)
302 {
303         u_char *s, *e;
304         loff_t off;
305         int clsz = boot->SecPerClust * boot->BytesPerSec;
306
307         s = delbuf + startoff;
308         e = delbuf + clsz;
309         while (startcl >= CLUST_FIRST && startcl < boot->NumClusters) {
310                 if (startcl == endcl) {
311                         if (notlast)
312                                 break;
313                         e = delbuf + endoff;
314                 }
315                 off = startcl * boot->SecPerClust + boot->ClusterOffset;
316                 off *= boot->BytesPerSec;
317                 if (lseek64(f, off, SEEK_SET) != off) {
318                         printf("off = %llu\n", off);
319                         perror("Unable to lseek64");
320                         return FSFATAL;
321                 }
322                 if (read(f, delbuf, clsz) != clsz) {
323                         perror("Unable to read directory");
324                         return FSFATAL;
325                 }
326                 while (s < e) {
327                         *s = SLOT_DELETED;
328                         s += 32;
329                 }
330                 if (lseek64(f, off, SEEK_SET) != off) {
331                         printf("off = %llu\n", off);
332                         perror("Unable to lseek64");
333                         return FSFATAL;
334                 }
335                 if (write(f, delbuf, clsz) != clsz) {
336                         perror("Unable to write directory");
337                         return FSFATAL;
338                 }
339                 if (startcl == endcl)
340                         break;
341                 startcl = fat[startcl].next;
342                 s = delbuf;
343         }
344         return FSOK;
345 }
346
347 static int
348 removede(int f, struct bootblock *boot, struct fatEntry *fat, u_char *start,
349     u_char *end, cl_t startcl, cl_t endcl, cl_t curcl, char *path, int type)
350 {
351         switch (type) {
352         case 0:
353                 pwarn("Invalid long filename entry for %s\n", path);
354                 break;
355         case 1:
356                 pwarn("Invalid long filename entry at end of directory %s\n", path);
357                 break;
358         case 2:
359                 pwarn("Invalid long filename entry for volume label\n");
360                 break;
361         }
362         if (ask(1, "Remove")) {
363                 if (startcl != curcl) {
364                         if (delete(f, boot, fat,
365                                    startcl, start - buffer,
366                                    endcl, end - buffer,
367                                    endcl == curcl) == FSFATAL)
368                                 return FSFATAL;
369                         start = buffer;
370                 }
371                 if (endcl == curcl)
372                         for (; start < end; start += 32)
373                                 *start = SLOT_DELETED;
374                 return FSDIRMOD;
375         }
376         return FSERROR;
377 }
378
379 /*
380  * Check an in-memory file entry
381  */
382 static int
383 checksize(struct bootblock *boot, struct fatEntry *fat, u_char *p,
384     struct dosDirEntry *dir)
385 {
386         /*
387          * Check size on ordinary files
388          */
389         int32_t physicalSize;
390
391         if (dir->head == CLUST_FREE)
392                 physicalSize = 0;
393         else {
394                 if (dir->head < CLUST_FIRST || dir->head >= boot->NumClusters)
395                         return FSERROR;
396                 physicalSize = fat[dir->head].length * boot->ClusterSize;
397         }
398         if (physicalSize < dir->size) {
399                 pwarn("size of %s is %u, should at most be %u\n",
400                       fullpath(dir), dir->size, physicalSize);
401                 if (ask(1, "Truncate")) {
402                         dir->size = physicalSize;
403                         p[28] = (u_char)physicalSize;
404                         p[29] = (u_char)(physicalSize >> 8);
405                         p[30] = (u_char)(physicalSize >> 16);
406                         p[31] = (u_char)(physicalSize >> 24);
407                         return FSDIRMOD;
408                 } else
409                         return FSERROR;
410         } else if (physicalSize - dir->size >= boot->ClusterSize) {
411                 pwarn("%s has too many clusters allocated\n",
412                       fullpath(dir));
413                 if (ask(1, "Drop superfluous clusters")) {
414                         cl_t cl;
415                         u_int32_t sz = 0;
416
417                         for (cl = dir->head; (sz += boot->ClusterSize) < dir->size;)
418                                 cl = fat[cl].next;
419                         clearchain(boot, fat, fat[cl].next);
420                         fat[cl].next = CLUST_EOF;
421                         return FSFATMOD;
422                 } else
423                         return FSERROR;
424         }
425         return FSOK;
426 }
427
428
429 static u_char  dot_header[16]={0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00};
430 static u_char  dot_dot_header[16]={0x2E, 0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00};
431
432 /*
433  * Check for missing or broken '.' and '..' entries.
434  */
435 static int
436 check_dot_dot(int f, struct bootblock *boot, struct fatEntry *fat,struct dosDirEntry *dir)
437 {
438         u_char *p, *buf;
439         loff_t off;
440         int last;
441         cl_t cl;
442         int rc=0, n_count;
443
444         int dot, dotdot;
445         dot = dotdot = 0;
446         cl = dir->head;
447
448         if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
449                 return rc;
450         }
451
452         do {
453                 if (!(boot->flags & FAT32) && !dir->parent) {
454                         last = boot->RootDirEnts * 32;
455                         off = boot->ResSectors + boot->FATs * boot->FATsecs;
456                 } else {
457                         last = boot->SecPerClust * boot->BytesPerSec;
458                         off = cl * boot->SecPerClust + boot->ClusterOffset;
459                 }
460
461                 off *= boot->BytesPerSec;
462                 buf = malloc(last);
463                 if (!buf) {
464                         perror("Unable to malloc");
465                         return FSFATAL;
466                 }
467                 if (lseek64(f, off, SEEK_SET) != off) {
468                         printf("off = %llu\n", off);
469                         perror("Unable to lseek64");
470                         free(buf);
471                         return FSFATAL;
472                 }
473                 if (read(f, buf, last) != last) {
474                         perror("Unable to read");
475                         free(buf);
476                         return FSFATAL;
477                 }
478                 last /= 32;
479                 p = buf;
480                 for (n_count=0, rc=0; n_count < 11; n_count++) {
481                         if (dot_header[n_count] != p[n_count]) {
482                                 rc=-1;
483                                 break;
484                         }
485                 }
486                  if(!rc)
487                         dot=1;
488
489                 for (n_count = 0, rc = 0; n_count < 11; n_count++) {
490                         if (dot_dot_header[n_count] != p[n_count+32]) {
491                                 rc=-1;
492                                 break;
493                         }
494                 }
495                 if(!rc)
496                         dotdot=1;
497                 free(buf);
498         } while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters);
499
500         if (!dot || !dotdot) {
501                 if (!dot)
502                         pwarn("%s: '.' absent for %s.\n",__func__,dir->name);
503
504                 if (!dotdot)
505                         pwarn("%s: '..' absent for %s. \n",__func__,dir->name);
506                 return -1;
507         }
508         return 0;
509 }
510
511 /*
512  * Read a directory and
513  *   - resolve long name records
514  *   - enter file and directory records into the parent's list
515  *   - push directories onto the todo-stack
516  */
517 static int
518 readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat,
519     struct dosDirEntry *dir)
520 {
521         struct dosDirEntry dirent, *d;
522         u_char *p, *vallfn, *invlfn, *empty;
523         loff_t off;
524         int i, j, k, last;
525         cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0;
526         char *t;
527         u_int lidx = 0;
528         int shortSum;
529         int mod = FSOK;
530         int n_count=0;
531         int rc=0;
532 #define THISMOD 0x8000                  /* Only used within this routine */
533
534         cl = dir->head;
535         if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
536                 /*
537                  * Already handled somewhere else.
538                  */
539                 return FSOK;
540         }
541         shortSum = -1;
542         vallfn = invlfn = empty = NULL;
543         int dot,dotdot;
544         dot = dotdot = 0;
545
546         do {
547                 if (!(boot->flags & FAT32) && !dir->parent) {
548                         last = boot->RootDirEnts * 32;
549                         off = boot->ResSectors + boot->FATs * boot->FATsecs;
550                 } else {
551                         last = boot->SecPerClust * boot->BytesPerSec;
552                         off = cl * boot->SecPerClust + boot->ClusterOffset;
553                 }
554
555                 off *= boot->BytesPerSec;
556                 if (lseek64(f, off, SEEK_SET) != off) {
557                         printf("off = %llu\n", off);
558                         perror("Unable to lseek64");
559                         return FSFATAL;
560                 }
561                 if (read(f, buffer, last) != last) {
562                         perror("Unable to read");
563                         return FSFATAL;
564                 }
565                 last /= 32;
566                 for (p = buffer, i = 0; i < last; i++, p += 32) {
567                         if (dir->fsckflags & DIREMPWARN) {
568                                 *p = SLOT_EMPTY;
569                                 continue;
570                         }
571
572                         if (*p == SLOT_EMPTY || *p == SLOT_DELETED) {
573                                 if (*p == SLOT_EMPTY) {
574                                         dir->fsckflags |= DIREMPTY;
575                                         empty = p;
576                                         empcl = cl;
577                                 }
578                                 continue;
579                         }
580
581                         if (dir->fsckflags & DIREMPTY) {
582                                 if (!(dir->fsckflags & DIREMPWARN)) {
583                                         pwarn("%s has entries after end of directory\n",
584                                               fullpath(dir));
585                                         if (ask(1, "Extend")) {
586                                                 u_char *q;
587
588                                                 dir->fsckflags &= ~DIREMPTY;
589                                                 if (delete(f, boot, fat,
590                                                            empcl, empty - buffer,
591                                                            cl, p - buffer, 1) == FSFATAL)
592                                                         return FSFATAL;
593                                                 q = empcl == cl ? empty : buffer;
594                                                 for (; q < p; q += 32)
595                                                         *q = SLOT_DELETED;
596                                                 mod |= THISMOD|FSDIRMOD;
597                                         } else if (ask(1, "Truncate"))
598                                                 dir->fsckflags |= DIREMPWARN;
599                                 }
600                                 if (dir->fsckflags & DIREMPWARN) {
601                                         *p = SLOT_DELETED;
602                                         mod |= THISMOD|FSDIRMOD;
603                                         continue;
604                                 } else if (dir->fsckflags & DIREMPTY)
605                                         mod |= FSERROR;
606                                 empty = NULL;
607                         }
608
609                         if (p[11] == ATTR_WIN95) {
610                                 if (*p & LRFIRST) {
611                                         if (shortSum != -1) {
612                                                 if (!invlfn) {
613                                                         invlfn = vallfn;
614                                                         invcl = valcl;
615                                                 }
616                                         }
617                                         memset(longName, 0, sizeof longName);
618                                         shortSum = p[13];
619                                         vallfn = p;
620                                         valcl = cl;
621                                 } else if (shortSum != p[13]
622                                            || lidx != (*p & LRNOMASK)) {
623                                         if (!invlfn) {
624                                                 invlfn = vallfn;
625                                                 invcl = valcl;
626                                         }
627                                         if (!invlfn) {
628                                                 invlfn = p;
629                                                 invcl = cl;
630                                         }
631                                         vallfn = NULL;
632                                 }
633                                 lidx = *p & LRNOMASK;
634                                 t = longName + --lidx * 13;
635                                 for (k = 1; k < 11 && t < longName + sizeof(longName); k += 2) {
636                                         if (!p[k] && !p[k + 1])
637                                                 break;
638                                         *t++ = p[k];
639                                         /*
640                                          * Warn about those unusable chars in msdosfs here?     XXX
641                                          */
642                                         if (p[k + 1])
643                                                 t[-1] = '?';
644                                 }
645                                 if (k >= 11)
646                                         for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) {
647                                                 if (!p[k] && !p[k + 1])
648                                                         break;
649                                                 *t++ = p[k];
650                                                 if (p[k + 1])
651                                                         t[-1] = '?';
652                                         }
653                                 if (k >= 26)
654                                         for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) {
655                                                 if (!p[k] && !p[k + 1])
656                                                         break;
657                                                 *t++ = p[k];
658                                                 if (p[k + 1])
659                                                         t[-1] = '?';
660                                         }
661                                 if (t >= longName + sizeof(longName)) {
662                                         pwarn("long filename too long\n");
663                                         if (!invlfn) {
664                                                 invlfn = vallfn;
665                                                 invcl = valcl;
666                                         }
667                                         vallfn = NULL;
668                                 }
669                                 if (p[26] | (p[27] << 8)) {
670                                         pwarn("long filename record cluster start != 0\n");
671                                         if (!invlfn) {
672                                                 invlfn = vallfn;
673                                                 invcl = cl;
674                                         }
675                                         vallfn = NULL;
676                                 }
677                                 continue;       /* long records don't carry further
678                                                  * information */
679                         }
680
681                         /*
682                          * This is a standard msdosfs directory entry.
683                          */
684                         memset(&dirent, 0, sizeof dirent);
685
686                         /*
687                          * it's a short name record, but we need to know
688                          * more, so get the flags first.
689                          */
690                         dirent.flags = p[11];
691
692                         /*
693                          * Translate from 850 to ISO here               XXX
694                          */
695                         for (j = 0; j < 8; j++)
696                                 dirent.name[j] = p[j];
697                         dirent.name[8] = '\0';
698                         for (k = 7; k >= 0 && dirent.name[k] == ' '; k--)
699                                 dirent.name[k] = '\0';
700                         if (dirent.name[k] != '\0')
701                                 k++;
702                         if (dirent.name[0] == SLOT_E5)
703                                 dirent.name[0] = 0xe5;
704
705                         if (dirent.flags & ATTR_VOLUME) {
706                                 if (vallfn || invlfn) {
707                                         mod |= removede(f, boot, fat,
708                                                         invlfn ? invlfn : vallfn, p,
709                                                         invlfn ? invcl : valcl, -1, 0,
710                                                         fullpath(dir), 2);
711                                         vallfn = NULL;
712                                         invlfn = NULL;
713                                 }
714                                 continue;
715                         }
716
717                         if (p[8] != ' ')
718                                 dirent.name[k++] = '.';
719                         for (j = 0; j < 3; j++)
720                                 dirent.name[k++] = p[j+8];
721                         dirent.name[k] = '\0';
722                         for (k--; k >= 0 && dirent.name[k] == ' '; k--)
723                                 dirent.name[k] = '\0';
724
725                         if (vallfn && shortSum != calcShortSum(p)) {
726                                 if (!invlfn) {
727                                         invlfn = vallfn;
728                                         invcl = valcl;
729                                 }
730                                 vallfn = NULL;
731                         }
732                         dirent.head = p[26] | (p[27] << 8);
733                         if (boot->ClustMask == CLUST32_MASK)
734                                 dirent.head |= (p[20] << 16) | (p[21] << 24);
735                         dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24);
736                         if (vallfn) {
737                                 strcpy(dirent.lname, longName);
738                                 longName[0] = '\0';
739                                 shortSum = -1;
740                         }
741
742                         dirent.parent = dir;
743                         dirent.next = dir->child;
744
745                         if (invlfn) {
746                                 mod |= k = removede(f, boot, fat,
747                                                     invlfn, vallfn ? vallfn : p,
748                                                     invcl, vallfn ? valcl : cl, cl,
749                                                     fullpath(&dirent), 0);
750                                 if (mod & FSFATAL)
751                                         return FSFATAL;
752                                 if (vallfn
753                                     ? (valcl == cl && vallfn != buffer)
754                                     : p != buffer)
755                                         if (k & FSDIRMOD)
756                                                 mod |= THISMOD;
757                         }
758
759                         vallfn = NULL; /* not used any longer */
760                         invlfn = NULL;
761
762                         if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) {
763                                 if (dirent.head != 0) {
764                                         pwarn("%s has clusters, but size 0\n",
765                                               fullpath(&dirent));
766                                         if (ask(1, "Drop allocated clusters")) {
767                                                 p[26] = p[27] = 0;
768                                                 if (boot->ClustMask == CLUST32_MASK)
769                                                         p[20] = p[21] = 0;
770                                                 clearchain(boot, fat, dirent.head);
771                                                 dirent.head = 0;
772                                                 mod |= THISMOD|FSDIRMOD|FSFATMOD;
773                                         } else
774                                                 mod |= FSERROR;
775                                 }
776                         } else if (dirent.head == 0
777                                    && !strcmp(dirent.name, "..")
778                                    && dir->parent                       /* XXX */
779                                    && !dir->parent->parent) {
780                                 /*
781                                  *  Do nothing, the parent is the root
782                                  */
783                         } else if (dirent.head < CLUST_FIRST
784                                    || dirent.head >= boot->NumClusters
785                                    || fat[dirent.head].next == CLUST_FREE
786                                    || (fat[dirent.head].next >= CLUST_RSRVD
787                                        && fat[dirent.head].next < CLUST_EOFS)
788                                    || fat[dirent.head].head != dirent.head) {
789                                 if (dirent.head == 0)
790                                         pwarn("%s has no clusters\n",
791                                               fullpath(&dirent));
792                                 else if (dirent.head < CLUST_FIRST
793                                          || dirent.head >= boot->NumClusters)
794                                         pwarn("%s starts with cluster out of range(%u)\n",
795                                               fullpath(&dirent),
796                                               dirent.head);
797                                 else if (fat[dirent.head].next == CLUST_FREE)
798                                         pwarn("%s starts with free cluster\n",
799                                               fullpath(&dirent));
800                                 else if (fat[dirent.head].next >= CLUST_RSRVD)
801                                         pwarn("%s starts with cluster marked %s\n",
802                                               fullpath(&dirent),
803                                               rsrvdcltype(fat[dirent.head].next));
804                                 else
805                                         pwarn("%s doesn't start a cluster chain\n",
806                                               fullpath(&dirent));
807                                 if (dirent.flags & ATTR_DIRECTORY) {
808                                         if (ask(1, "Remove")) {
809                                                 *p = SLOT_DELETED;
810                                                 mod |= THISMOD|FSDIRMOD;
811                                         } else
812                                                 mod |= FSERROR;
813                                         continue;
814                                 } else {
815                                         if (ask(1, "Truncate")) {
816                                                 p[28] = p[29] = p[30] = p[31] = 0;
817                                                 p[26] = p[27] = 0;
818                                                 if (boot->ClustMask == CLUST32_MASK)
819                                                         p[20] = p[21] = 0;
820                                                 dirent.size = 0;
821                                                 mod |= THISMOD|FSDIRMOD;
822                                         } else
823                                                 mod |= FSERROR;
824                                 }
825                         }
826
827                         if (dirent.head >= CLUST_FIRST && dirent.head < boot->NumClusters)
828                                 fat[dirent.head].flags |= FAT_USED;
829
830                         if (dirent.flags & ATTR_DIRECTORY) {
831                                 /*
832                                  * gather more info for directories
833                                  */
834                                 struct dirTodoNode *n;
835
836                                 if (dirent.size) {
837                                         pwarn("Directory %s has size != 0\n",
838                                               fullpath(&dirent));
839                                         if (ask(1, "Correct")) {
840                                                 p[28] = p[29] = p[30] = p[31] = 0;
841                                                 dirent.size = 0;
842                                                 mod |= THISMOD|FSDIRMOD;
843                                         } else
844                                                 mod |= FSERROR;
845                                 }
846                                 /*
847                                  * handle '.' and '..' specially
848                                  */
849                                 if (strcmp(dirent.name, ".") == 0) {
850                                         if (dirent.head != dir->head) {
851                                                 pwarn("'.' entry in %s has incorrect start cluster\n",
852                                                       fullpath(dir));
853                                                 if (ask(1, "Correct")) {
854                                                         dirent.head = dir->head;
855                                                         p[26] = (u_char)dirent.head;
856                                                         p[27] = (u_char)(dirent.head >> 8);
857                                                         if (boot->ClustMask == CLUST32_MASK) {
858                                                                 p[20] = (u_char)(dirent.head >> 16);
859                                                                 p[21] = (u_char)(dirent.head >> 24);
860                                                         }
861                                                         mod |= THISMOD|FSDIRMOD;
862                                                 } else
863                                                         mod |= FSERROR;
864                                         }
865                                         continue;
866                 } else if (strcmp(dirent.name, "..") == 0) {
867                                         if (dir->parent) {              /* XXX */
868                                                 if (!dir->parent->parent) {
869                                                         if (dirent.head) {
870                                                                 pwarn("'..' entry in %s has non-zero start cluster\n",
871                                                                       fullpath(dir));
872                                                                 if (ask(1, "Correct")) {
873                                                                         dirent.head = 0;
874                                                                         p[26] = p[27] = 0;
875                                                                         if (boot->ClustMask == CLUST32_MASK)
876                                                                                 p[20] = p[21] = 0;
877                                                                         mod |= THISMOD|FSDIRMOD;
878                                                                 } else
879                                                                         mod |= FSERROR;
880                                                         }
881                                                 } else if (dirent.head != dir->parent->head) {
882                                                         pwarn("'..' entry in %s has incorrect start cluster\n",
883                                                               fullpath(dir));
884                                                         if (ask(1, "Correct")) {
885                                                                 dirent.head = dir->parent->head;
886                                                                 p[26] = (u_char)dirent.head;
887                                                                 p[27] = (u_char)(dirent.head >> 8);
888                                                                 if (boot->ClustMask == CLUST32_MASK) {
889                                                                         p[20] = (u_char)(dirent.head >> 16);
890                                                                         p[21] = (u_char)(dirent.head >> 24);
891                                                                 }
892                                                                 mod |= THISMOD|FSDIRMOD;
893                                                         } else
894                                                                 mod |= FSERROR;
895                                                 }
896                                         }
897                                         continue;
898                                 } else { //only one directory entry can point to dir->head, it's  '.'
899                                         if (dirent.head == dir->head) {
900                                                 pwarn("%s entry in %s has incorrect start cluster.remove\n",
901                                                                 dirent.name, fullpath(dir));
902                                                 //we have to remove this directory entry rigth now rigth here
903                                                 if (ask(1, "Remove")) {
904                                                         *p = SLOT_DELETED;
905                                                         mod |= THISMOD|FSDIRMOD;
906                                                 } else
907                                                         mod |= FSERROR;
908                                                 continue;
909                                         }
910                                         /* Consistency checking. a directory must have at least two entries:
911                                            a dot (.) entry that points to itself, and a dot-dot (..)
912                                            entry that points to its parent.
913                                          */
914                                         if (check_dot_dot(f,boot,fat,&dirent)) {
915                                                 //mark directory entry as deleted.
916                                                 if (ask(1, "Remove")) {
917                                                         *p = SLOT_DELETED;
918                                                         mod |= THISMOD|FSDIRMOD;
919                                                 } else
920                                                         mod |= FSERROR;
921                                                 continue;
922                     }
923                                 }
924
925                                 /* create directory tree node */
926                                 if (!(d = newDosDirEntry())) {
927                                         perror("No space for directory");
928                                         return FSFATAL;
929                                 }
930                                 memcpy(d, &dirent, sizeof(struct dosDirEntry));
931                                 /* link it into the tree */
932                                 dir->child = d;
933 #if 0
934                                 printf("%s: %s : 0x%02x:head %d, next 0x%0x parent 0x%0x child 0x%0x\n",
935                                                 __func__,d->name,d->flags,d->head,d->next,d->parent,d->child);
936 #endif
937                                 /* Enter this directory into the todo list */
938                                 if (!(n = newDirTodo())) {
939                                         perror("No space for todo list");
940                                         return FSFATAL;
941                                 }
942                                 n->next = pendingDirectories;
943                                 n->dir = d;
944                                 pendingDirectories = n;
945                         } else {
946                                 mod |= k = checksize(boot, fat, p, &dirent);
947                                 if (k & FSDIRMOD)
948                                         mod |= THISMOD;
949                         }
950                         boot->NumFiles++;
951                 }
952                 if (mod & THISMOD) {
953                         last *= 32;
954                         if (lseek64(f, off, SEEK_SET) != off
955                             || write(f, buffer, last) != last) {
956                                 perror("Unable to write directory");
957                                 return FSFATAL;
958                         }
959                         mod &= ~THISMOD;
960                 }
961         } while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters);
962         if (invlfn || vallfn)
963                 mod |= removede(f, boot, fat,
964                                 invlfn ? invlfn : vallfn, p,
965                                 invlfn ? invcl : valcl, -1, 0,
966                                 fullpath(dir), 1);
967         return mod & ~THISMOD;
968 }
969
970 int
971 handleDirTree(int dosfs, struct bootblock *boot, struct fatEntry *fat)
972 {
973         int mod;
974
975         mod = readDosDirSection(dosfs, boot, fat, rootDir);
976         if (mod & FSFATAL)
977                 return FSFATAL;
978
979         /*
980          * process the directory todo list
981          */
982         while (pendingDirectories) {
983                 struct dosDirEntry *dir = pendingDirectories->dir;
984                 struct dirTodoNode *n = pendingDirectories->next;
985
986                 /*
987                  * remove TODO entry now, the list might change during
988                  * directory reads
989                  */
990                 freeDirTodo(pendingDirectories);
991                 pendingDirectories = n;
992
993                 /*
994                  * handle subdirectory
995                  */
996                 mod |= readDosDirSection(dosfs, boot, fat, dir);
997                 if (mod & FSFATAL)
998                         return FSFATAL;
999         }
1000
1001         return mod;
1002 }
1003
1004 /*
1005  * Try to reconnect a FAT chain into dir
1006  */
1007 static u_char *lfbuf;
1008 static cl_t lfcl;
1009 static loff_t lfoff;
1010
1011 int
1012 reconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t head)
1013 {
1014         struct dosDirEntry d;
1015         u_char *p;
1016
1017         if (!ask(1, "Reconnect"))
1018                 return FSERROR;
1019
1020         if (!lostDir) {
1021                 for (lostDir = rootDir->child; lostDir; lostDir = lostDir->next) {
1022                         if (!strcmp(lostDir->name, LOSTDIR))
1023                                 break;
1024                 }
1025                 if (!lostDir) {         /* Create LOSTDIR?              XXX */
1026                         pwarn("No %s directory\n", LOSTDIR);
1027                         return FSERROR;
1028                 }
1029         }
1030         if (!lfbuf) {
1031                 lfbuf = malloc(boot->ClusterSize);
1032                 if (!lfbuf) {
1033                         perror("No space for buffer");
1034                         return FSFATAL;
1035                 }
1036                 p = NULL;
1037         } else
1038                 p = lfbuf;
1039         while (1) {
1040                 if (p)
1041                         for (; p < lfbuf + boot->ClusterSize; p += 32)
1042                                 if (*p == SLOT_EMPTY
1043                                     || *p == SLOT_DELETED)
1044                                         break;
1045                 if (p && p < lfbuf + boot->ClusterSize)
1046                         break;
1047                 lfcl = p ? fat[lfcl].next : lostDir->head;
1048                 if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) {
1049                         /* Extend LOSTDIR?                              XXX */
1050                         pwarn("No space in %s\n", LOSTDIR);
1051                         lfcl = (lostDir->head < boot->NumClusters) ? lostDir->head : 0;
1052                         return FSERROR;
1053                 }
1054                 lfoff = lfcl * boot->ClusterSize
1055                     + boot->ClusterOffset * boot->BytesPerSec;
1056                 if (lseek64(dosfs, lfoff, SEEK_SET) != lfoff
1057                     || read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
1058                         perror("could not read LOST.DIR");
1059                         return FSFATAL;
1060                 }
1061                 p = lfbuf;
1062         }
1063
1064         boot->NumFiles++;
1065         /* Ensure uniqueness of entry here!                             XXX */
1066         memset(&d, 0, sizeof d);
1067         (void)snprintf(d.name, sizeof(d.name), "%u", head);
1068         d.flags = 0;
1069         d.head = head;
1070         d.size = fat[head].length * boot->ClusterSize;
1071
1072         memset(p, 0, 32);
1073         memset(p, ' ', 11);
1074         memcpy(p, d.name, strlen(d.name));
1075         p[26] = (u_char)d.head;
1076         p[27] = (u_char)(d.head >> 8);
1077         if (boot->ClustMask == CLUST32_MASK) {
1078                 p[20] = (u_char)(d.head >> 16);
1079                 p[21] = (u_char)(d.head >> 24);
1080         }
1081         p[28] = (u_char)d.size;
1082         p[29] = (u_char)(d.size >> 8);
1083         p[30] = (u_char)(d.size >> 16);
1084         p[31] = (u_char)(d.size >> 24);
1085         fat[head].flags |= FAT_USED;
1086         if (lseek64(dosfs, lfoff, SEEK_SET) != lfoff
1087             || write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
1088                 perror("could not write LOST.DIR");
1089                 return FSFATAL;
1090         }
1091         return FSDIRMOD;
1092 }
1093
1094 void
1095 finishlf(void)
1096 {
1097         if (lfbuf)
1098                 free(lfbuf);
1099         lfbuf = NULL;
1100 }