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