xref: /openbsd/sbin/fsck_msdos/dir.c (revision 6a697e54)
1*6a697e54Sjsg /*	$OpenBSD: dir.c,v 1.31 2017/06/27 12:10:21 jsg Exp $	*/
2b099d67bSprovos /*	$NetBSD: dir.c,v 1.11 1997/10/17 11:19:35 ws Exp $	*/
39646ab25Sderaadt 
49646ab25Sderaadt /*
5b099d67bSprovos  * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
69646ab25Sderaadt  * Copyright (c) 1995 Martin Husemann
79646ab25Sderaadt  * Some structure declaration borrowed from Paul Popelka
89646ab25Sderaadt  * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference.
99646ab25Sderaadt  *
109646ab25Sderaadt  * Redistribution and use in source and binary forms, with or without
119646ab25Sderaadt  * modification, are permitted provided that the following conditions
129646ab25Sderaadt  * are met:
139646ab25Sderaadt  * 1. Redistributions of source code must retain the above copyright
149646ab25Sderaadt  *    notice, this list of conditions and the following disclaimer.
159646ab25Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
169646ab25Sderaadt  *    notice, this list of conditions and the following disclaimer in the
179646ab25Sderaadt  *    documentation and/or other materials provided with the distribution.
189646ab25Sderaadt  *
199646ab25Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
209646ab25Sderaadt  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
219646ab25Sderaadt  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
229646ab25Sderaadt  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
239646ab25Sderaadt  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
249646ab25Sderaadt  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
259646ab25Sderaadt  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
269646ab25Sderaadt  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
279646ab25Sderaadt  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
289646ab25Sderaadt  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
299646ab25Sderaadt  */
309646ab25Sderaadt 
319646ab25Sderaadt #include <stdio.h>
329646ab25Sderaadt #include <stdlib.h>
339646ab25Sderaadt #include <string.h>
349646ab25Sderaadt #include <ctype.h>
359646ab25Sderaadt #include <unistd.h>
36b9fc9a72Sderaadt #include <limits.h>
379646ab25Sderaadt #include <time.h>
389646ab25Sderaadt 
399646ab25Sderaadt #include "ext.h"
409646ab25Sderaadt 
419646ab25Sderaadt #define	SLOT_EMPTY	0x00		/* slot has never been used */
429646ab25Sderaadt #define	SLOT_E5		0x05		/* the real value is 0xe5 */
439646ab25Sderaadt #define	SLOT_DELETED	0xe5		/* file in this slot deleted */
449646ab25Sderaadt 
459646ab25Sderaadt #define	ATTR_NORMAL	0x00		/* normal file */
469646ab25Sderaadt #define	ATTR_READONLY	0x01		/* file is readonly */
479646ab25Sderaadt #define	ATTR_HIDDEN	0x02		/* file is hidden */
489646ab25Sderaadt #define	ATTR_SYSTEM	0x04		/* file is a system file */
499646ab25Sderaadt #define	ATTR_VOLUME	0x08		/* entry is a volume label */
509646ab25Sderaadt #define	ATTR_DIRECTORY	0x10		/* entry is a directory name */
519646ab25Sderaadt #define	ATTR_ARCHIVE	0x20		/* file is new or modified */
529646ab25Sderaadt 
539646ab25Sderaadt #define	ATTR_WIN95	0x0f		/* long name record */
549646ab25Sderaadt 
559646ab25Sderaadt /*
569646ab25Sderaadt  * This is the format of the contents of the deTime field in the direntry
579646ab25Sderaadt  * structure.
589646ab25Sderaadt  * We don't use bitfields because we don't know how compilers for
599646ab25Sderaadt  * arbitrary machines will lay them out.
609646ab25Sderaadt  */
619646ab25Sderaadt #define DT_2SECONDS_MASK	0x1F	/* seconds divided by 2 */
629646ab25Sderaadt #define DT_2SECONDS_SHIFT	0
639646ab25Sderaadt #define DT_MINUTES_MASK		0x7E0	/* minutes */
649646ab25Sderaadt #define DT_MINUTES_SHIFT	5
659646ab25Sderaadt #define DT_HOURS_MASK		0xF800	/* hours */
669646ab25Sderaadt #define DT_HOURS_SHIFT		11
679646ab25Sderaadt 
689646ab25Sderaadt /*
699646ab25Sderaadt  * This is the format of the contents of the deDate field in the direntry
709646ab25Sderaadt  * structure.
719646ab25Sderaadt  */
729646ab25Sderaadt #define DD_DAY_MASK		0x1F	/* day of month */
739646ab25Sderaadt #define DD_DAY_SHIFT		0
749646ab25Sderaadt #define DD_MONTH_MASK		0x1E0	/* month */
759646ab25Sderaadt #define DD_MONTH_SHIFT		5
769646ab25Sderaadt #define DD_YEAR_MASK		0xFE00	/* year - 1980 */
779646ab25Sderaadt #define DD_YEAR_SHIFT		9
789646ab25Sderaadt 
79a0dbeb59Smillert /* dir.c */
80c72b5b24Smillert static struct dosDirEntry *newDosDirEntry(void);
81c72b5b24Smillert static void freeDosDirEntry(struct dosDirEntry *);
82c72b5b24Smillert static struct dirTodoNode *newDirTodo(void);
83c72b5b24Smillert static void freeDirTodo(struct dirTodoNode *);
84c72b5b24Smillert static char *fullpath(struct dosDirEntry *);
85c72b5b24Smillert static u_char calcShortSum(u_char *);
86f3c3a9c6Smillert static int delete(int, struct bootblock *, struct fatEntry *, cl_t, int,
87f3c3a9c6Smillert     cl_t, int, int);
88f3c3a9c6Smillert static int removede(int, struct bootblock *, struct fatEntry *, u_char *,
89f3c3a9c6Smillert     u_char *, cl_t, cl_t, cl_t, char *, int);
90f3c3a9c6Smillert static int checksize(struct bootblock *, struct fatEntry *, u_char *,
91f3c3a9c6Smillert     struct dosDirEntry *);
92f3c3a9c6Smillert static int readDosDirSection(int, struct bootblock *, struct fatEntry *,
93f3c3a9c6Smillert     struct dosDirEntry *);
94a0dbeb59Smillert 
959646ab25Sderaadt /*
962a4cde9cSderaadt  * Manage free dosDirEntry structures.
972a4cde9cSderaadt  */
982a4cde9cSderaadt static struct dosDirEntry *freede;
992a4cde9cSderaadt 
1002a4cde9cSderaadt static struct dosDirEntry *
newDosDirEntry(void)1014e95fccfSderaadt newDosDirEntry(void)
1022a4cde9cSderaadt {
1032a4cde9cSderaadt 	struct dosDirEntry *de;
1042a4cde9cSderaadt 
1052a4cde9cSderaadt 	if (!(de = freede)) {
1065ae94ef8Sderaadt 		if (!(de = malloc(sizeof *de)))
107a0dbeb59Smillert 			return (0);
1082a4cde9cSderaadt 	} else
1092a4cde9cSderaadt 		freede = de->next;
110a0dbeb59Smillert 	return (de);
1112a4cde9cSderaadt }
1122a4cde9cSderaadt 
1132a4cde9cSderaadt static void
freeDosDirEntry(struct dosDirEntry * de)1144e95fccfSderaadt freeDosDirEntry(struct dosDirEntry *de)
1152a4cde9cSderaadt {
1162a4cde9cSderaadt 	de->next = freede;
1172a4cde9cSderaadt 	freede = de;
1182a4cde9cSderaadt }
1192a4cde9cSderaadt 
1202a4cde9cSderaadt /*
1212a4cde9cSderaadt  * The same for dirTodoNode structures.
1222a4cde9cSderaadt  */
1232a4cde9cSderaadt static struct dirTodoNode *freedt;
1242a4cde9cSderaadt 
1252a4cde9cSderaadt static struct dirTodoNode *
newDirTodo(void)1264e95fccfSderaadt newDirTodo(void)
1272a4cde9cSderaadt {
1282a4cde9cSderaadt 	struct dirTodoNode *dt;
1292a4cde9cSderaadt 
1302a4cde9cSderaadt 	if (!(dt = freedt)) {
1315ae94ef8Sderaadt 		if (!(dt = malloc(sizeof *dt)))
132a0dbeb59Smillert 			return (0);
1332a4cde9cSderaadt 	} else
1342a4cde9cSderaadt 		freedt = dt->next;
135a0dbeb59Smillert 	return (dt);
1362a4cde9cSderaadt }
1372a4cde9cSderaadt 
1382a4cde9cSderaadt static void
freeDirTodo(struct dirTodoNode * dt)1394e95fccfSderaadt freeDirTodo(struct dirTodoNode *dt)
1402a4cde9cSderaadt {
1412a4cde9cSderaadt 	dt->next = freedt;
1422a4cde9cSderaadt 	freedt = dt;
1432a4cde9cSderaadt }
1442a4cde9cSderaadt 
1452a4cde9cSderaadt /*
1462a4cde9cSderaadt  * The stack of unread directories
1472a4cde9cSderaadt  */
148e55eaf4bStobias static struct dirTodoNode *pendingDirectories = NULL;
1492a4cde9cSderaadt 
1502a4cde9cSderaadt /*
1512a4cde9cSderaadt  * Return the full pathname for a directory entry.
1522a4cde9cSderaadt  */
1532a4cde9cSderaadt static char *
fullpath(struct dosDirEntry * dir)1544e95fccfSderaadt fullpath(struct dosDirEntry *dir)
1552a4cde9cSderaadt {
156b9fc9a72Sderaadt 	static char namebuf[PATH_MAX + 1];
1572a4cde9cSderaadt 	char *cp, *np;
1582a4cde9cSderaadt 	int nl;
1592a4cde9cSderaadt 
1605cb823e1Sespie 	cp = namebuf + sizeof namebuf;
1615cb823e1Sespie 	*--cp = '\0';
1625cb823e1Sespie 	for(;;) {
1632a4cde9cSderaadt 		np = dir->lname[0] ? dir->lname : dir->name;
1642a4cde9cSderaadt 		nl = strlen(np);
1655cb823e1Sespie 			/* cf dosDirEntry, sizeof(lname) < MAXPATHLEN, so test is safe */
1665cb823e1Sespie 		if (cp <= namebuf + 1 + nl) {
1672a4cde9cSderaadt 			*--cp = '?';
1685cb823e1Sespie 			break;
1695cb823e1Sespie 		}
1705cb823e1Sespie 		cp -= nl;
1715cb823e1Sespie 		(void)memcpy(cp, np, nl);
1725cb823e1Sespie 		dir = dir->parent;
1735cb823e1Sespie 		if (!dir)
1745cb823e1Sespie 			break;
1755cb823e1Sespie 		*--cp = '/';
1765cb823e1Sespie 	}
177a0dbeb59Smillert 	return (cp);
1782a4cde9cSderaadt }
1792a4cde9cSderaadt 
1802a4cde9cSderaadt /*
1819646ab25Sderaadt  * Calculate a checksum over an 8.3 alias name
1829646ab25Sderaadt  */
1839646ab25Sderaadt static u_char
calcShortSum(u_char * p)1844e95fccfSderaadt calcShortSum(u_char *p)
1859646ab25Sderaadt {
1869646ab25Sderaadt 	u_char sum = 0;
1879646ab25Sderaadt 	int i;
1889646ab25Sderaadt 
1899646ab25Sderaadt 	for (i = 0; i < 11; i++) {
1909646ab25Sderaadt 		sum = (sum << 7)|(sum >> 1);	/* rotate right */
1919646ab25Sderaadt 		sum += p[i];
1929646ab25Sderaadt 	}
1939646ab25Sderaadt 
194a0dbeb59Smillert 	return (sum);
1959646ab25Sderaadt }
1969646ab25Sderaadt 
1979646ab25Sderaadt /*
1989646ab25Sderaadt  * Global variables temporarily used during a directory scan
1999646ab25Sderaadt  */
2009646ab25Sderaadt static char longName[DOSLONGNAMELEN] = "";
2019646ab25Sderaadt static u_char *buffer = NULL;
2029646ab25Sderaadt static u_char *delbuf = NULL;
2039646ab25Sderaadt 
204e55eaf4bStobias static struct dosDirEntry *rootDir;
2052a4cde9cSderaadt static struct dosDirEntry *lostDir;
2062a4cde9cSderaadt 
2079646ab25Sderaadt /*
2089646ab25Sderaadt  * Init internal state for a new directory scan.
2099646ab25Sderaadt  */
2109646ab25Sderaadt int
resetDosDirSection(struct bootblock * boot,struct fatEntry * fat)2114e95fccfSderaadt resetDosDirSection(struct bootblock *boot, struct fatEntry *fat)
2129646ab25Sderaadt {
2139646ab25Sderaadt 	int b1, b2;
214b099d67bSprovos 	int ret = FSOK;
2159646ab25Sderaadt 
2169646ab25Sderaadt 	b1 = boot->RootDirEnts * 32;
2179646ab25Sderaadt 	b2 = boot->SecPerClust * boot->BytesPerSec;
2189646ab25Sderaadt 
2199646ab25Sderaadt 	if (!(buffer = malloc(b1 > b2 ? b1 : b2))
2202a4cde9cSderaadt 	    || !(delbuf = malloc(b2))
2212a4cde9cSderaadt 	    || !(rootDir = newDosDirEntry())) {
222dd133929Sthib 		xperror("No space for directory");
223e55eaf4bStobias 		goto fail;
2249646ab25Sderaadt 	}
225a0dbeb59Smillert 	(void)memset(rootDir, 0, sizeof *rootDir);
226b099d67bSprovos 	if (boot->flags & FAT32) {
227b099d67bSprovos 		if (boot->RootCl < CLUST_FIRST || boot->RootCl >= boot->NumClusters) {
22862d85a42Sian 			pfatal("Root directory starts with cluster out of range(%u)\n",
229b099d67bSprovos 			       boot->RootCl);
230e55eaf4bStobias 			goto fail;
231b099d67bSprovos 		}
23219675891Stobias 		if (fat[boot->RootCl].head != boot->RootCl) {
23362d85a42Sian 			pfatal("Root directory doesn't start a cluster chain\n");
234e55eaf4bStobias 			goto fail;
235b099d67bSprovos 		}
236b099d67bSprovos 
237b099d67bSprovos 		fat[boot->RootCl].flags |= FAT_USED;
238b099d67bSprovos 		rootDir->head = boot->RootCl;
239b099d67bSprovos 	}
240b099d67bSprovos 
241b099d67bSprovos 	return (ret);
242e55eaf4bStobias fail:
243e55eaf4bStobias 	finishDosDirSection();
244e55eaf4bStobias 	return (FSFATAL);
2459646ab25Sderaadt }
2469646ab25Sderaadt 
2479646ab25Sderaadt /*
2489646ab25Sderaadt  * Cleanup after a directory scan
2499646ab25Sderaadt  */
2509646ab25Sderaadt void
finishDosDirSection(void)2514e95fccfSderaadt finishDosDirSection(void)
2529646ab25Sderaadt {
2532a4cde9cSderaadt 	struct dirTodoNode *p, *np;
2542a4cde9cSderaadt 	struct dosDirEntry *d, *nd;
2552a4cde9cSderaadt 
2562a4cde9cSderaadt 	for (p = pendingDirectories; p; p = np) {
2572a4cde9cSderaadt 		np = p->next;
2582a4cde9cSderaadt 		freeDirTodo(p);
2592a4cde9cSderaadt 	}
2602a4cde9cSderaadt 	pendingDirectories = 0;
2612a4cde9cSderaadt 	for (d = rootDir; d; d = nd) {
262a0dbeb59Smillert 		if ((nd = d->child) != NULL) {
2632a4cde9cSderaadt 			d->child = 0;
2642a4cde9cSderaadt 			continue;
2652a4cde9cSderaadt 		}
2662a4cde9cSderaadt 		if (!(nd = d->next))
2672a4cde9cSderaadt 			nd = d->parent;
2682a4cde9cSderaadt 		freeDosDirEntry(d);
2692a4cde9cSderaadt 	}
2702a4cde9cSderaadt 	rootDir = lostDir = NULL;
2719646ab25Sderaadt 	free(buffer);
2729646ab25Sderaadt 	free(delbuf);
2739646ab25Sderaadt 	buffer = NULL;
2749646ab25Sderaadt 	delbuf = NULL;
2759646ab25Sderaadt }
2769646ab25Sderaadt 
2779646ab25Sderaadt /*
2789646ab25Sderaadt  * Delete directory entries between startcl, startoff and endcl, endoff.
2799646ab25Sderaadt  */
2809646ab25Sderaadt static int
delete(int f,struct bootblock * boot,struct fatEntry * fat,cl_t startcl,int startoff,cl_t endcl,int endoff,int notlast)2814e95fccfSderaadt delete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl,
2824e95fccfSderaadt        int startoff, cl_t endcl, int endoff, int notlast)
2839646ab25Sderaadt {
2849646ab25Sderaadt 	u_char *s, *e;
2859646ab25Sderaadt 	off_t off;
2869646ab25Sderaadt 	int clsz = boot->SecPerClust * boot->BytesPerSec;
2879646ab25Sderaadt 
2889646ab25Sderaadt 	s = delbuf + startoff;
2899646ab25Sderaadt 	e = delbuf + clsz;
2909646ab25Sderaadt 	while (startcl >= CLUST_FIRST && startcl < boot->NumClusters) {
2919646ab25Sderaadt 		if (startcl == endcl) {
2929646ab25Sderaadt 			if (notlast)
2939646ab25Sderaadt 				break;
2949646ab25Sderaadt 			e = delbuf + endoff;
2959646ab25Sderaadt 		}
2969646ab25Sderaadt 		off = startcl * boot->SecPerClust + boot->ClusterOffset;
2979646ab25Sderaadt 		off *= boot->BytesPerSec;
2989646ab25Sderaadt 		if (lseek(f, off, SEEK_SET) != off
2999646ab25Sderaadt 		    || read(f, delbuf, clsz) != clsz) {
300dd133929Sthib 			xperror("Unable to read directory");
301a0dbeb59Smillert 			return (FSFATAL);
3029646ab25Sderaadt 		}
3039646ab25Sderaadt 		while (s < e) {
3049646ab25Sderaadt 			*s = SLOT_DELETED;
3059646ab25Sderaadt 			s += 32;
3069646ab25Sderaadt 		}
3079646ab25Sderaadt 		if (lseek(f, off, SEEK_SET) != off
3089646ab25Sderaadt 		    || write(f, delbuf, clsz) != clsz) {
309dd133929Sthib 			xperror("Unable to write directory");
310a0dbeb59Smillert 			return (FSFATAL);
3119646ab25Sderaadt 		}
3129646ab25Sderaadt 		if (startcl == endcl)
3139646ab25Sderaadt 			break;
3149646ab25Sderaadt 		startcl = fat[startcl].next;
3159646ab25Sderaadt 		s = delbuf;
3169646ab25Sderaadt 	}
317a0dbeb59Smillert 	return (FSOK);
3189646ab25Sderaadt }
3199646ab25Sderaadt 
3209646ab25Sderaadt static int
removede(int f,struct bootblock * boot,struct fatEntry * fat,u_char * start,u_char * end,cl_t startcl,cl_t endcl,cl_t curcl,char * path,int type)3214e95fccfSderaadt removede(int f, struct bootblock *boot, struct fatEntry *fat, u_char *start,
3224e95fccfSderaadt 	 u_char *end, cl_t startcl, cl_t endcl, cl_t curcl, char *path, int type)
3239646ab25Sderaadt {
3242a4cde9cSderaadt 	switch (type) {
3252a4cde9cSderaadt 	case 0:
3269646ab25Sderaadt 		pwarn("Invalid long filename entry for %s\n", path);
3272a4cde9cSderaadt 		break;
3282a4cde9cSderaadt 	case 1:
3299646ab25Sderaadt 		pwarn("Invalid long filename entry at end of directory %s\n", path);
3302a4cde9cSderaadt 		break;
3312a4cde9cSderaadt 	case 2:
3322a4cde9cSderaadt 		pwarn("Invalid long filename entry for volume label\n");
3332a4cde9cSderaadt 		break;
3342a4cde9cSderaadt 	}
3359646ab25Sderaadt 	if (ask(0, "Remove")) {
3369646ab25Sderaadt 		if (startcl != curcl) {
3379646ab25Sderaadt 			if (delete(f, boot, fat,
3389646ab25Sderaadt 				   startcl, start - buffer,
3399646ab25Sderaadt 				   endcl, end - buffer,
3409646ab25Sderaadt 				   endcl == curcl) == FSFATAL)
341a0dbeb59Smillert 				return (FSFATAL);
3429646ab25Sderaadt 			start = buffer;
3439646ab25Sderaadt 		}
3449646ab25Sderaadt 		if (endcl == curcl)
3459646ab25Sderaadt 			for (; start < end; start += 32)
3469646ab25Sderaadt 				*start = SLOT_DELETED;
347a0dbeb59Smillert 		return (FSDIRMOD);
3489646ab25Sderaadt 	}
349a0dbeb59Smillert 	return (FSERROR);
3509646ab25Sderaadt }
3519646ab25Sderaadt 
3529646ab25Sderaadt /*
3539646ab25Sderaadt  * Check an in-memory file entry
3549646ab25Sderaadt  */
3559646ab25Sderaadt static int
checksize(struct bootblock * boot,struct fatEntry * fat,u_char * p,struct dosDirEntry * dir)3564e95fccfSderaadt checksize(struct bootblock *boot, struct fatEntry *fat, u_char *p,
3574e95fccfSderaadt 	  struct dosDirEntry *dir)
3589646ab25Sderaadt {
3599646ab25Sderaadt 	/*
3609646ab25Sderaadt 	 * Check size on ordinary files
3619646ab25Sderaadt 	 */
36283571c46Stobias 	u_int32_t physicalSize;
3639646ab25Sderaadt 
3642a4cde9cSderaadt 	if (dir->head == CLUST_FREE)
3652a4cde9cSderaadt 		physicalSize = 0;
3662a4cde9cSderaadt 	else {
3679646ab25Sderaadt 		if (dir->head < CLUST_FIRST || dir->head >= boot->NumClusters)
368a0dbeb59Smillert 			return (FSERROR);
3699646ab25Sderaadt 		physicalSize = fat[dir->head].length * boot->ClusterSize;
3702a4cde9cSderaadt 	}
3719646ab25Sderaadt 	if (physicalSize < dir->size) {
372a0dbeb59Smillert 		pwarn("size of %s is %u, should at most be %u\n",
3732a4cde9cSderaadt 		      fullpath(dir), dir->size, physicalSize);
3749646ab25Sderaadt 		if (ask(1, "Truncate")) {
3759646ab25Sderaadt 			dir->size = physicalSize;
3769646ab25Sderaadt 			p[28] = (u_char)physicalSize;
3779646ab25Sderaadt 			p[29] = (u_char)(physicalSize >> 8);
3789646ab25Sderaadt 			p[30] = (u_char)(physicalSize >> 16);
3799646ab25Sderaadt 			p[31] = (u_char)(physicalSize >> 24);
380a0dbeb59Smillert 			return (FSDIRMOD);
3819646ab25Sderaadt 		} else
382a0dbeb59Smillert 			return (FSERROR);
3839646ab25Sderaadt 	} else if (physicalSize - dir->size >= boot->ClusterSize) {
3849646ab25Sderaadt 		pwarn("%s has too many clusters allocated\n",
3852a4cde9cSderaadt 		      fullpath(dir));
3869646ab25Sderaadt 		if (ask(1, "Drop superfluous clusters")) {
3879646ab25Sderaadt 			cl_t cl;
38883571c46Stobias 			u_int32_t len, sz;
3899646ab25Sderaadt 
39083571c46Stobias 			len = sz = 0;
39183571c46Stobias 			for (cl = dir->head; (sz += boot->ClusterSize) < dir->size;) {
3929646ab25Sderaadt 				cl = fat[cl].next;
39383571c46Stobias 				len++;
39483571c46Stobias 			}
3959646ab25Sderaadt 			clearchain(boot, fat, fat[cl].next);
3969646ab25Sderaadt 			fat[cl].next = CLUST_EOF;
39783571c46Stobias 			fat[dir->head].length = len;
398a0dbeb59Smillert 			return (FSFATMOD);
3999646ab25Sderaadt 		} else
400a0dbeb59Smillert 			return (FSERROR);
4019646ab25Sderaadt 	}
402a0dbeb59Smillert 	return (FSOK);
4039646ab25Sderaadt }
4049646ab25Sderaadt 
4059646ab25Sderaadt /*
4069646ab25Sderaadt  * Read a directory and
4079646ab25Sderaadt  *   - resolve long name records
4089646ab25Sderaadt  *   - enter file and directory records into the parent's list
4099646ab25Sderaadt  *   - push directories onto the todo-stack
4109646ab25Sderaadt  */
4112a4cde9cSderaadt static int
readDosDirSection(int f,struct bootblock * boot,struct fatEntry * fat,struct dosDirEntry * dir)4124e95fccfSderaadt readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat,
4134e95fccfSderaadt 		  struct dosDirEntry *dir)
4149646ab25Sderaadt {
4159646ab25Sderaadt 	struct dosDirEntry dirent, *d;
4169646ab25Sderaadt 	u_char *p, *vallfn, *invlfn, *empty;
4179646ab25Sderaadt 	off_t off;
4189646ab25Sderaadt 	int i, j, k, last;
419a0dbeb59Smillert 	cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0;
4209646ab25Sderaadt 	char *t;
4219646ab25Sderaadt 	u_int lidx = 0;
4229646ab25Sderaadt 	int shortSum;
4239646ab25Sderaadt 	int mod = FSOK;
4249646ab25Sderaadt #define	THISMOD	0x8000			/* Only used within this routine */
4259646ab25Sderaadt 
4269646ab25Sderaadt 	cl = dir->head;
4272a4cde9cSderaadt 	if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
4289646ab25Sderaadt 		/*
4299646ab25Sderaadt 		 * Already handled somewhere else.
4309646ab25Sderaadt 		 */
431a0dbeb59Smillert 		return (FSOK);
4329646ab25Sderaadt 	}
4339646ab25Sderaadt 	shortSum = -1;
4349646ab25Sderaadt 	vallfn = invlfn = empty = NULL;
4359646ab25Sderaadt 	do {
436b099d67bSprovos 		if (!(boot->flags & FAT32) && !dir->parent) {
4379646ab25Sderaadt 			last = boot->RootDirEnts * 32;
4389646ab25Sderaadt 			off = boot->ResSectors + boot->FATs * boot->FATsecs;
4399646ab25Sderaadt 		} else {
4409646ab25Sderaadt 			last = boot->SecPerClust * boot->BytesPerSec;
4419646ab25Sderaadt 			off = cl * boot->SecPerClust + boot->ClusterOffset;
4429646ab25Sderaadt 		}
4439646ab25Sderaadt 
4449646ab25Sderaadt 		off *= boot->BytesPerSec;
4459646ab25Sderaadt 		if (lseek(f, off, SEEK_SET) != off
4469646ab25Sderaadt 		    || read(f, buffer, last) != last) {
447dd133929Sthib 			xperror("Unable to read directory");
448a0dbeb59Smillert 			return (FSFATAL);
4499646ab25Sderaadt 		}
4509646ab25Sderaadt 		last /= 32;
4519646ab25Sderaadt 		/*
4529646ab25Sderaadt 		 * Check `.' and `..' entries here?			XXX
4539646ab25Sderaadt 		 */
4549646ab25Sderaadt 		for (p = buffer, i = 0; i < last; i++, p += 32) {
4559646ab25Sderaadt 			if (dir->fsckflags & DIREMPWARN) {
4569646ab25Sderaadt 				*p = SLOT_EMPTY;
4579646ab25Sderaadt 				continue;
4589646ab25Sderaadt 			}
4599646ab25Sderaadt 
4609646ab25Sderaadt 			if (*p == SLOT_EMPTY || *p == SLOT_DELETED) {
4619646ab25Sderaadt 				if (*p == SLOT_EMPTY) {
4629646ab25Sderaadt 					dir->fsckflags |= DIREMPTY;
4639646ab25Sderaadt 					empty = p;
4649646ab25Sderaadt 					empcl = cl;
4659646ab25Sderaadt 				}
4669646ab25Sderaadt 				continue;
4679646ab25Sderaadt 			}
4689646ab25Sderaadt 
4699646ab25Sderaadt 			if (dir->fsckflags & DIREMPTY) {
4709646ab25Sderaadt 				if (!(dir->fsckflags & DIREMPWARN)) {
4719646ab25Sderaadt 					pwarn("%s has entries after end of directory\n",
4722a4cde9cSderaadt 					      fullpath(dir));
4739646ab25Sderaadt 					if (ask(1, "Extend")) {
474a0dbeb59Smillert 						u_char *q;
475a0dbeb59Smillert 
4769646ab25Sderaadt 						dir->fsckflags &= ~DIREMPTY;
4779646ab25Sderaadt 						if (delete(f, boot, fat,
4789646ab25Sderaadt 							   empcl, empty - buffer,
479a0dbeb59Smillert 							   cl, p - buffer, 1) == FSFATAL)
480a0dbeb59Smillert 							return (FSFATAL);
481a0dbeb59Smillert 						q = empcl == cl ? empty : buffer;
482a0dbeb59Smillert 						for (; q < p; q += 32)
483a0dbeb59Smillert 							*q = SLOT_DELETED;
484a0dbeb59Smillert 						mod |= THISMOD|FSDIRMOD;
4859646ab25Sderaadt 					} else if (ask(0, "Truncate"))
4869646ab25Sderaadt 						dir->fsckflags |= DIREMPWARN;
4879646ab25Sderaadt 				}
4889646ab25Sderaadt 				if (dir->fsckflags & DIREMPWARN) {
4899646ab25Sderaadt 					*p = SLOT_DELETED;
4909646ab25Sderaadt 					mod |= THISMOD|FSDIRMOD;
4919646ab25Sderaadt 					continue;
4929646ab25Sderaadt 				} else if (dir->fsckflags & DIREMPTY)
4939646ab25Sderaadt 					mod |= FSERROR;
4949646ab25Sderaadt 				empty = NULL;
4959646ab25Sderaadt 			}
4969646ab25Sderaadt 
4979646ab25Sderaadt 			if (p[11] == ATTR_WIN95) {
4989646ab25Sderaadt 				if (*p & LRFIRST) {
4999646ab25Sderaadt 					if (shortSum != -1) {
5009646ab25Sderaadt 						if (!invlfn) {
5019646ab25Sderaadt 							invlfn = vallfn;
5029646ab25Sderaadt 							invcl = valcl;
5039646ab25Sderaadt 						}
5049646ab25Sderaadt 					}
505a0dbeb59Smillert 					(void)memset(longName, 0, sizeof longName);
5069646ab25Sderaadt 					shortSum = p[13];
5079646ab25Sderaadt 					vallfn = p;
5089646ab25Sderaadt 					valcl = cl;
5099646ab25Sderaadt 				} else if (shortSum != p[13]
510c7481c7eSmillert 					   || lidx != (*p & LRNOMASK)) {
5119646ab25Sderaadt 					if (!invlfn) {
5129646ab25Sderaadt 						invlfn = vallfn;
5139646ab25Sderaadt 						invcl = valcl;
5149646ab25Sderaadt 					}
5159646ab25Sderaadt 					if (!invlfn) {
5169646ab25Sderaadt 						invlfn = p;
5179646ab25Sderaadt 						invcl = cl;
5189646ab25Sderaadt 					}
5199646ab25Sderaadt 					vallfn = NULL;
5209646ab25Sderaadt 				}
5219646ab25Sderaadt 				lidx = *p & LRNOMASK;
522fe6e501bStobias 				if (lidx == 0) {
523fe6e501bStobias 					if (!invlfn) {
524fe6e501bStobias 						invlfn = vallfn;
525fe6e501bStobias 						invcl = valcl;
526fe6e501bStobias 					}
527fe6e501bStobias 					vallfn = NULL;
528fe6e501bStobias 					continue;
529fe6e501bStobias 				}
5309646ab25Sderaadt 				t = longName + --lidx * 13;
5319646ab25Sderaadt 				for (k = 1; k < 11 && t < longName + sizeof(longName); k += 2) {
5329646ab25Sderaadt 					if (!p[k] && !p[k + 1])
5339646ab25Sderaadt 						break;
5349646ab25Sderaadt 					*t++ = p[k];
5359646ab25Sderaadt 					/*
5369646ab25Sderaadt 					 * Warn about those unusable chars in msdosfs here?	XXX
5379646ab25Sderaadt 					 */
5389646ab25Sderaadt 					if (p[k + 1])
5399646ab25Sderaadt 						t[-1] = '?';
5409646ab25Sderaadt 				}
5419646ab25Sderaadt 				if (k >= 11)
5429646ab25Sderaadt 					for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) {
5439646ab25Sderaadt 						if (!p[k] && !p[k + 1])
5449646ab25Sderaadt 							break;
5459646ab25Sderaadt 						*t++ = p[k];
5469646ab25Sderaadt 						if (p[k + 1])
5479646ab25Sderaadt 							t[-1] = '?';
5489646ab25Sderaadt 					}
5499646ab25Sderaadt 				if (k >= 26)
5509646ab25Sderaadt 					for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) {
5519646ab25Sderaadt 						if (!p[k] && !p[k + 1])
5529646ab25Sderaadt 							break;
5539646ab25Sderaadt 						*t++ = p[k];
5549646ab25Sderaadt 						if (p[k + 1])
5559646ab25Sderaadt 							t[-1] = '?';
5569646ab25Sderaadt 					}
5579646ab25Sderaadt 				if (t >= longName + sizeof(longName)) {
5589646ab25Sderaadt 					pwarn("long filename too long\n");
5599646ab25Sderaadt 					if (!invlfn) {
5609646ab25Sderaadt 						invlfn = vallfn;
5619646ab25Sderaadt 						invcl = valcl;
5629646ab25Sderaadt 					}
5639646ab25Sderaadt 					vallfn = NULL;
5649646ab25Sderaadt 				}
5659646ab25Sderaadt 				if (p[26] | (p[27] << 8)) {
5669646ab25Sderaadt 					pwarn("long filename record cluster start != 0\n");
5679646ab25Sderaadt 					if (!invlfn) {
5689646ab25Sderaadt 						invlfn = vallfn;
5699646ab25Sderaadt 						invcl = cl;
5709646ab25Sderaadt 					}
5719646ab25Sderaadt 					vallfn = NULL;
5729646ab25Sderaadt 				}
5739646ab25Sderaadt 				continue;	/* long records don't carry further
5749646ab25Sderaadt 						 * information */
5759646ab25Sderaadt 			}
5769646ab25Sderaadt 
5779646ab25Sderaadt 			/*
5789646ab25Sderaadt 			 * This is a standard msdosfs directory entry.
5799646ab25Sderaadt 			 */
580a0dbeb59Smillert 			(void)memset(&dirent, 0, sizeof dirent);
5819646ab25Sderaadt 
5829646ab25Sderaadt 			/*
5839646ab25Sderaadt 			 * it's a short name record, but we need to know
5849646ab25Sderaadt 			 * more, so get the flags first.
5859646ab25Sderaadt 			 */
5869646ab25Sderaadt 			dirent.flags = p[11];
5879646ab25Sderaadt 
5889646ab25Sderaadt 			/*
5899646ab25Sderaadt 			 * Translate from 850 to ISO here		XXX
5909646ab25Sderaadt 			 */
5919646ab25Sderaadt 			for (j = 0; j < 8; j++)
5929646ab25Sderaadt 				dirent.name[j] = p[j];
5939646ab25Sderaadt 			dirent.name[8] = '\0';
5949646ab25Sderaadt 			for (k = 7; k >= 0 && dirent.name[k] == ' '; k--)
5959646ab25Sderaadt 				dirent.name[k] = '\0';
596*6a697e54Sjsg 			if (k < 0)
597*6a697e54Sjsg 				k = 0;
5989646ab25Sderaadt 			if (dirent.name[k] != '\0')
5999646ab25Sderaadt 				k++;
6009646ab25Sderaadt 			if (dirent.name[0] == SLOT_E5)
6019646ab25Sderaadt 				dirent.name[0] = 0xe5;
6022a4cde9cSderaadt 
6032a4cde9cSderaadt 			if (dirent.flags & ATTR_VOLUME) {
6042a4cde9cSderaadt 				if (vallfn || invlfn) {
6052a4cde9cSderaadt 					mod |= removede(f, boot, fat,
6062a4cde9cSderaadt 							invlfn ? invlfn : vallfn, p,
6072a4cde9cSderaadt 							invlfn ? invcl : valcl, -1, 0,
6082a4cde9cSderaadt 							fullpath(dir), 2);
6092a4cde9cSderaadt 					vallfn = NULL;
6102a4cde9cSderaadt 					invlfn = NULL;
6112a4cde9cSderaadt 				}
6122a4cde9cSderaadt 				continue;
6132a4cde9cSderaadt 			}
6142a4cde9cSderaadt 
6152a4cde9cSderaadt 			if (p[8] != ' ')
6169646ab25Sderaadt 				dirent.name[k++] = '.';
6179646ab25Sderaadt 			for (j = 0; j < 3; j++)
6189646ab25Sderaadt 				dirent.name[k++] = p[j+8];
6199646ab25Sderaadt 			dirent.name[k] = '\0';
6209646ab25Sderaadt 			for (k--; k >= 0 && dirent.name[k] == ' '; k--)
6219646ab25Sderaadt 				dirent.name[k] = '\0';
6229646ab25Sderaadt 
6239646ab25Sderaadt 			if (vallfn && shortSum != calcShortSum(p)) {
6249646ab25Sderaadt 				if (!invlfn) {
6259646ab25Sderaadt 					invlfn = vallfn;
6269646ab25Sderaadt 					invcl = valcl;
6279646ab25Sderaadt 				}
6289646ab25Sderaadt 				vallfn = NULL;
6299646ab25Sderaadt 			}
6309646ab25Sderaadt 			dirent.head = p[26] | (p[27] << 8);
631b099d67bSprovos 			if (boot->ClustMask == CLUST32_MASK)
632b099d67bSprovos 				dirent.head |= (p[20] << 16) | (p[21] << 24);
6339646ab25Sderaadt 			dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24);
6349646ab25Sderaadt 			if (vallfn) {
635334e018bSderaadt 				strlcpy(dirent.lname, longName, sizeof dirent.lname);
6369646ab25Sderaadt 				longName[0] = '\0';
6379646ab25Sderaadt 				shortSum = -1;
6389646ab25Sderaadt 			}
6399646ab25Sderaadt 
640b099d67bSprovos 			dirent.parent = dir;
641b099d67bSprovos 			dirent.next = dir->child;
642b099d67bSprovos 
6439646ab25Sderaadt 			if (invlfn) {
6449646ab25Sderaadt 				mod |= k = removede(f, boot, fat,
6459646ab25Sderaadt 						    invlfn, vallfn ? vallfn : p,
6469646ab25Sderaadt 						    invcl, vallfn ? valcl : cl, cl,
6472a4cde9cSderaadt 						    fullpath(&dirent), 0);
6489646ab25Sderaadt 				if (mod & FSFATAL)
649a0dbeb59Smillert 					return (FSFATAL);
6509646ab25Sderaadt 				if (vallfn
6519646ab25Sderaadt 				    ? (valcl == cl && vallfn != buffer)
6529646ab25Sderaadt 				    : p != buffer)
6539646ab25Sderaadt 					if (k & FSDIRMOD)
6549646ab25Sderaadt 						mod |= THISMOD;
6559646ab25Sderaadt 			}
656b099d67bSprovos 
6579646ab25Sderaadt 			vallfn = NULL; /* not used any longer */
6589646ab25Sderaadt 			invlfn = NULL;
6599646ab25Sderaadt 
6609646ab25Sderaadt 			if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) {
6619646ab25Sderaadt 				if (dirent.head != 0) {
6629646ab25Sderaadt 					pwarn("%s has clusters, but size 0\n",
6632a4cde9cSderaadt 					      fullpath(&dirent));
6649646ab25Sderaadt 					if (ask(1, "Drop allocated clusters")) {
6659646ab25Sderaadt 						p[26] = p[27] = 0;
666b099d67bSprovos 						if (boot->ClustMask == CLUST32_MASK)
667b099d67bSprovos 							p[20] = p[21] = 0;
6689646ab25Sderaadt 						clearchain(boot, fat, dirent.head);
6699646ab25Sderaadt 						dirent.head = 0;
6709646ab25Sderaadt 						mod |= THISMOD|FSDIRMOD|FSFATMOD;
6719646ab25Sderaadt 					} else
6729646ab25Sderaadt 						mod |= FSERROR;
6739646ab25Sderaadt 				}
6749646ab25Sderaadt 			} else if (dirent.head == 0
6759646ab25Sderaadt 				   && !strcmp(dirent.name, "..")
6762a4cde9cSderaadt 				   && dir->parent			/* XXX */
6772a4cde9cSderaadt 				   && !dir->parent->parent) {
6789646ab25Sderaadt 				/*
6799646ab25Sderaadt 				 *  Do nothing, the parent is the root
6809646ab25Sderaadt 				 */
6819646ab25Sderaadt 			} else if (dirent.head < CLUST_FIRST
6829646ab25Sderaadt 				   || dirent.head >= boot->NumClusters
6839646ab25Sderaadt 				   || fat[dirent.head].next == CLUST_FREE
6849646ab25Sderaadt 				   || (fat[dirent.head].next >= CLUST_RSRVD
6859646ab25Sderaadt 				       && fat[dirent.head].next < CLUST_EOFS)
6869646ab25Sderaadt 				   || fat[dirent.head].head != dirent.head) {
6879646ab25Sderaadt 				if (dirent.head == 0)
6889646ab25Sderaadt 					pwarn("%s has no clusters\n",
6892a4cde9cSderaadt 					      fullpath(&dirent));
6909646ab25Sderaadt 				else if (dirent.head < CLUST_FIRST
6919646ab25Sderaadt 					 || dirent.head >= boot->NumClusters)
692b099d67bSprovos 					pwarn("%s starts with cluster out of range(%u)\n",
6932a4cde9cSderaadt 					      fullpath(&dirent),
6949646ab25Sderaadt 					      dirent.head);
6959646ab25Sderaadt 				else if (fat[dirent.head].next == CLUST_FREE)
6969646ab25Sderaadt 					pwarn("%s starts with free cluster\n",
6972a4cde9cSderaadt 					      fullpath(&dirent));
6989646ab25Sderaadt 				else if (fat[dirent.head].next >= CLUST_RSRVD)
699b099d67bSprovos 					pwarn("%s starts with cluster marked %s\n",
7002a4cde9cSderaadt 					      fullpath(&dirent),
7019646ab25Sderaadt 					      rsrvdcltype(fat[dirent.head].next));
7029646ab25Sderaadt 				else
7039646ab25Sderaadt 					pwarn("%s doesn't start a cluster chain\n",
7042a4cde9cSderaadt 					      fullpath(&dirent));
7059646ab25Sderaadt 				if (dirent.flags & ATTR_DIRECTORY) {
7069646ab25Sderaadt 					if (ask(0, "Remove")) {
7079646ab25Sderaadt 						*p = SLOT_DELETED;
7089646ab25Sderaadt 						mod |= THISMOD|FSDIRMOD;
7099646ab25Sderaadt 					} else
7109646ab25Sderaadt 						mod |= FSERROR;
7119646ab25Sderaadt 					continue;
7129646ab25Sderaadt 				} else {
7139646ab25Sderaadt 					if (ask(1, "Truncate")) {
7149646ab25Sderaadt 						p[28] = p[29] = p[30] = p[31] = 0;
715b099d67bSprovos 						p[26] = p[27] = 0;
716b099d67bSprovos 						if (boot->ClustMask == CLUST32_MASK)
717b099d67bSprovos 							p[20] = p[21] = 0;
7189646ab25Sderaadt 						dirent.size = 0;
7199646ab25Sderaadt 						mod |= THISMOD|FSDIRMOD;
7209646ab25Sderaadt 					} else
7219646ab25Sderaadt 						mod |= FSERROR;
7229646ab25Sderaadt 				}
7239646ab25Sderaadt 			}
7249646ab25Sderaadt 
7252a4cde9cSderaadt 			if (dirent.head >= CLUST_FIRST && dirent.head < boot->NumClusters)
7262a4cde9cSderaadt 				fat[dirent.head].flags |= FAT_USED;
7279646ab25Sderaadt 
7282a4cde9cSderaadt 			if (dirent.flags & ATTR_DIRECTORY) {
7299646ab25Sderaadt 				/*
7309646ab25Sderaadt 				 * gather more info for directories
7319646ab25Sderaadt 				 */
7329646ab25Sderaadt 				struct dirTodoNode *n;
7339646ab25Sderaadt 
7342a4cde9cSderaadt 				if (dirent.size) {
7359646ab25Sderaadt 					pwarn("Directory %s has size != 0\n",
7362a4cde9cSderaadt 					      fullpath(&dirent));
7379646ab25Sderaadt 					if (ask(1, "Correct")) {
7389646ab25Sderaadt 						p[28] = p[29] = p[30] = p[31] = 0;
7392a4cde9cSderaadt 						dirent.size = 0;
7409646ab25Sderaadt 						mod |= THISMOD|FSDIRMOD;
7419646ab25Sderaadt 					} else
7429646ab25Sderaadt 						mod |= FSERROR;
7439646ab25Sderaadt 				}
7449646ab25Sderaadt 				/*
7459646ab25Sderaadt 				 * handle `.' and `..' specially
7469646ab25Sderaadt 				 */
7472a4cde9cSderaadt 				if (strcmp(dirent.name, ".") == 0) {
7482a4cde9cSderaadt 					if (dirent.head != dir->head) {
7499646ab25Sderaadt 						pwarn("`.' entry in %s has incorrect start cluster\n",
7502a4cde9cSderaadt 						      fullpath(dir));
7519646ab25Sderaadt 						if (ask(1, "Correct")) {
7522a4cde9cSderaadt 							dirent.head = dir->head;
7532a4cde9cSderaadt 							p[26] = (u_char)dirent.head;
7542a4cde9cSderaadt 							p[27] = (u_char)(dirent.head >> 8);
755b099d67bSprovos 							if (boot->ClustMask == CLUST32_MASK) {
756b099d67bSprovos 								p[20] = (u_char)(dirent.head >> 16);
757b099d67bSprovos 								p[21] = (u_char)(dirent.head >> 24);
758b099d67bSprovos 							}
7599646ab25Sderaadt 							mod |= THISMOD|FSDIRMOD;
7609646ab25Sderaadt 						} else
7619646ab25Sderaadt 							mod |= FSERROR;
7629646ab25Sderaadt 					}
7639646ab25Sderaadt 					continue;
7649646ab25Sderaadt 				}
7652a4cde9cSderaadt 				if (strcmp(dirent.name, "..") == 0) {
766a4df0321Sderaadt 					if (dir->parent) {		/* XXX */
767b099d67bSprovos 						if (!dir->parent->parent) {
768b099d67bSprovos 							if (dirent.head) {
769b099d67bSprovos 								pwarn("`..' entry in %s has non-zero start cluster\n",
770b099d67bSprovos 								      fullpath(dir));
771b099d67bSprovos 								if (ask(1, "Correct")) {
772b099d67bSprovos 									dirent.head = 0;
773b099d67bSprovos 									p[26] = p[27] = 0;
774b099d67bSprovos 									if (boot->ClustMask == CLUST32_MASK)
775b099d67bSprovos 										p[20] = p[21] = 0;
776b099d67bSprovos 									mod |= THISMOD|FSDIRMOD;
777b099d67bSprovos 								} else
778b099d67bSprovos 									mod |= FSERROR;
779b099d67bSprovos 							}
780b099d67bSprovos 						} else if (dirent.head != dir->parent->head) {
7819646ab25Sderaadt 							pwarn("`..' entry in %s has incorrect start cluster\n",
7822a4cde9cSderaadt 							      fullpath(dir));
7839646ab25Sderaadt 							if (ask(1, "Correct")) {
7842a4cde9cSderaadt 								dirent.head = dir->parent->head;
7852a4cde9cSderaadt 								p[26] = (u_char)dirent.head;
7862a4cde9cSderaadt 								p[27] = (u_char)(dirent.head >> 8);
787b099d67bSprovos 								if (boot->ClustMask == CLUST32_MASK) {
788b099d67bSprovos 									p[20] = (u_char)(dirent.head >> 16);
789b099d67bSprovos 									p[21] = (u_char)(dirent.head >> 24);
790b099d67bSprovos 								}
7919646ab25Sderaadt 								mod |= THISMOD|FSDIRMOD;
7929646ab25Sderaadt 							} else
7939646ab25Sderaadt 								mod |= FSERROR;
7949646ab25Sderaadt 						}
795a4df0321Sderaadt 					}
7969646ab25Sderaadt 					continue;
7979646ab25Sderaadt 				}
7989646ab25Sderaadt 
7992a4cde9cSderaadt 				/* create directory tree node */
8002a4cde9cSderaadt 				if (!(d = newDosDirEntry())) {
801dd133929Sthib 					xperror("No space for directory");
802a0dbeb59Smillert 					return (FSFATAL);
8032a4cde9cSderaadt 				}
804a0dbeb59Smillert 				(void)memcpy(d, &dirent, sizeof(struct dosDirEntry));
8052a4cde9cSderaadt 				/* link it into the tree */
8062a4cde9cSderaadt 				dir->child = d;
8072a4cde9cSderaadt 
8089646ab25Sderaadt 				/* Enter this directory into the todo list */
8092a4cde9cSderaadt 				if (!(n = newDirTodo())) {
810dd133929Sthib 					xperror("No space for todo list");
811a0dbeb59Smillert 					return (FSFATAL);
8122a4cde9cSderaadt 				}
8139646ab25Sderaadt 				n->next = pendingDirectories;
8149646ab25Sderaadt 				n->dir = d;
8159646ab25Sderaadt 				pendingDirectories = n;
8169646ab25Sderaadt 			} else {
8172a4cde9cSderaadt 				mod |= k = checksize(boot, fat, p, &dirent);
8189646ab25Sderaadt 				if (k & FSDIRMOD)
8199646ab25Sderaadt 					mod |= THISMOD;
8209646ab25Sderaadt 			}
8216dcefe1eSderaadt 			boot->NumFiles++;
8229646ab25Sderaadt 		}
8239646ab25Sderaadt 		if (mod & THISMOD) {
8249646ab25Sderaadt 			last *= 32;
8259646ab25Sderaadt 			if (lseek(f, off, SEEK_SET) != off
8269646ab25Sderaadt 			    || write(f, buffer, last) != last) {
827dd133929Sthib 				xperror("Unable to write directory");
828a0dbeb59Smillert 				return (FSFATAL);
8299646ab25Sderaadt 			}
8309646ab25Sderaadt 			mod &= ~THISMOD;
8319646ab25Sderaadt 		}
8329646ab25Sderaadt 	} while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters);
8339646ab25Sderaadt 	if (invlfn || vallfn)
8349646ab25Sderaadt 		mod |= removede(f, boot, fat,
8359646ab25Sderaadt 				invlfn ? invlfn : vallfn, p,
8369646ab25Sderaadt 				invlfn ? invcl : valcl, -1, 0,
8372a4cde9cSderaadt 				fullpath(dir), 1);
838a0dbeb59Smillert 	return (mod & ~THISMOD);
8399646ab25Sderaadt }
8409646ab25Sderaadt 
8412a4cde9cSderaadt int
handleDirTree(int dosfs,struct bootblock * boot,struct fatEntry * fat)8424e95fccfSderaadt handleDirTree(int dosfs, struct bootblock *boot, struct fatEntry *fat)
8432a4cde9cSderaadt {
8442a4cde9cSderaadt 	int mod;
8452a4cde9cSderaadt 
8462a4cde9cSderaadt 	mod = readDosDirSection(dosfs, boot, fat, rootDir);
8472a4cde9cSderaadt 	if (mod & FSFATAL)
848a0dbeb59Smillert 		return (FSFATAL);
8492a4cde9cSderaadt 
8502a4cde9cSderaadt 	if (mod & FSFATMOD) {
8512a4cde9cSderaadt 		mod &= ~FSFATMOD;
8522a4cde9cSderaadt 		mod |= writefat(dosfs, boot, fat); /* delay writing fats?	XXX */
8532a4cde9cSderaadt 	}
8542a4cde9cSderaadt 
8552a4cde9cSderaadt 	if (mod & FSFATAL)
856a0dbeb59Smillert 		return (FSFATAL);
8572a4cde9cSderaadt 
8582a4cde9cSderaadt 	/*
8592a4cde9cSderaadt 	 * process the directory todo list
8602a4cde9cSderaadt 	 */
8612a4cde9cSderaadt 	while (pendingDirectories) {
8622a4cde9cSderaadt 		struct dosDirEntry *dir = pendingDirectories->dir;
8632a4cde9cSderaadt 		struct dirTodoNode *n = pendingDirectories->next;
8642a4cde9cSderaadt 
8652a4cde9cSderaadt 		/*
8662a4cde9cSderaadt 		 * remove TODO entry now, the list might change during
8672a4cde9cSderaadt 		 * directory reads
8682a4cde9cSderaadt 		 */
8692a4cde9cSderaadt 		freeDirTodo(pendingDirectories);
8702a4cde9cSderaadt 		pendingDirectories = n;
8712a4cde9cSderaadt 
8722a4cde9cSderaadt 		/*
8732a4cde9cSderaadt 		 * handle subdirectory
8742a4cde9cSderaadt 		 */
8752a4cde9cSderaadt 		mod |= readDosDirSection(dosfs, boot, fat, dir);
8762a4cde9cSderaadt 		if (mod & FSFATAL)
877a0dbeb59Smillert 			return (FSFATAL);
8782a4cde9cSderaadt 		if (mod & FSFATMOD) {
8792a4cde9cSderaadt 			mod &= ~FSFATMOD;
8802a4cde9cSderaadt 			mod |= writefat(dosfs, boot, fat); /* delay writing fats? XXX */
8812a4cde9cSderaadt 		}
8822a4cde9cSderaadt 		if (mod & FSFATAL)
883a0dbeb59Smillert 			return (FSFATAL);
8842a4cde9cSderaadt 	}
885a0dbeb59Smillert 	return (mod);
8862a4cde9cSderaadt }
8872a4cde9cSderaadt 
8889646ab25Sderaadt /*
8899646ab25Sderaadt  * Try to reconnect a FAT chain into dir
8909646ab25Sderaadt  */
8919646ab25Sderaadt static u_char *lfbuf;
8929646ab25Sderaadt static cl_t lfcl;
8939646ab25Sderaadt static off_t lfoff;
8949646ab25Sderaadt 
8959646ab25Sderaadt int
reconnect(int dosfs,struct bootblock * boot,struct fatEntry * fat,cl_t head)8964e95fccfSderaadt reconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t head)
8979646ab25Sderaadt {
8989646ab25Sderaadt 	struct dosDirEntry d;
8999646ab25Sderaadt 	u_char *p;
9009646ab25Sderaadt 
90160db4759Sespie 	if (!ask(1, "Reconnect"))
902b099d67bSprovos 		return FSERROR;
903b099d67bSprovos 
9042a4cde9cSderaadt 	if (!lostDir) {
9052a4cde9cSderaadt 		for (lostDir = rootDir->child; lostDir; lostDir = lostDir->next) {
9062a4cde9cSderaadt 			if (!strcmp(lostDir->name, LOSTDIR))
9072a4cde9cSderaadt 				break;
9082a4cde9cSderaadt 		}
9092a4cde9cSderaadt 		if (!lostDir) {		/* Create LOSTDIR?		XXX */
9102a4cde9cSderaadt 			pwarn("No %s directory\n", LOSTDIR);
911a0dbeb59Smillert 			return (FSERROR);
9122a4cde9cSderaadt 		}
9132a4cde9cSderaadt 	}
9149646ab25Sderaadt 	if (!lfbuf) {
9159646ab25Sderaadt 		lfbuf = malloc(boot->ClusterSize);
9169646ab25Sderaadt 		if (!lfbuf) {
917dd133929Sthib 			xperror("No space for buffer");
918a0dbeb59Smillert 			return (FSFATAL);
9199646ab25Sderaadt 		}
9209646ab25Sderaadt 		p = NULL;
9219646ab25Sderaadt 	} else
9229646ab25Sderaadt 		p = lfbuf;
9239646ab25Sderaadt 	while (1) {
9249646ab25Sderaadt 		if (p)
9256dcefe1eSderaadt 			for (; p < lfbuf + boot->ClusterSize; p += 32)
9269646ab25Sderaadt 				if (*p == SLOT_EMPTY
9279646ab25Sderaadt 				    || *p == SLOT_DELETED)
9289646ab25Sderaadt 					break;
9299646ab25Sderaadt 		if (p && p < lfbuf + boot->ClusterSize)
9309646ab25Sderaadt 			break;
9312a4cde9cSderaadt 		lfcl = p ? fat[lfcl].next : lostDir->head;
9329646ab25Sderaadt 		if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) {
9332a4cde9cSderaadt 			/* Extend LOSTDIR?				XXX */
9349646ab25Sderaadt 			pwarn("No space in %s\n", LOSTDIR);
935a0dbeb59Smillert 			return (FSERROR);
9369646ab25Sderaadt 		}
9379646ab25Sderaadt 		lfoff = lfcl * boot->ClusterSize
9389646ab25Sderaadt 		    + boot->ClusterOffset * boot->BytesPerSec;
9399646ab25Sderaadt 		if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
9406dcefe1eSderaadt 		    || read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
941dd133929Sthib 			xperror("could not read LOST.DIR");
942a0dbeb59Smillert 			return (FSFATAL);
9439646ab25Sderaadt 		}
9449646ab25Sderaadt 		p = lfbuf;
9459646ab25Sderaadt 	}
9469646ab25Sderaadt 
9479646ab25Sderaadt 	boot->NumFiles++;
9489646ab25Sderaadt 	/* Ensure uniqueness of entry here!				XXX */
949a0dbeb59Smillert 	(void)memset(&d, 0, sizeof d);
9503a4077acSderaadt 	snprintf(d.name, sizeof d.name, "%u", head);
9519646ab25Sderaadt 	d.flags = 0;
9529646ab25Sderaadt 	d.head = head;
9539646ab25Sderaadt 	d.size = fat[head].length * boot->ClusterSize;
9549646ab25Sderaadt 
955a0dbeb59Smillert 	(void)memset(p, 0, 32);
956a0dbeb59Smillert 	(void)memset(p, ' ', 11);
957a0dbeb59Smillert 	(void)memcpy(p, d.name, strlen(d.name));
9582a4cde9cSderaadt 	p[26] = (u_char)d.head;
9592a4cde9cSderaadt 	p[27] = (u_char)(d.head >> 8);
960b099d67bSprovos 	if (boot->ClustMask == CLUST32_MASK) {
961b099d67bSprovos 		p[20] = (u_char)(d.head >> 16);
962b099d67bSprovos 		p[21] = (u_char)(d.head >> 24);
963b099d67bSprovos 	}
9642a4cde9cSderaadt 	p[28] = (u_char)d.size;
9652a4cde9cSderaadt 	p[29] = (u_char)(d.size >> 8);
9662a4cde9cSderaadt 	p[30] = (u_char)(d.size >> 16);
9672a4cde9cSderaadt 	p[31] = (u_char)(d.size >> 24);
9682a4cde9cSderaadt 	fat[head].flags |= FAT_USED;
9699646ab25Sderaadt 	if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
9706dcefe1eSderaadt 	    || write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
971dd133929Sthib 		xperror("could not write LOST.DIR");
972a0dbeb59Smillert 		return (FSFATAL);
9739646ab25Sderaadt 	}
974a0dbeb59Smillert 	return (FSDIRMOD);
9759646ab25Sderaadt }
9769646ab25Sderaadt 
9779646ab25Sderaadt void
finishlf(void)9784e95fccfSderaadt finishlf(void)
9799646ab25Sderaadt {
9809646ab25Sderaadt 	free(lfbuf);
9819646ab25Sderaadt 	lfbuf = NULL;
9829646ab25Sderaadt }
983