xref: /freebsd-src/sbin/fsck_msdosfs/dir.c (revision deeb1d34024b3f843d965cdf204c745165c9bac0)
11de7b4b8SPedro F. Giffuni /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
49708ba9fSXin LI  * Copyright (c) 2019 Google LLC
50121b42aSDavid E. O'Brien  * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
60121b42aSDavid E. O'Brien  * Copyright (c) 1995 Martin Husemann
70121b42aSDavid E. O'Brien  * Some structure declaration borrowed from Paul Popelka
80121b42aSDavid E. O'Brien  * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference.
90121b42aSDavid E. O'Brien  *
100121b42aSDavid E. O'Brien  * Redistribution and use in source and binary forms, with or without
110121b42aSDavid E. O'Brien  * modification, are permitted provided that the following conditions
120121b42aSDavid E. O'Brien  * are met:
130121b42aSDavid E. O'Brien  * 1. Redistributions of source code must retain the above copyright
140121b42aSDavid E. O'Brien  *    notice, this list of conditions and the following disclaimer.
150121b42aSDavid E. O'Brien  * 2. Redistributions in binary form must reproduce the above copyright
160121b42aSDavid E. O'Brien  *    notice, this list of conditions and the following disclaimer in the
170121b42aSDavid E. O'Brien  *    documentation and/or other materials provided with the distribution.
180121b42aSDavid E. O'Brien  *
190121b42aSDavid E. O'Brien  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
200121b42aSDavid E. O'Brien  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
210121b42aSDavid E. O'Brien  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
220121b42aSDavid E. O'Brien  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
230121b42aSDavid E. O'Brien  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
240121b42aSDavid E. O'Brien  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
250121b42aSDavid E. O'Brien  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
260121b42aSDavid E. O'Brien  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
270121b42aSDavid E. O'Brien  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
280121b42aSDavid E. O'Brien  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
290121b42aSDavid E. O'Brien  */
300121b42aSDavid E. O'Brien 
310121b42aSDavid E. O'Brien 
320121b42aSDavid E. O'Brien #include <sys/cdefs.h>
330121b42aSDavid E. O'Brien #ifndef lint
346cf357bcSUlrich Spörlein __RCSID("$NetBSD: dir.c,v 1.20 2006/06/05 16:51:18 christos Exp $");
350121b42aSDavid E. O'Brien #endif /* not lint */
360121b42aSDavid E. O'Brien 
37f806eb62SXin LI #include <assert.h>
389ace3717SXin LI #include <inttypes.h>
390121b42aSDavid E. O'Brien #include <stdio.h>
400121b42aSDavid E. O'Brien #include <stdlib.h>
410121b42aSDavid E. O'Brien #include <string.h>
420121b42aSDavid E. O'Brien #include <ctype.h>
430121b42aSDavid E. O'Brien #include <unistd.h>
440121b42aSDavid E. O'Brien #include <time.h>
450121b42aSDavid E. O'Brien 
460121b42aSDavid E. O'Brien #include <sys/param.h>
470121b42aSDavid E. O'Brien 
480121b42aSDavid E. O'Brien #include "ext.h"
490121b42aSDavid E. O'Brien #include "fsutil.h"
500121b42aSDavid E. O'Brien 
510121b42aSDavid E. O'Brien #define	SLOT_EMPTY	0x00		/* slot has never been used */
520121b42aSDavid E. O'Brien #define	SLOT_E5		0x05		/* the real value is 0xe5 */
530121b42aSDavid E. O'Brien #define	SLOT_DELETED	0xe5		/* file in this slot deleted */
540121b42aSDavid E. O'Brien 
550121b42aSDavid E. O'Brien #define	ATTR_NORMAL	0x00		/* normal file */
560121b42aSDavid E. O'Brien #define	ATTR_READONLY	0x01		/* file is readonly */
570121b42aSDavid E. O'Brien #define	ATTR_HIDDEN	0x02		/* file is hidden */
580121b42aSDavid E. O'Brien #define	ATTR_SYSTEM	0x04		/* file is a system file */
590121b42aSDavid E. O'Brien #define	ATTR_VOLUME	0x08		/* entry is a volume label */
600121b42aSDavid E. O'Brien #define	ATTR_DIRECTORY	0x10		/* entry is a directory name */
610121b42aSDavid E. O'Brien #define	ATTR_ARCHIVE	0x20		/* file is new or modified */
620121b42aSDavid E. O'Brien 
630121b42aSDavid E. O'Brien #define	ATTR_WIN95	0x0f		/* long name record */
640121b42aSDavid E. O'Brien 
650121b42aSDavid E. O'Brien /*
660121b42aSDavid E. O'Brien  * This is the format of the contents of the deTime field in the direntry
670121b42aSDavid E. O'Brien  * structure.
680121b42aSDavid E. O'Brien  * We don't use bitfields because we don't know how compilers for
690121b42aSDavid E. O'Brien  * arbitrary machines will lay them out.
700121b42aSDavid E. O'Brien  */
710121b42aSDavid E. O'Brien #define DT_2SECONDS_MASK	0x1F	/* seconds divided by 2 */
720121b42aSDavid E. O'Brien #define DT_2SECONDS_SHIFT	0
730121b42aSDavid E. O'Brien #define DT_MINUTES_MASK		0x7E0	/* minutes */
740121b42aSDavid E. O'Brien #define DT_MINUTES_SHIFT	5
750121b42aSDavid E. O'Brien #define DT_HOURS_MASK		0xF800	/* hours */
760121b42aSDavid E. O'Brien #define DT_HOURS_SHIFT		11
770121b42aSDavid E. O'Brien 
780121b42aSDavid E. O'Brien /*
790121b42aSDavid E. O'Brien  * This is the format of the contents of the deDate field in the direntry
800121b42aSDavid E. O'Brien  * structure.
810121b42aSDavid E. O'Brien  */
820121b42aSDavid E. O'Brien #define DD_DAY_MASK		0x1F	/* day of month */
830121b42aSDavid E. O'Brien #define DD_DAY_SHIFT		0
840121b42aSDavid E. O'Brien #define DD_MONTH_MASK		0x1E0	/* month */
850121b42aSDavid E. O'Brien #define DD_MONTH_SHIFT		5
860121b42aSDavid E. O'Brien #define DD_YEAR_MASK		0xFE00	/* year - 1980 */
870121b42aSDavid E. O'Brien #define DD_YEAR_SHIFT		9
880121b42aSDavid E. O'Brien 
890121b42aSDavid E. O'Brien 
900121b42aSDavid E. O'Brien /* dir.c */
91b70cd7eeSWarner Losh static struct dosDirEntry *newDosDirEntry(void);
92b70cd7eeSWarner Losh static void freeDosDirEntry(struct dosDirEntry *);
93b70cd7eeSWarner Losh static struct dirTodoNode *newDirTodo(void);
94b70cd7eeSWarner Losh static void freeDirTodo(struct dirTodoNode *);
95b70cd7eeSWarner Losh static char *fullpath(struct dosDirEntry *);
96b70cd7eeSWarner Losh static u_char calcShortSum(u_char *);
979708ba9fSXin LI static int delete(struct fat_descriptor *, cl_t, int, cl_t, int, int);
989708ba9fSXin LI static int removede(struct fat_descriptor *, u_char *, u_char *,
999708ba9fSXin LI     cl_t, cl_t, cl_t, char *, int);
1009708ba9fSXin LI static int checksize(struct fat_descriptor *, u_char *, struct dosDirEntry *);
1019708ba9fSXin LI static int readDosDirSection(struct fat_descriptor *, struct dosDirEntry *);
1020121b42aSDavid E. O'Brien 
1030121b42aSDavid E. O'Brien /*
1040121b42aSDavid E. O'Brien  * Manage free dosDirEntry structures.
1050121b42aSDavid E. O'Brien  */
1060121b42aSDavid E. O'Brien static struct dosDirEntry *freede;
1070121b42aSDavid E. O'Brien 
1080121b42aSDavid E. O'Brien static struct dosDirEntry *
newDosDirEntry(void)109b70cd7eeSWarner Losh newDosDirEntry(void)
1100121b42aSDavid E. O'Brien {
1110121b42aSDavid E. O'Brien 	struct dosDirEntry *de;
1120121b42aSDavid E. O'Brien 
1130121b42aSDavid E. O'Brien 	if (!(de = freede)) {
114dad02d7dSXin LI 		if (!(de = malloc(sizeof *de)))
1159708ba9fSXin LI 			return (NULL);
1160121b42aSDavid E. O'Brien 	} else
1170121b42aSDavid E. O'Brien 		freede = de->next;
1180121b42aSDavid E. O'Brien 	return de;
1190121b42aSDavid E. O'Brien }
1200121b42aSDavid E. O'Brien 
1210121b42aSDavid E. O'Brien static void
freeDosDirEntry(struct dosDirEntry * de)122b70cd7eeSWarner Losh freeDosDirEntry(struct dosDirEntry *de)
1230121b42aSDavid E. O'Brien {
1240121b42aSDavid E. O'Brien 	de->next = freede;
1250121b42aSDavid E. O'Brien 	freede = de;
1260121b42aSDavid E. O'Brien }
1270121b42aSDavid E. O'Brien 
1280121b42aSDavid E. O'Brien /*
1290121b42aSDavid E. O'Brien  * The same for dirTodoNode structures.
1300121b42aSDavid E. O'Brien  */
1310121b42aSDavid E. O'Brien static struct dirTodoNode *freedt;
1320121b42aSDavid E. O'Brien 
1330121b42aSDavid E. O'Brien static struct dirTodoNode *
newDirTodo(void)134b70cd7eeSWarner Losh newDirTodo(void)
1350121b42aSDavid E. O'Brien {
1360121b42aSDavid E. O'Brien 	struct dirTodoNode *dt;
1370121b42aSDavid E. O'Brien 
1380121b42aSDavid E. O'Brien 	if (!(dt = freedt)) {
139dad02d7dSXin LI 		if (!(dt = malloc(sizeof *dt)))
1400121b42aSDavid E. O'Brien 			return 0;
1410121b42aSDavid E. O'Brien 	} else
1420121b42aSDavid E. O'Brien 		freedt = dt->next;
1430121b42aSDavid E. O'Brien 	return dt;
1440121b42aSDavid E. O'Brien }
1450121b42aSDavid E. O'Brien 
1460121b42aSDavid E. O'Brien static void
freeDirTodo(struct dirTodoNode * dt)147b70cd7eeSWarner Losh freeDirTodo(struct dirTodoNode *dt)
1480121b42aSDavid E. O'Brien {
1490121b42aSDavid E. O'Brien 	dt->next = freedt;
1500121b42aSDavid E. O'Brien 	freedt = dt;
1510121b42aSDavid E. O'Brien }
1520121b42aSDavid E. O'Brien 
1530121b42aSDavid E. O'Brien /*
1540121b42aSDavid E. O'Brien  * The stack of unread directories
1550121b42aSDavid E. O'Brien  */
1561efe3c6bSEd Schouten static struct dirTodoNode *pendingDirectories = NULL;
1570121b42aSDavid E. O'Brien 
1580121b42aSDavid E. O'Brien /*
1590121b42aSDavid E. O'Brien  * Return the full pathname for a directory entry.
1600121b42aSDavid E. O'Brien  */
1610121b42aSDavid E. O'Brien static char *
fullpath(struct dosDirEntry * dir)162b70cd7eeSWarner Losh fullpath(struct dosDirEntry *dir)
1630121b42aSDavid E. O'Brien {
1640121b42aSDavid E. O'Brien 	static char namebuf[MAXPATHLEN + 1];
1650121b42aSDavid E. O'Brien 	char *cp, *np;
1660121b42aSDavid E. O'Brien 	int nl;
1670121b42aSDavid E. O'Brien 
16807282103SXin LI 	cp = namebuf + sizeof namebuf;
16907282103SXin LI 	*--cp = '\0';
17007282103SXin LI 
17107282103SXin LI 	for(;;) {
1720121b42aSDavid E. O'Brien 		np = dir->lname[0] ? dir->lname : dir->name;
1730121b42aSDavid E. O'Brien 		nl = strlen(np);
17407282103SXin LI 		if (cp <= namebuf + 1 + nl) {
1750121b42aSDavid E. O'Brien 			*--cp = '?';
17607282103SXin LI 			break;
17707282103SXin LI 		}
17807282103SXin LI 		cp -= nl;
17907282103SXin LI 		memcpy(cp, np, nl);
18007282103SXin LI 		dir = dir->parent;
18107282103SXin LI 		if (!dir)
18207282103SXin LI 			break;
18307282103SXin LI 		*--cp = '/';
18407282103SXin LI 	}
18507282103SXin LI 
1860121b42aSDavid E. O'Brien 	return cp;
1870121b42aSDavid E. O'Brien }
1880121b42aSDavid E. O'Brien 
1890121b42aSDavid E. O'Brien /*
1900121b42aSDavid E. O'Brien  * Calculate a checksum over an 8.3 alias name
1910121b42aSDavid E. O'Brien  */
1929708ba9fSXin LI static inline u_char
calcShortSum(u_char * p)193b70cd7eeSWarner Losh calcShortSum(u_char *p)
1940121b42aSDavid E. O'Brien {
1950121b42aSDavid E. O'Brien 	u_char sum = 0;
1960121b42aSDavid E. O'Brien 	int i;
1970121b42aSDavid E. O'Brien 
1980121b42aSDavid E. O'Brien 	for (i = 0; i < 11; i++) {
1990121b42aSDavid E. O'Brien 		sum = (sum << 7)|(sum >> 1);	/* rotate right */
2000121b42aSDavid E. O'Brien 		sum += p[i];
2010121b42aSDavid E. O'Brien 	}
2020121b42aSDavid E. O'Brien 
2030121b42aSDavid E. O'Brien 	return sum;
2040121b42aSDavid E. O'Brien }
2050121b42aSDavid E. O'Brien 
2060121b42aSDavid E. O'Brien /*
2070121b42aSDavid E. O'Brien  * Global variables temporarily used during a directory scan
2080121b42aSDavid E. O'Brien  */
2090121b42aSDavid E. O'Brien static char longName[DOSLONGNAMELEN] = "";
2100121b42aSDavid E. O'Brien static u_char *buffer = NULL;
2110121b42aSDavid E. O'Brien static u_char *delbuf = NULL;
2120121b42aSDavid E. O'Brien 
2132b9fea6cSPedro F. Giffuni static struct dosDirEntry *rootDir;
2140121b42aSDavid E. O'Brien static struct dosDirEntry *lostDir;
2150121b42aSDavid E. O'Brien 
2160121b42aSDavid E. O'Brien /*
2170121b42aSDavid E. O'Brien  * Init internal state for a new directory scan.
2180121b42aSDavid E. O'Brien  */
2190121b42aSDavid E. O'Brien int
resetDosDirSection(struct fat_descriptor * fat)2209708ba9fSXin LI resetDosDirSection(struct fat_descriptor *fat)
2210121b42aSDavid E. O'Brien {
2229708ba9fSXin LI 	int rootdir_size, cluster_size;
2230121b42aSDavid E. O'Brien 	int ret = FSOK;
2246cf357bcSUlrich Spörlein 	size_t len;
2259708ba9fSXin LI 	struct bootblock *boot;
2260121b42aSDavid E. O'Brien 
2279708ba9fSXin LI 	boot = fat_get_boot(fat);
2280121b42aSDavid E. O'Brien 
2299708ba9fSXin LI 	rootdir_size = boot->bpbRootDirEnts * 32;
2309708ba9fSXin LI 	cluster_size = boot->bpbSecPerClust * boot->bpbBytesPerSec;
2319708ba9fSXin LI 
2329708ba9fSXin LI 	if ((buffer = malloc(len = MAX(rootdir_size, cluster_size))) == NULL) {
2336cf357bcSUlrich Spörlein 		perr("No space for directory buffer (%zu)", len);
2340121b42aSDavid E. O'Brien 		return FSFATAL;
2350121b42aSDavid E. O'Brien 	}
2366069db97SKonstantin Belousov 
2379708ba9fSXin LI 	if ((delbuf = malloc(len = cluster_size)) == NULL) {
2386069db97SKonstantin Belousov 		free(buffer);
2396cf357bcSUlrich Spörlein 		perr("No space for directory delbuf (%zu)", len);
2406069db97SKonstantin Belousov 		return FSFATAL;
2416069db97SKonstantin Belousov 	}
2426069db97SKonstantin Belousov 
2436069db97SKonstantin Belousov 	if ((rootDir = newDosDirEntry()) == NULL) {
2446069db97SKonstantin Belousov 		free(buffer);
2456069db97SKonstantin Belousov 		free(delbuf);
2466cf357bcSUlrich Spörlein 		perr("No space for directory entry");
2476069db97SKonstantin Belousov 		return FSFATAL;
2486069db97SKonstantin Belousov 	}
2496069db97SKonstantin Belousov 
2500121b42aSDavid E. O'Brien 	memset(rootDir, 0, sizeof *rootDir);
2510121b42aSDavid E. O'Brien 	if (boot->flags & FAT32) {
2529708ba9fSXin LI 		if (!fat_is_cl_head(fat, boot->bpbRootClust)) {
2530121b42aSDavid E. O'Brien 			pfatal("Root directory doesn't start a cluster chain");
2540121b42aSDavid E. O'Brien 			return FSFATAL;
2550121b42aSDavid E. O'Brien 		}
25675fb5353SKonstantin Belousov 		rootDir->head = boot->bpbRootClust;
2570121b42aSDavid E. O'Brien 	}
2580121b42aSDavid E. O'Brien 
2590121b42aSDavid E. O'Brien 	return ret;
2600121b42aSDavid E. O'Brien }
2610121b42aSDavid E. O'Brien 
2620121b42aSDavid E. O'Brien /*
2630121b42aSDavid E. O'Brien  * Cleanup after a directory scan
2640121b42aSDavid E. O'Brien  */
2650121b42aSDavid E. O'Brien void
finishDosDirSection(void)266b70cd7eeSWarner Losh finishDosDirSection(void)
2670121b42aSDavid E. O'Brien {
2680121b42aSDavid E. O'Brien 	struct dirTodoNode *p, *np;
2690121b42aSDavid E. O'Brien 	struct dosDirEntry *d, *nd;
2700121b42aSDavid E. O'Brien 
2710121b42aSDavid E. O'Brien 	for (p = pendingDirectories; p; p = np) {
2720121b42aSDavid E. O'Brien 		np = p->next;
2730121b42aSDavid E. O'Brien 		freeDirTodo(p);
2740121b42aSDavid E. O'Brien 	}
275f3858adaSPedro F. Giffuni 	pendingDirectories = NULL;
2760121b42aSDavid E. O'Brien 	for (d = rootDir; d; d = nd) {
2770121b42aSDavid E. O'Brien 		if ((nd = d->child) != NULL) {
2780121b42aSDavid E. O'Brien 			d->child = 0;
2790121b42aSDavid E. O'Brien 			continue;
2800121b42aSDavid E. O'Brien 		}
2810121b42aSDavid E. O'Brien 		if (!(nd = d->next))
2820121b42aSDavid E. O'Brien 			nd = d->parent;
2830121b42aSDavid E. O'Brien 		freeDosDirEntry(d);
2840121b42aSDavid E. O'Brien 	}
2850121b42aSDavid E. O'Brien 	rootDir = lostDir = NULL;
2860121b42aSDavid E. O'Brien 	free(buffer);
2870121b42aSDavid E. O'Brien 	free(delbuf);
2880121b42aSDavid E. O'Brien 	buffer = NULL;
2890121b42aSDavid E. O'Brien 	delbuf = NULL;
2900121b42aSDavid E. O'Brien }
2910121b42aSDavid E. O'Brien 
2920121b42aSDavid E. O'Brien /*
2930121b42aSDavid E. O'Brien  * Delete directory entries between startcl, startoff and endcl, endoff.
2940121b42aSDavid E. O'Brien  */
2950121b42aSDavid E. O'Brien static int
delete(struct fat_descriptor * fat,cl_t startcl,int startoff,cl_t endcl,int endoff,int notlast)2969708ba9fSXin LI delete(struct fat_descriptor *fat, cl_t startcl,
297b70cd7eeSWarner Losh     int startoff, cl_t endcl, int endoff, int notlast)
2980121b42aSDavid E. O'Brien {
2990121b42aSDavid E. O'Brien 	u_char *s, *e;
3000121b42aSDavid E. O'Brien 	off_t off;
3019708ba9fSXin LI 	int clsz, fd;
3029708ba9fSXin LI 	struct bootblock *boot;
3039708ba9fSXin LI 
3049708ba9fSXin LI 	boot = fat_get_boot(fat);
3059708ba9fSXin LI 	fd = fat_get_fd(fat);
3069708ba9fSXin LI 	clsz = boot->bpbSecPerClust * boot->bpbBytesPerSec;
3070121b42aSDavid E. O'Brien 
3080121b42aSDavid E. O'Brien 	s = delbuf + startoff;
3090121b42aSDavid E. O'Brien 	e = delbuf + clsz;
3109708ba9fSXin LI 	while (fat_is_valid_cl(fat, startcl)) {
3110121b42aSDavid E. O'Brien 		if (startcl == endcl) {
3120121b42aSDavid E. O'Brien 			if (notlast)
3130121b42aSDavid E. O'Brien 				break;
3140121b42aSDavid E. O'Brien 			e = delbuf + endoff;
3150121b42aSDavid E. O'Brien 		}
316eb1c42c1SXin LI 		off = (startcl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster;
317eb1c42c1SXin LI 
31875fb5353SKonstantin Belousov 		off *= boot->bpbBytesPerSec;
3199708ba9fSXin LI 		if (lseek(fd, off, SEEK_SET) != off) {
3209ace3717SXin LI 			perr("Unable to lseek to %" PRId64, off);
3219ace3717SXin LI 			return FSFATAL;
3229ace3717SXin LI 		}
3239708ba9fSXin LI 		if (read(fd, delbuf, clsz) != clsz) {
3246cf357bcSUlrich Spörlein 			perr("Unable to read directory");
3250121b42aSDavid E. O'Brien 			return FSFATAL;
3260121b42aSDavid E. O'Brien 		}
3270121b42aSDavid E. O'Brien 		while (s < e) {
3280121b42aSDavid E. O'Brien 			*s = SLOT_DELETED;
3290121b42aSDavid E. O'Brien 			s += 32;
3300121b42aSDavid E. O'Brien 		}
3319708ba9fSXin LI 		if (lseek(fd, off, SEEK_SET) != off) {
3329ace3717SXin LI 			perr("Unable to lseek to %" PRId64, off);
3339ace3717SXin LI 			return FSFATAL;
3349ace3717SXin LI 		}
3359708ba9fSXin LI 		if (write(fd, delbuf, clsz) != clsz) {
3366cf357bcSUlrich Spörlein 			perr("Unable to write directory");
3370121b42aSDavid E. O'Brien 			return FSFATAL;
3380121b42aSDavid E. O'Brien 		}
3390121b42aSDavid E. O'Brien 		if (startcl == endcl)
3400121b42aSDavid E. O'Brien 			break;
3419708ba9fSXin LI 		startcl = fat_get_cl_next(fat, startcl);
3420121b42aSDavid E. O'Brien 		s = delbuf;
3430121b42aSDavid E. O'Brien 	}
3440121b42aSDavid E. O'Brien 	return FSOK;
3450121b42aSDavid E. O'Brien }
3460121b42aSDavid E. O'Brien 
3470121b42aSDavid E. O'Brien static int
removede(struct fat_descriptor * fat,u_char * start,u_char * end,cl_t startcl,cl_t endcl,cl_t curcl,char * path,int type)3489708ba9fSXin LI removede(struct fat_descriptor *fat, u_char *start,
3499708ba9fSXin LI     u_char *end, cl_t startcl, cl_t endcl, cl_t curcl,
3509708ba9fSXin LI     char *path, int type)
3510121b42aSDavid E. O'Brien {
3520121b42aSDavid E. O'Brien 	switch (type) {
3530121b42aSDavid E. O'Brien 	case 0:
3540121b42aSDavid E. O'Brien 		pwarn("Invalid long filename entry for %s\n", path);
3550121b42aSDavid E. O'Brien 		break;
3560121b42aSDavid E. O'Brien 	case 1:
35748790afaSBrian Somers 		pwarn("Invalid long filename entry at end of directory %s\n",
35848790afaSBrian Somers 		    path);
3590121b42aSDavid E. O'Brien 		break;
3600121b42aSDavid E. O'Brien 	case 2:
3610121b42aSDavid E. O'Brien 		pwarn("Invalid long filename entry for volume label\n");
3620121b42aSDavid E. O'Brien 		break;
3630121b42aSDavid E. O'Brien 	}
3640121b42aSDavid E. O'Brien 	if (ask(0, "Remove")) {
3650121b42aSDavid E. O'Brien 		if (startcl != curcl) {
3669708ba9fSXin LI 			if (delete(fat,
3670121b42aSDavid E. O'Brien 				   startcl, start - buffer,
3680121b42aSDavid E. O'Brien 				   endcl, end - buffer,
3690121b42aSDavid E. O'Brien 				   endcl == curcl) == FSFATAL)
3700121b42aSDavid E. O'Brien 				return FSFATAL;
3710121b42aSDavid E. O'Brien 			start = buffer;
3720121b42aSDavid E. O'Brien 		}
3739708ba9fSXin LI 		/* startcl is < CLUST_FIRST for !FAT32 root */
3746069db97SKonstantin Belousov 		if ((endcl == curcl) || (startcl < CLUST_FIRST))
3750121b42aSDavid E. O'Brien 			for (; start < end; start += 32)
3760121b42aSDavid E. O'Brien 				*start = SLOT_DELETED;
3770121b42aSDavid E. O'Brien 		return FSDIRMOD;
3780121b42aSDavid E. O'Brien 	}
3790121b42aSDavid E. O'Brien 	return FSERROR;
3800121b42aSDavid E. O'Brien }
3810121b42aSDavid E. O'Brien 
3820121b42aSDavid E. O'Brien /*
3830121b42aSDavid E. O'Brien  * Check an in-memory file entry
3840121b42aSDavid E. O'Brien  */
3850121b42aSDavid E. O'Brien static int
checksize(struct fat_descriptor * fat,u_char * p,struct dosDirEntry * dir)3869708ba9fSXin LI checksize(struct fat_descriptor *fat, u_char *p, struct dosDirEntry *dir)
3870121b42aSDavid E. O'Brien {
3889708ba9fSXin LI 	int ret = FSOK;
3894d2c300bSXin LI 	size_t chainsize;
3904d2c300bSXin LI 	u_int64_t physicalSize;
3919708ba9fSXin LI 	struct bootblock *boot;
3929708ba9fSXin LI 
3939708ba9fSXin LI 	boot = fat_get_boot(fat);
3949708ba9fSXin LI 
3950121b42aSDavid E. O'Brien 	/*
3960121b42aSDavid E. O'Brien 	 * Check size on ordinary files
3970121b42aSDavid E. O'Brien 	 */
3989708ba9fSXin LI 	if (dir->head == CLUST_FREE) {
3990121b42aSDavid E. O'Brien 		physicalSize = 0;
4009708ba9fSXin LI 	} else {
401890cae19SXin LI 		if (!fat_is_valid_cl(fat, dir->head) || !fat_is_cl_head(fat, dir->head)) {
402890cae19SXin LI 			pwarn("Directory entry %s of size %u referencing invalid cluster %u\n",
403890cae19SXin LI 			    fullpath(dir), dir->size, dir->head);
404890cae19SXin LI 			if (ask(1, "Truncate")) {
405890cae19SXin LI 				p[28] = p[29] = p[30] = p[31] = 0;
406890cae19SXin LI 				p[26] = p[27] = 0;
407890cae19SXin LI 				if (boot->ClustMask == CLUST32_MASK)
408890cae19SXin LI 					p[20] = p[21] = 0;
409890cae19SXin LI 				dir->size = 0;
410890cae19SXin LI 				dir->head = CLUST_FREE;
411890cae19SXin LI 				return FSDIRMOD;
412890cae19SXin LI 			} else {
4130121b42aSDavid E. O'Brien 				return FSERROR;
414890cae19SXin LI 			}
415890cae19SXin LI 		}
4164d2c300bSXin LI 		ret = checkchain(fat, dir->head, &chainsize);
4179708ba9fSXin LI 		/*
4184d2c300bSXin LI 		 * Upon return, chainsize would hold the chain length
4199708ba9fSXin LI 		 * that checkchain() was able to validate, but if the user
4209708ba9fSXin LI 		 * refused the proposed repair, it would be unsafe to
4219708ba9fSXin LI 		 * proceed with directory entry fix, so bail out in that
4229708ba9fSXin LI 		 * case.
4239708ba9fSXin LI 		 */
4249708ba9fSXin LI 		if (ret == FSERROR) {
4259708ba9fSXin LI 			return (FSERROR);
4269708ba9fSXin LI 		}
4274d2c300bSXin LI 		/*
4284d2c300bSXin LI 		 * The maximum file size on FAT32 is 4GiB - 1, which
4294d2c300bSXin LI 		 * will occupy a cluster chain of exactly 4GiB in
4304d2c300bSXin LI 		 * size.  On 32-bit platforms, since size_t is 32-bit,
4314d2c300bSXin LI 		 * it would wrap back to 0.
4324d2c300bSXin LI 		 */
4334d2c300bSXin LI 		physicalSize = (u_int64_t)chainsize * boot->ClusterSize;
4340121b42aSDavid E. O'Brien 	}
4350121b42aSDavid E. O'Brien 	if (physicalSize < dir->size) {
4365e822110SXin LI 		pwarn("size of %s is %u, should at most be %ju\n",
4375e822110SXin LI 		      fullpath(dir), dir->size, (uintmax_t)physicalSize);
4380121b42aSDavid E. O'Brien 		if (ask(1, "Truncate")) {
4390121b42aSDavid E. O'Brien 			dir->size = physicalSize;
4400121b42aSDavid E. O'Brien 			p[28] = (u_char)physicalSize;
4410121b42aSDavid E. O'Brien 			p[29] = (u_char)(physicalSize >> 8);
4420121b42aSDavid E. O'Brien 			p[30] = (u_char)(physicalSize >> 16);
4430121b42aSDavid E. O'Brien 			p[31] = (u_char)(physicalSize >> 24);
4440121b42aSDavid E. O'Brien 			return FSDIRMOD;
4450121b42aSDavid E. O'Brien 		} else
4460121b42aSDavid E. O'Brien 			return FSERROR;
4470121b42aSDavid E. O'Brien 	} else if (physicalSize - dir->size >= boot->ClusterSize) {
4480121b42aSDavid E. O'Brien 		pwarn("%s has too many clusters allocated\n",
4490121b42aSDavid E. O'Brien 		      fullpath(dir));
4500121b42aSDavid E. O'Brien 		if (ask(1, "Drop superfluous clusters")) {
4510121b42aSDavid E. O'Brien 			cl_t cl;
452ea4cbe31SPedro F. Giffuni 			u_int32_t sz, len;
4530121b42aSDavid E. O'Brien 
454ea4cbe31SPedro F. Giffuni 			for (cl = dir->head, len = sz = 0;
455ea4cbe31SPedro F. Giffuni 			    (sz += boot->ClusterSize) < dir->size; len++)
4569708ba9fSXin LI 				cl = fat_get_cl_next(fat, cl);
4579708ba9fSXin LI 			clearchain(fat, fat_get_cl_next(fat, cl));
4589708ba9fSXin LI 			ret = fat_set_cl_next(fat, cl, CLUST_EOF);
4599708ba9fSXin LI 			return (FSFATMOD | ret);
4600121b42aSDavid E. O'Brien 		} else
4610121b42aSDavid E. O'Brien 			return FSERROR;
4620121b42aSDavid E. O'Brien 	}
4630121b42aSDavid E. O'Brien 	return FSOK;
4640121b42aSDavid E. O'Brien }
4650121b42aSDavid E. O'Brien 
4660e6eb62dSXin LI static const u_char dot_name[11]    = ".          ";
4670e6eb62dSXin LI static const u_char dotdot_name[11] = "..         ";
468d78b6f1eSXin LI 
469d78b6f1eSXin LI /*
470d78b6f1eSXin LI  * Basic sanity check if the subdirectory have good '.' and '..' entries,
471d78b6f1eSXin LI  * and they are directory entries.  Further sanity checks are performed
472d78b6f1eSXin LI  * when we traverse into it.
473d78b6f1eSXin LI  */
474d78b6f1eSXin LI static int
check_subdirectory(struct fat_descriptor * fat,struct dosDirEntry * dir)4759708ba9fSXin LI check_subdirectory(struct fat_descriptor *fat, struct dosDirEntry *dir)
476d78b6f1eSXin LI {
477d78b6f1eSXin LI 	u_char *buf, *cp;
478d78b6f1eSXin LI 	off_t off;
479d78b6f1eSXin LI 	cl_t cl;
480d78b6f1eSXin LI 	int retval = FSOK;
4819708ba9fSXin LI 	int fd;
4829708ba9fSXin LI 	struct bootblock *boot;
4839708ba9fSXin LI 
4849708ba9fSXin LI 	boot = fat_get_boot(fat);
4859708ba9fSXin LI 	fd = fat_get_fd(fat);
486d78b6f1eSXin LI 
487d78b6f1eSXin LI 	cl = dir->head;
4889708ba9fSXin LI 	if (dir->parent && !fat_is_valid_cl(fat, cl)) {
489d78b6f1eSXin LI 		return FSERROR;
490d78b6f1eSXin LI 	}
491d78b6f1eSXin LI 
492d78b6f1eSXin LI 	if (!(boot->flags & FAT32) && !dir->parent) {
493d78b6f1eSXin LI 		off = boot->bpbResSectors + boot->bpbFATs *
494d78b6f1eSXin LI 			boot->FATsecs;
495d78b6f1eSXin LI 	} else {
496eb1c42c1SXin LI 		off = (cl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster;
497d78b6f1eSXin LI 	}
498d78b6f1eSXin LI 
499d78b6f1eSXin LI 	/*
500d78b6f1eSXin LI 	 * We only need to check the first two entries of the directory,
501d78b6f1eSXin LI 	 * which is found in the first sector of the directory entry,
502d78b6f1eSXin LI 	 * so read in only the first sector.
503d78b6f1eSXin LI 	 */
504d78b6f1eSXin LI 	buf = malloc(boot->bpbBytesPerSec);
505d78b6f1eSXin LI 	if (buf == NULL) {
506d78b6f1eSXin LI 		perr("No space for directory buffer (%u)",
507d78b6f1eSXin LI 		    boot->bpbBytesPerSec);
508d78b6f1eSXin LI 		return FSFATAL;
509d78b6f1eSXin LI 	}
510d78b6f1eSXin LI 
511d78b6f1eSXin LI 	off *= boot->bpbBytesPerSec;
5129708ba9fSXin LI 	if (lseek(fd, off, SEEK_SET) != off ||
5139708ba9fSXin LI 	    read(fd, buf, boot->bpbBytesPerSec) != (ssize_t)boot->bpbBytesPerSec) {
514d78b6f1eSXin LI 		perr("Unable to read directory");
515d78b6f1eSXin LI 		free(buf);
516d78b6f1eSXin LI 		return FSFATAL;
517d78b6f1eSXin LI 	}
518d78b6f1eSXin LI 
519d78b6f1eSXin LI 	/*
520d78b6f1eSXin LI 	 * Both `.' and `..' must be present and be the first two entries
521d78b6f1eSXin LI 	 * and be ATTR_DIRECTORY of a valid subdirectory.
522d78b6f1eSXin LI 	 */
523d78b6f1eSXin LI 	cp = buf;
524d78b6f1eSXin LI 	if (memcmp(cp, dot_name, sizeof(dot_name)) != 0 ||
525d78b6f1eSXin LI 	    (cp[11] & ATTR_DIRECTORY) != ATTR_DIRECTORY) {
526d78b6f1eSXin LI 		pwarn("%s: Incorrect `.' for %s.\n", __func__, dir->name);
527d78b6f1eSXin LI 		retval |= FSERROR;
528d78b6f1eSXin LI 	}
529d78b6f1eSXin LI 	cp += 32;
530d78b6f1eSXin LI 	if (memcmp(cp, dotdot_name, sizeof(dotdot_name)) != 0 ||
531d78b6f1eSXin LI 	    (cp[11] & ATTR_DIRECTORY) != ATTR_DIRECTORY) {
532d78b6f1eSXin LI 		pwarn("%s: Incorrect `..' for %s. \n", __func__, dir->name);
533d78b6f1eSXin LI 		retval |= FSERROR;
534d78b6f1eSXin LI 	}
535d78b6f1eSXin LI 
536d78b6f1eSXin LI 	free(buf);
537d78b6f1eSXin LI 	return retval;
538d78b6f1eSXin LI }
539d78b6f1eSXin LI 
5400121b42aSDavid E. O'Brien /*
5410121b42aSDavid E. O'Brien  * Read a directory and
5420121b42aSDavid E. O'Brien  *   - resolve long name records
5430121b42aSDavid E. O'Brien  *   - enter file and directory records into the parent's list
5440121b42aSDavid E. O'Brien  *   - push directories onto the todo-stack
5450121b42aSDavid E. O'Brien  */
5460121b42aSDavid E. O'Brien static int
readDosDirSection(struct fat_descriptor * fat,struct dosDirEntry * dir)5479708ba9fSXin LI readDosDirSection(struct fat_descriptor *fat, struct dosDirEntry *dir)
5480121b42aSDavid E. O'Brien {
5499708ba9fSXin LI 	struct bootblock *boot;
5500121b42aSDavid E. O'Brien 	struct dosDirEntry dirent, *d;
5510121b42aSDavid E. O'Brien 	u_char *p, *vallfn, *invlfn, *empty;
5520121b42aSDavid E. O'Brien 	off_t off;
5539708ba9fSXin LI 	int fd, i, j, k, iosize, entries;
5549708ba9fSXin LI 	bool is_legacyroot;
5550121b42aSDavid E. O'Brien 	cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0;
5560121b42aSDavid E. O'Brien 	char *t;
5570121b42aSDavid E. O'Brien 	u_int lidx = 0;
5580121b42aSDavid E. O'Brien 	int shortSum;
5590121b42aSDavid E. O'Brien 	int mod = FSOK;
5609708ba9fSXin LI 	size_t dirclusters;
5610121b42aSDavid E. O'Brien #define	THISMOD	0x8000			/* Only used within this routine */
5620121b42aSDavid E. O'Brien 
5639708ba9fSXin LI 	boot = fat_get_boot(fat);
5649708ba9fSXin LI 	fd = fat_get_fd(fat);
5659708ba9fSXin LI 
5660121b42aSDavid E. O'Brien 	cl = dir->head;
5679708ba9fSXin LI 	if (dir->parent && (!fat_is_valid_cl(fat, cl))) {
5680121b42aSDavid E. O'Brien 		/*
5690121b42aSDavid E. O'Brien 		 * Already handled somewhere else.
5700121b42aSDavid E. O'Brien 		 */
5710121b42aSDavid E. O'Brien 		return FSOK;
5720121b42aSDavid E. O'Brien 	}
5730121b42aSDavid E. O'Brien 	shortSum = -1;
5740121b42aSDavid E. O'Brien 	vallfn = invlfn = empty = NULL;
5759708ba9fSXin LI 
5769708ba9fSXin LI 	/*
5779708ba9fSXin LI 	 * If we are checking the legacy root (for FAT12/FAT16),
5789708ba9fSXin LI 	 * we will operate on the whole directory; otherwise, we
5799708ba9fSXin LI 	 * will operate on one cluster at a time, and also take
5809708ba9fSXin LI 	 * this opportunity to examine the chain.
5819708ba9fSXin LI 	 *
5829708ba9fSXin LI 	 * Derive how many entries we are going to encounter from
5839708ba9fSXin LI 	 * the I/O size.
5849708ba9fSXin LI 	 */
5859708ba9fSXin LI 	is_legacyroot = (dir->parent == NULL && !(boot->flags & FAT32));
5869708ba9fSXin LI 	if (is_legacyroot) {
5879708ba9fSXin LI 		iosize = boot->bpbRootDirEnts * 32;
5889708ba9fSXin LI 		entries = boot->bpbRootDirEnts;
5899708ba9fSXin LI 	} else {
5909708ba9fSXin LI 		iosize = boot->bpbSecPerClust * boot->bpbBytesPerSec;
5919708ba9fSXin LI 		entries = iosize / 32;
5929708ba9fSXin LI 		mod |= checkchain(fat, dir->head, &dirclusters);
5939708ba9fSXin LI 	}
5949708ba9fSXin LI 
5950121b42aSDavid E. O'Brien 	do {
5969708ba9fSXin LI 		if (is_legacyroot) {
5979708ba9fSXin LI 			/*
5989708ba9fSXin LI 			 * Special case for FAT12/FAT16 root -- read
5999708ba9fSXin LI 			 * in the whole root directory.
6009708ba9fSXin LI 			 */
60148790afaSBrian Somers 			off = boot->bpbResSectors + boot->bpbFATs *
60248790afaSBrian Somers 			    boot->FATsecs;
6030121b42aSDavid E. O'Brien 		} else {
6049708ba9fSXin LI 			/*
6059708ba9fSXin LI 			 * Otherwise, read in a cluster of the
6069708ba9fSXin LI 			 * directory.
6079708ba9fSXin LI 			 */
608eb1c42c1SXin LI 			off = (cl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster;
6090121b42aSDavid E. O'Brien 		}
6100121b42aSDavid E. O'Brien 
61175fb5353SKonstantin Belousov 		off *= boot->bpbBytesPerSec;
6129708ba9fSXin LI 		if (lseek(fd, off, SEEK_SET) != off ||
6139708ba9fSXin LI 		    read(fd, buffer, iosize) != iosize) {
6146cf357bcSUlrich Spörlein 			perr("Unable to read directory");
6150121b42aSDavid E. O'Brien 			return FSFATAL;
6160121b42aSDavid E. O'Brien 		}
6179708ba9fSXin LI 
6189708ba9fSXin LI 		for (p = buffer, i = 0; i < entries; i++, p += 32) {
6190121b42aSDavid E. O'Brien 			if (dir->fsckflags & DIREMPWARN) {
6200121b42aSDavid E. O'Brien 				*p = SLOT_EMPTY;
6210121b42aSDavid E. O'Brien 				continue;
6220121b42aSDavid E. O'Brien 			}
6230121b42aSDavid E. O'Brien 
6240121b42aSDavid E. O'Brien 			if (*p == SLOT_EMPTY || *p == SLOT_DELETED) {
6250121b42aSDavid E. O'Brien 				if (*p == SLOT_EMPTY) {
6260121b42aSDavid E. O'Brien 					dir->fsckflags |= DIREMPTY;
6270121b42aSDavid E. O'Brien 					empty = p;
6280121b42aSDavid E. O'Brien 					empcl = cl;
6290121b42aSDavid E. O'Brien 				}
6300121b42aSDavid E. O'Brien 				continue;
6310121b42aSDavid E. O'Brien 			}
6320121b42aSDavid E. O'Brien 
6330121b42aSDavid E. O'Brien 			if (dir->fsckflags & DIREMPTY) {
6340121b42aSDavid E. O'Brien 				if (!(dir->fsckflags & DIREMPWARN)) {
6350121b42aSDavid E. O'Brien 					pwarn("%s has entries after end of directory\n",
6360121b42aSDavid E. O'Brien 					      fullpath(dir));
6370121b42aSDavid E. O'Brien 					if (ask(1, "Extend")) {
6380121b42aSDavid E. O'Brien 						u_char *q;
6390121b42aSDavid E. O'Brien 
6400121b42aSDavid E. O'Brien 						dir->fsckflags &= ~DIREMPTY;
6419708ba9fSXin LI 						if (delete(fat,
6420121b42aSDavid E. O'Brien 							   empcl, empty - buffer,
6430121b42aSDavid E. O'Brien 							   cl, p - buffer, 1) == FSFATAL)
6440121b42aSDavid E. O'Brien 							return FSFATAL;
645f806eb62SXin LI 						q = ((empcl == cl) ? empty : buffer);
646f806eb62SXin LI 						assert(q != NULL);
6470121b42aSDavid E. O'Brien 						for (; q < p; q += 32)
6480121b42aSDavid E. O'Brien 							*q = SLOT_DELETED;
6490121b42aSDavid E. O'Brien 						mod |= THISMOD|FSDIRMOD;
6500121b42aSDavid E. O'Brien 					} else if (ask(0, "Truncate"))
6510121b42aSDavid E. O'Brien 						dir->fsckflags |= DIREMPWARN;
6520121b42aSDavid E. O'Brien 				}
6530121b42aSDavid E. O'Brien 				if (dir->fsckflags & DIREMPWARN) {
6540121b42aSDavid E. O'Brien 					*p = SLOT_DELETED;
6550121b42aSDavid E. O'Brien 					mod |= THISMOD|FSDIRMOD;
6560121b42aSDavid E. O'Brien 					continue;
6570121b42aSDavid E. O'Brien 				} else if (dir->fsckflags & DIREMPTY)
6580121b42aSDavid E. O'Brien 					mod |= FSERROR;
6590121b42aSDavid E. O'Brien 				empty = NULL;
6600121b42aSDavid E. O'Brien 			}
6610121b42aSDavid E. O'Brien 
6620121b42aSDavid E. O'Brien 			if (p[11] == ATTR_WIN95) {
6630121b42aSDavid E. O'Brien 				if (*p & LRFIRST) {
6640121b42aSDavid E. O'Brien 					if (shortSum != -1) {
6650121b42aSDavid E. O'Brien 						if (!invlfn) {
6660121b42aSDavid E. O'Brien 							invlfn = vallfn;
6670121b42aSDavid E. O'Brien 							invcl = valcl;
6680121b42aSDavid E. O'Brien 						}
6690121b42aSDavid E. O'Brien 					}
6700121b42aSDavid E. O'Brien 					memset(longName, 0, sizeof longName);
6710121b42aSDavid E. O'Brien 					shortSum = p[13];
6720121b42aSDavid E. O'Brien 					vallfn = p;
6730121b42aSDavid E. O'Brien 					valcl = cl;
6740121b42aSDavid E. O'Brien 				} else if (shortSum != p[13]
6750121b42aSDavid E. O'Brien 					   || lidx != (*p & LRNOMASK)) {
6760121b42aSDavid E. O'Brien 					if (!invlfn) {
6770121b42aSDavid E. O'Brien 						invlfn = vallfn;
6780121b42aSDavid E. O'Brien 						invcl = valcl;
6790121b42aSDavid E. O'Brien 					}
6800121b42aSDavid E. O'Brien 					if (!invlfn) {
6810121b42aSDavid E. O'Brien 						invlfn = p;
6820121b42aSDavid E. O'Brien 						invcl = cl;
6830121b42aSDavid E. O'Brien 					}
6840121b42aSDavid E. O'Brien 					vallfn = NULL;
6850121b42aSDavid E. O'Brien 				}
6860121b42aSDavid E. O'Brien 				lidx = *p & LRNOMASK;
687bf6ed416SXin LI 				if (lidx == 0) {
688bf6ed416SXin LI 					pwarn("invalid long name\n");
689bf6ed416SXin LI 					if (!invlfn) {
690bf6ed416SXin LI 						invlfn = vallfn;
691bf6ed416SXin LI 						invcl = valcl;
692bf6ed416SXin LI 					}
693bf6ed416SXin LI 					vallfn = NULL;
694bf6ed416SXin LI 					continue;
695bf6ed416SXin LI 				}
6960121b42aSDavid E. O'Brien 				t = longName + --lidx * 13;
69748790afaSBrian Somers 				for (k = 1; k < 11 && t < longName +
69848790afaSBrian Somers 				    sizeof(longName); k += 2) {
6990121b42aSDavid E. O'Brien 					if (!p[k] && !p[k + 1])
7000121b42aSDavid E. O'Brien 						break;
7010121b42aSDavid E. O'Brien 					*t++ = p[k];
7020121b42aSDavid E. O'Brien 					/*
7030121b42aSDavid E. O'Brien 					 * Warn about those unusable chars in msdosfs here?	XXX
7040121b42aSDavid E. O'Brien 					 */
7050121b42aSDavid E. O'Brien 					if (p[k + 1])
7060121b42aSDavid E. O'Brien 						t[-1] = '?';
7070121b42aSDavid E. O'Brien 				}
7080121b42aSDavid E. O'Brien 				if (k >= 11)
7090121b42aSDavid E. O'Brien 					for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) {
7100121b42aSDavid E. O'Brien 						if (!p[k] && !p[k + 1])
7110121b42aSDavid E. O'Brien 							break;
7120121b42aSDavid E. O'Brien 						*t++ = p[k];
7130121b42aSDavid E. O'Brien 						if (p[k + 1])
7140121b42aSDavid E. O'Brien 							t[-1] = '?';
7150121b42aSDavid E. O'Brien 					}
7160121b42aSDavid E. O'Brien 				if (k >= 26)
7170121b42aSDavid E. O'Brien 					for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) {
7180121b42aSDavid E. O'Brien 						if (!p[k] && !p[k + 1])
7190121b42aSDavid E. O'Brien 							break;
7200121b42aSDavid E. O'Brien 						*t++ = p[k];
7210121b42aSDavid E. O'Brien 						if (p[k + 1])
7220121b42aSDavid E. O'Brien 							t[-1] = '?';
7230121b42aSDavid E. O'Brien 					}
7240121b42aSDavid E. O'Brien 				if (t >= longName + sizeof(longName)) {
7250121b42aSDavid E. O'Brien 					pwarn("long filename too long\n");
7260121b42aSDavid E. O'Brien 					if (!invlfn) {
7270121b42aSDavid E. O'Brien 						invlfn = vallfn;
7280121b42aSDavid E. O'Brien 						invcl = valcl;
7290121b42aSDavid E. O'Brien 					}
7300121b42aSDavid E. O'Brien 					vallfn = NULL;
7310121b42aSDavid E. O'Brien 				}
7320121b42aSDavid E. O'Brien 				if (p[26] | (p[27] << 8)) {
7330121b42aSDavid E. O'Brien 					pwarn("long filename record cluster start != 0\n");
7340121b42aSDavid E. O'Brien 					if (!invlfn) {
7350121b42aSDavid E. O'Brien 						invlfn = vallfn;
7360121b42aSDavid E. O'Brien 						invcl = cl;
7370121b42aSDavid E. O'Brien 					}
7380121b42aSDavid E. O'Brien 					vallfn = NULL;
7390121b42aSDavid E. O'Brien 				}
7400121b42aSDavid E. O'Brien 				continue;	/* long records don't carry further
7410121b42aSDavid E. O'Brien 						 * information */
7420121b42aSDavid E. O'Brien 			}
7430121b42aSDavid E. O'Brien 
7440121b42aSDavid E. O'Brien 			/*
7450121b42aSDavid E. O'Brien 			 * This is a standard msdosfs directory entry.
7460121b42aSDavid E. O'Brien 			 */
7470121b42aSDavid E. O'Brien 			memset(&dirent, 0, sizeof dirent);
7480121b42aSDavid E. O'Brien 
7490121b42aSDavid E. O'Brien 			/*
7500121b42aSDavid E. O'Brien 			 * it's a short name record, but we need to know
7510121b42aSDavid E. O'Brien 			 * more, so get the flags first.
7520121b42aSDavid E. O'Brien 			 */
7530121b42aSDavid E. O'Brien 			dirent.flags = p[11];
7540121b42aSDavid E. O'Brien 
7550121b42aSDavid E. O'Brien 			/*
7560121b42aSDavid E. O'Brien 			 * Translate from 850 to ISO here		XXX
7570121b42aSDavid E. O'Brien 			 */
7580121b42aSDavid E. O'Brien 			for (j = 0; j < 8; j++)
7590121b42aSDavid E. O'Brien 				dirent.name[j] = p[j];
7600121b42aSDavid E. O'Brien 			dirent.name[8] = '\0';
7610121b42aSDavid E. O'Brien 			for (k = 7; k >= 0 && dirent.name[k] == ' '; k--)
7620121b42aSDavid E. O'Brien 				dirent.name[k] = '\0';
763aa031ef4SXin LI 			if (k < 0 || dirent.name[k] != '\0')
7640121b42aSDavid E. O'Brien 				k++;
7650121b42aSDavid E. O'Brien 			if (dirent.name[0] == SLOT_E5)
7660121b42aSDavid E. O'Brien 				dirent.name[0] = 0xe5;
7670121b42aSDavid E. O'Brien 
7680121b42aSDavid E. O'Brien 			if (dirent.flags & ATTR_VOLUME) {
7690121b42aSDavid E. O'Brien 				if (vallfn || invlfn) {
7709708ba9fSXin LI 					mod |= removede(fat,
7710121b42aSDavid E. O'Brien 							invlfn ? invlfn : vallfn, p,
7720121b42aSDavid E. O'Brien 							invlfn ? invcl : valcl, -1, 0,
7730121b42aSDavid E. O'Brien 							fullpath(dir), 2);
7740121b42aSDavid E. O'Brien 					vallfn = NULL;
7750121b42aSDavid E. O'Brien 					invlfn = NULL;
7760121b42aSDavid E. O'Brien 				}
7770121b42aSDavid E. O'Brien 				continue;
7780121b42aSDavid E. O'Brien 			}
7790121b42aSDavid E. O'Brien 
7800121b42aSDavid E. O'Brien 			if (p[8] != ' ')
7810121b42aSDavid E. O'Brien 				dirent.name[k++] = '.';
7820121b42aSDavid E. O'Brien 			for (j = 0; j < 3; j++)
7830121b42aSDavid E. O'Brien 				dirent.name[k++] = p[j+8];
7840121b42aSDavid E. O'Brien 			dirent.name[k] = '\0';
7850121b42aSDavid E. O'Brien 			for (k--; k >= 0 && dirent.name[k] == ' '; k--)
7860121b42aSDavid E. O'Brien 				dirent.name[k] = '\0';
7870121b42aSDavid E. O'Brien 
7880121b42aSDavid E. O'Brien 			if (vallfn && shortSum != calcShortSum(p)) {
7890121b42aSDavid E. O'Brien 				if (!invlfn) {
7900121b42aSDavid E. O'Brien 					invlfn = vallfn;
7910121b42aSDavid E. O'Brien 					invcl = valcl;
7920121b42aSDavid E. O'Brien 				}
7930121b42aSDavid E. O'Brien 				vallfn = NULL;
7940121b42aSDavid E. O'Brien 			}
7950121b42aSDavid E. O'Brien 			dirent.head = p[26] | (p[27] << 8);
7960121b42aSDavid E. O'Brien 			if (boot->ClustMask == CLUST32_MASK)
7970121b42aSDavid E. O'Brien 				dirent.head |= (p[20] << 16) | (p[21] << 24);
7980121b42aSDavid E. O'Brien 			dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24);
7990121b42aSDavid E. O'Brien 			if (vallfn) {
8006069db97SKonstantin Belousov 				strlcpy(dirent.lname, longName,
8016069db97SKonstantin Belousov 				    sizeof(dirent.lname));
8020121b42aSDavid E. O'Brien 				longName[0] = '\0';
8030121b42aSDavid E. O'Brien 				shortSum = -1;
8040121b42aSDavid E. O'Brien 			}
8050121b42aSDavid E. O'Brien 
8060121b42aSDavid E. O'Brien 			dirent.parent = dir;
8070121b42aSDavid E. O'Brien 			dirent.next = dir->child;
8080121b42aSDavid E. O'Brien 
8090121b42aSDavid E. O'Brien 			if (invlfn) {
8109708ba9fSXin LI 				mod |= k = removede(fat,
8110121b42aSDavid E. O'Brien 						    invlfn, vallfn ? vallfn : p,
8120121b42aSDavid E. O'Brien 						    invcl, vallfn ? valcl : cl, cl,
8130121b42aSDavid E. O'Brien 						    fullpath(&dirent), 0);
8140121b42aSDavid E. O'Brien 				if (mod & FSFATAL)
8150121b42aSDavid E. O'Brien 					return FSFATAL;
8160121b42aSDavid E. O'Brien 				if (vallfn
8170121b42aSDavid E. O'Brien 				    ? (valcl == cl && vallfn != buffer)
8180121b42aSDavid E. O'Brien 				    : p != buffer)
8190121b42aSDavid E. O'Brien 					if (k & FSDIRMOD)
8200121b42aSDavid E. O'Brien 						mod |= THISMOD;
8210121b42aSDavid E. O'Brien 			}
8220121b42aSDavid E. O'Brien 
8230121b42aSDavid E. O'Brien 			vallfn = NULL; /* not used any longer */
8240121b42aSDavid E. O'Brien 			invlfn = NULL;
8250121b42aSDavid E. O'Brien 
8260121b42aSDavid E. O'Brien 			/*
8279708ba9fSXin LI 			 * Check if the directory entry is sane.
8289708ba9fSXin LI 			 *
8299708ba9fSXin LI 			 * '.' and '..' are skipped, their sanity is
8309708ba9fSXin LI 			 * checked somewhere else.
8319708ba9fSXin LI 			 *
8329708ba9fSXin LI 			 * For everything else, check if we have a new,
8339708ba9fSXin LI 			 * valid cluster chain (beginning of a file or
8349708ba9fSXin LI 			 * directory that was never previously claimed
8359708ba9fSXin LI 			 * by another file) when it's a non-empty file
8369708ba9fSXin LI 			 * or a directory. The sanity of the cluster
8379708ba9fSXin LI 			 * chain is checked at a later time when we
8389708ba9fSXin LI 			 * traverse into the directory, or examine the
8399708ba9fSXin LI 			 * file's directory entry.
8409708ba9fSXin LI 			 *
8419708ba9fSXin LI 			 * The only possible fix is to delete the entry
8429708ba9fSXin LI 			 * if it's a directory; for file, we have to
8439708ba9fSXin LI 			 * truncate the size to 0.
8440121b42aSDavid E. O'Brien 			 */
8459708ba9fSXin LI 			if (!(dirent.flags & ATTR_DIRECTORY) ||
8469708ba9fSXin LI 			    (strcmp(dirent.name, ".") != 0 &&
8479708ba9fSXin LI 			    strcmp(dirent.name, "..") != 0)) {
8489708ba9fSXin LI 				if ((dirent.size != 0 || (dirent.flags & ATTR_DIRECTORY)) &&
8499708ba9fSXin LI 				    ((!fat_is_valid_cl(fat, dirent.head) ||
8509708ba9fSXin LI 				    !fat_is_cl_head(fat, dirent.head)))) {
8519708ba9fSXin LI 					if (!fat_is_valid_cl(fat, dirent.head)) {
8520121b42aSDavid E. O'Brien 						pwarn("%s starts with cluster out of range(%u)\n",
8530121b42aSDavid E. O'Brien 						    fullpath(&dirent),
8540121b42aSDavid E. O'Brien 						    dirent.head);
8559708ba9fSXin LI 					} else {
8569708ba9fSXin LI 						pwarn("%s doesn't start a new cluster chain\n",
8570121b42aSDavid E. O'Brien 						    fullpath(&dirent));
8589708ba9fSXin LI 					}
8599708ba9fSXin LI 
8600121b42aSDavid E. O'Brien 					if (dirent.flags & ATTR_DIRECTORY) {
8610121b42aSDavid E. O'Brien 						if (ask(0, "Remove")) {
8620121b42aSDavid E. O'Brien 							*p = SLOT_DELETED;
8630121b42aSDavid E. O'Brien 							mod |= THISMOD|FSDIRMOD;
8640121b42aSDavid E. O'Brien 						} else
8650121b42aSDavid E. O'Brien 							mod |= FSERROR;
8660121b42aSDavid E. O'Brien 						continue;
8670121b42aSDavid E. O'Brien 					} else {
8680121b42aSDavid E. O'Brien 						if (ask(1, "Truncate")) {
8690121b42aSDavid E. O'Brien 							p[28] = p[29] = p[30] = p[31] = 0;
8700121b42aSDavid E. O'Brien 							p[26] = p[27] = 0;
8710121b42aSDavid E. O'Brien 							if (boot->ClustMask == CLUST32_MASK)
8720121b42aSDavid E. O'Brien 								p[20] = p[21] = 0;
8730121b42aSDavid E. O'Brien 							dirent.size = 0;
8749708ba9fSXin LI 							dirent.head = 0;
8750121b42aSDavid E. O'Brien 							mod |= THISMOD|FSDIRMOD;
8760121b42aSDavid E. O'Brien 						} else
8770121b42aSDavid E. O'Brien 							mod |= FSERROR;
8780121b42aSDavid E. O'Brien 					}
8790121b42aSDavid E. O'Brien 				}
8809708ba9fSXin LI 			}
8810121b42aSDavid E. O'Brien 			if (dirent.flags & ATTR_DIRECTORY) {
8820121b42aSDavid E. O'Brien 				/*
8830121b42aSDavid E. O'Brien 				 * gather more info for directories
8840121b42aSDavid E. O'Brien 				 */
8850121b42aSDavid E. O'Brien 				struct dirTodoNode *n;
8860121b42aSDavid E. O'Brien 
8870121b42aSDavid E. O'Brien 				if (dirent.size) {
8880121b42aSDavid E. O'Brien 					pwarn("Directory %s has size != 0\n",
8890121b42aSDavid E. O'Brien 					      fullpath(&dirent));
8900121b42aSDavid E. O'Brien 					if (ask(1, "Correct")) {
8910121b42aSDavid E. O'Brien 						p[28] = p[29] = p[30] = p[31] = 0;
8920121b42aSDavid E. O'Brien 						dirent.size = 0;
8930121b42aSDavid E. O'Brien 						mod |= THISMOD|FSDIRMOD;
8940121b42aSDavid E. O'Brien 					} else
8950121b42aSDavid E. O'Brien 						mod |= FSERROR;
8960121b42aSDavid E. O'Brien 				}
8970121b42aSDavid E. O'Brien 				/*
8980121b42aSDavid E. O'Brien 				 * handle `.' and `..' specially
8990121b42aSDavid E. O'Brien 				 */
9000121b42aSDavid E. O'Brien 				if (strcmp(dirent.name, ".") == 0) {
9010121b42aSDavid E. O'Brien 					if (dirent.head != dir->head) {
9020121b42aSDavid E. O'Brien 						pwarn("`.' entry in %s has incorrect start cluster\n",
9030121b42aSDavid E. O'Brien 						      fullpath(dir));
9040121b42aSDavid E. O'Brien 						if (ask(1, "Correct")) {
9050121b42aSDavid E. O'Brien 							dirent.head = dir->head;
9060121b42aSDavid E. O'Brien 							p[26] = (u_char)dirent.head;
9070121b42aSDavid E. O'Brien 							p[27] = (u_char)(dirent.head >> 8);
9080121b42aSDavid E. O'Brien 							if (boot->ClustMask == CLUST32_MASK) {
9090121b42aSDavid E. O'Brien 								p[20] = (u_char)(dirent.head >> 16);
9100121b42aSDavid E. O'Brien 								p[21] = (u_char)(dirent.head >> 24);
9110121b42aSDavid E. O'Brien 							}
9120121b42aSDavid E. O'Brien 							mod |= THISMOD|FSDIRMOD;
9130121b42aSDavid E. O'Brien 						} else
9140121b42aSDavid E. O'Brien 							mod |= FSERROR;
9150121b42aSDavid E. O'Brien 					}
9160121b42aSDavid E. O'Brien 					continue;
9179708ba9fSXin LI 				} else if (strcmp(dirent.name, "..") == 0) {
9180121b42aSDavid E. O'Brien 					if (dir->parent) {		/* XXX */
9190121b42aSDavid E. O'Brien 						if (!dir->parent->parent) {
9200121b42aSDavid E. O'Brien 							if (dirent.head) {
9210121b42aSDavid E. O'Brien 								pwarn("`..' entry in %s has non-zero start cluster\n",
9220121b42aSDavid E. O'Brien 								      fullpath(dir));
9230121b42aSDavid E. O'Brien 								if (ask(1, "Correct")) {
9240121b42aSDavid E. O'Brien 									dirent.head = 0;
9250121b42aSDavid E. O'Brien 									p[26] = p[27] = 0;
9260121b42aSDavid E. O'Brien 									if (boot->ClustMask == CLUST32_MASK)
9270121b42aSDavid E. O'Brien 										p[20] = p[21] = 0;
9280121b42aSDavid E. O'Brien 									mod |= THISMOD|FSDIRMOD;
9290121b42aSDavid E. O'Brien 								} else
9300121b42aSDavid E. O'Brien 									mod |= FSERROR;
9310121b42aSDavid E. O'Brien 							}
9320121b42aSDavid E. O'Brien 						} else if (dirent.head != dir->parent->head) {
9330121b42aSDavid E. O'Brien 							pwarn("`..' entry in %s has incorrect start cluster\n",
9340121b42aSDavid E. O'Brien 							      fullpath(dir));
9350121b42aSDavid E. O'Brien 							if (ask(1, "Correct")) {
9360121b42aSDavid E. O'Brien 								dirent.head = dir->parent->head;
9370121b42aSDavid E. O'Brien 								p[26] = (u_char)dirent.head;
9380121b42aSDavid E. O'Brien 								p[27] = (u_char)(dirent.head >> 8);
9390121b42aSDavid E. O'Brien 								if (boot->ClustMask == CLUST32_MASK) {
9400121b42aSDavid E. O'Brien 									p[20] = (u_char)(dirent.head >> 16);
9410121b42aSDavid E. O'Brien 									p[21] = (u_char)(dirent.head >> 24);
9420121b42aSDavid E. O'Brien 								}
9430121b42aSDavid E. O'Brien 								mod |= THISMOD|FSDIRMOD;
9440121b42aSDavid E. O'Brien 							} else
9450121b42aSDavid E. O'Brien 								mod |= FSERROR;
9460121b42aSDavid E. O'Brien 						}
9470121b42aSDavid E. O'Brien 					}
9480121b42aSDavid E. O'Brien 					continue;
949d78b6f1eSXin LI 				} else {
950d78b6f1eSXin LI 					/*
951d78b6f1eSXin LI 					 * Only one directory entry can point
952d78b6f1eSXin LI 					 * to dir->head, it's '.'.
953d78b6f1eSXin LI 					 */
954d78b6f1eSXin LI 					if (dirent.head == dir->head) {
955d78b6f1eSXin LI 						pwarn("%s entry in %s has incorrect start cluster\n",
956d78b6f1eSXin LI 								dirent.name, fullpath(dir));
957d78b6f1eSXin LI 						if (ask(1, "Remove")) {
958d78b6f1eSXin LI 							*p = SLOT_DELETED;
959d78b6f1eSXin LI 							mod |= THISMOD|FSDIRMOD;
960d78b6f1eSXin LI 						} else
961d78b6f1eSXin LI 							mod |= FSERROR;
962d78b6f1eSXin LI 						continue;
9639708ba9fSXin LI 					} else if ((check_subdirectory(fat,
964d78b6f1eSXin LI 					    &dirent) & FSERROR) == FSERROR) {
965d78b6f1eSXin LI 						/*
966d78b6f1eSXin LI 						 * A subdirectory should have
967d78b6f1eSXin LI 						 * a dot (.) entry and a dot-dot
968d78b6f1eSXin LI 						 * (..) entry of ATTR_DIRECTORY,
969d78b6f1eSXin LI 						 * we will inspect further when
970d78b6f1eSXin LI 						 * traversing into it.
971d78b6f1eSXin LI 						 */
972d78b6f1eSXin LI 						if (ask(1, "Remove")) {
973d78b6f1eSXin LI 							*p = SLOT_DELETED;
974d78b6f1eSXin LI 							mod |= THISMOD|FSDIRMOD;
975d78b6f1eSXin LI 						} else
976d78b6f1eSXin LI 							mod |= FSERROR;
977d78b6f1eSXin LI 						continue;
978d78b6f1eSXin LI 					}
9790121b42aSDavid E. O'Brien 				}
9800121b42aSDavid E. O'Brien 
9810121b42aSDavid E. O'Brien 				/* create directory tree node */
9820121b42aSDavid E. O'Brien 				if (!(d = newDosDirEntry())) {
9836cf357bcSUlrich Spörlein 					perr("No space for directory");
9840121b42aSDavid E. O'Brien 					return FSFATAL;
9850121b42aSDavid E. O'Brien 				}
9860121b42aSDavid E. O'Brien 				memcpy(d, &dirent, sizeof(struct dosDirEntry));
9870121b42aSDavid E. O'Brien 				/* link it into the tree */
9880121b42aSDavid E. O'Brien 				dir->child = d;
9890121b42aSDavid E. O'Brien 
9900121b42aSDavid E. O'Brien 				/* Enter this directory into the todo list */
9910121b42aSDavid E. O'Brien 				if (!(n = newDirTodo())) {
9926cf357bcSUlrich Spörlein 					perr("No space for todo list");
9930121b42aSDavid E. O'Brien 					return FSFATAL;
9940121b42aSDavid E. O'Brien 				}
9950121b42aSDavid E. O'Brien 				n->next = pendingDirectories;
9960121b42aSDavid E. O'Brien 				n->dir = d;
9970121b42aSDavid E. O'Brien 				pendingDirectories = n;
998*deeb1d34SJohn F. Carr 			} else if (!(mod & FSERROR)) {
9999708ba9fSXin LI 				mod |= k = checksize(fat, p, &dirent);
10000121b42aSDavid E. O'Brien 				if (k & FSDIRMOD)
10010121b42aSDavid E. O'Brien 					mod |= THISMOD;
10020121b42aSDavid E. O'Brien 			}
10030121b42aSDavid E. O'Brien 			boot->NumFiles++;
10040121b42aSDavid E. O'Brien 		}
10056069db97SKonstantin Belousov 
10069708ba9fSXin LI 		if (is_legacyroot) {
10079708ba9fSXin LI 			/*
10089708ba9fSXin LI 			 * Don't bother to write back right now because
10099708ba9fSXin LI 			 * we may continue to make modification to the
10109708ba9fSXin LI 			 * non-FAT32 root directory below.
10119708ba9fSXin LI 			 */
10126069db97SKonstantin Belousov 			break;
10139708ba9fSXin LI 		} else if (mod & THISMOD) {
10149708ba9fSXin LI 			if (lseek(fd, off, SEEK_SET) != off
10159708ba9fSXin LI 			    || write(fd, buffer, iosize) != iosize) {
10166cf357bcSUlrich Spörlein 				perr("Unable to write directory");
10170121b42aSDavid E. O'Brien 				return FSFATAL;
10180121b42aSDavid E. O'Brien 			}
10190121b42aSDavid E. O'Brien 			mod &= ~THISMOD;
10200121b42aSDavid E. O'Brien 		}
10219708ba9fSXin LI 	} while (fat_is_valid_cl(fat, (cl = fat_get_cl_next(fat, cl))));
10220121b42aSDavid E. O'Brien 	if (invlfn || vallfn)
10239708ba9fSXin LI 		mod |= removede(fat,
10240121b42aSDavid E. O'Brien 				invlfn ? invlfn : vallfn, p,
10250121b42aSDavid E. O'Brien 				invlfn ? invcl : valcl, -1, 0,
10260121b42aSDavid E. O'Brien 				fullpath(dir), 1);
10276069db97SKonstantin Belousov 
10289708ba9fSXin LI 	/*
10299708ba9fSXin LI 	 * The root directory of non-FAT32 filesystems is in a special
10309708ba9fSXin LI 	 * area and may have been modified above removede() without
10319708ba9fSXin LI 	 * being written out.
10326069db97SKonstantin Belousov 	 */
10339708ba9fSXin LI 	if ((mod & FSDIRMOD) && is_legacyroot) {
10349708ba9fSXin LI 		if (lseek(fd, off, SEEK_SET) != off
10359708ba9fSXin LI 		    || write(fd, buffer, iosize) != iosize) {
10366cf357bcSUlrich Spörlein 			perr("Unable to write directory");
10376069db97SKonstantin Belousov 			return FSFATAL;
10386069db97SKonstantin Belousov 		}
10396069db97SKonstantin Belousov 		mod &= ~THISMOD;
10406069db97SKonstantin Belousov 	}
10410121b42aSDavid E. O'Brien 	return mod & ~THISMOD;
10420121b42aSDavid E. O'Brien }
10430121b42aSDavid E. O'Brien 
10440121b42aSDavid E. O'Brien int
handleDirTree(struct fat_descriptor * fat)10459708ba9fSXin LI handleDirTree(struct fat_descriptor *fat)
10460121b42aSDavid E. O'Brien {
10470121b42aSDavid E. O'Brien 	int mod;
10480121b42aSDavid E. O'Brien 
10499708ba9fSXin LI 	mod = readDosDirSection(fat, rootDir);
10500121b42aSDavid E. O'Brien 	if (mod & FSFATAL)
10510121b42aSDavid E. O'Brien 		return FSFATAL;
10520121b42aSDavid E. O'Brien 
10530121b42aSDavid E. O'Brien 	/*
10540121b42aSDavid E. O'Brien 	 * process the directory todo list
10550121b42aSDavid E. O'Brien 	 */
10560121b42aSDavid E. O'Brien 	while (pendingDirectories) {
10570121b42aSDavid E. O'Brien 		struct dosDirEntry *dir = pendingDirectories->dir;
10580121b42aSDavid E. O'Brien 		struct dirTodoNode *n = pendingDirectories->next;
10590121b42aSDavid E. O'Brien 
10600121b42aSDavid E. O'Brien 		/*
10610121b42aSDavid E. O'Brien 		 * remove TODO entry now, the list might change during
10620121b42aSDavid E. O'Brien 		 * directory reads
10630121b42aSDavid E. O'Brien 		 */
10640121b42aSDavid E. O'Brien 		freeDirTodo(pendingDirectories);
10650121b42aSDavid E. O'Brien 		pendingDirectories = n;
10660121b42aSDavid E. O'Brien 
10670121b42aSDavid E. O'Brien 		/*
10680121b42aSDavid E. O'Brien 		 * handle subdirectory
10690121b42aSDavid E. O'Brien 		 */
10709708ba9fSXin LI 		mod |= readDosDirSection(fat, dir);
10710121b42aSDavid E. O'Brien 		if (mod & FSFATAL)
10720121b42aSDavid E. O'Brien 			return FSFATAL;
10730121b42aSDavid E. O'Brien 	}
10740121b42aSDavid E. O'Brien 
10750121b42aSDavid E. O'Brien 	return mod;
10760121b42aSDavid E. O'Brien }
10770121b42aSDavid E. O'Brien 
10780121b42aSDavid E. O'Brien /*
10790121b42aSDavid E. O'Brien  * Try to reconnect a FAT chain into dir
10800121b42aSDavid E. O'Brien  */
10810121b42aSDavid E. O'Brien static u_char *lfbuf;
10820121b42aSDavid E. O'Brien static cl_t lfcl;
10830121b42aSDavid E. O'Brien static off_t lfoff;
10840121b42aSDavid E. O'Brien 
10850121b42aSDavid E. O'Brien int
reconnect(struct fat_descriptor * fat,cl_t head,size_t length)10869708ba9fSXin LI reconnect(struct fat_descriptor *fat, cl_t head, size_t length)
10870121b42aSDavid E. O'Brien {
10889708ba9fSXin LI 	struct bootblock *boot = fat_get_boot(fat);
10890121b42aSDavid E. O'Brien 	struct dosDirEntry d;
10909708ba9fSXin LI 	int len, dosfs;
10910121b42aSDavid E. O'Brien 	u_char *p;
10920121b42aSDavid E. O'Brien 
10939708ba9fSXin LI 	dosfs = fat_get_fd(fat);
10949708ba9fSXin LI 
10950121b42aSDavid E. O'Brien 	if (!ask(1, "Reconnect"))
10960121b42aSDavid E. O'Brien 		return FSERROR;
10970121b42aSDavid E. O'Brien 
10980121b42aSDavid E. O'Brien 	if (!lostDir) {
10990121b42aSDavid E. O'Brien 		for (lostDir = rootDir->child; lostDir; lostDir = lostDir->next) {
11000121b42aSDavid E. O'Brien 			if (!strcmp(lostDir->name, LOSTDIR))
11010121b42aSDavid E. O'Brien 				break;
11020121b42aSDavid E. O'Brien 		}
11030121b42aSDavid E. O'Brien 		if (!lostDir) {		/* Create LOSTDIR?		XXX */
11040121b42aSDavid E. O'Brien 			pwarn("No %s directory\n", LOSTDIR);
11050121b42aSDavid E. O'Brien 			return FSERROR;
11060121b42aSDavid E. O'Brien 		}
11070121b42aSDavid E. O'Brien 	}
11080121b42aSDavid E. O'Brien 	if (!lfbuf) {
11090121b42aSDavid E. O'Brien 		lfbuf = malloc(boot->ClusterSize);
11100121b42aSDavid E. O'Brien 		if (!lfbuf) {
11116cf357bcSUlrich Spörlein 			perr("No space for buffer");
11120121b42aSDavid E. O'Brien 			return FSFATAL;
11130121b42aSDavid E. O'Brien 		}
11140121b42aSDavid E. O'Brien 		p = NULL;
11150121b42aSDavid E. O'Brien 	} else
11160121b42aSDavid E. O'Brien 		p = lfbuf;
11170121b42aSDavid E. O'Brien 	while (1) {
11180121b42aSDavid E. O'Brien 		if (p)
11190121b42aSDavid E. O'Brien 			for (; p < lfbuf + boot->ClusterSize; p += 32)
11200121b42aSDavid E. O'Brien 				if (*p == SLOT_EMPTY
11210121b42aSDavid E. O'Brien 				    || *p == SLOT_DELETED)
11220121b42aSDavid E. O'Brien 					break;
11230121b42aSDavid E. O'Brien 		if (p && p < lfbuf + boot->ClusterSize)
11240121b42aSDavid E. O'Brien 			break;
11259708ba9fSXin LI 		lfcl = p ? fat_get_cl_next(fat, lfcl) : lostDir->head;
11260121b42aSDavid E. O'Brien 		if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) {
11270121b42aSDavid E. O'Brien 			/* Extend LOSTDIR?				XXX */
11280121b42aSDavid E. O'Brien 			pwarn("No space in %s\n", LOSTDIR);
1129fbf8a560SXin LI 			lfcl = (lostDir->head < boot->NumClusters) ? lostDir->head : 0;
11300121b42aSDavid E. O'Brien 			return FSERROR;
11310121b42aSDavid E. O'Brien 		}
1132eb1c42c1SXin LI 		lfoff = (lfcl - CLUST_FIRST) * boot->ClusterSize
1133eb1c42c1SXin LI 		    + boot->FirstCluster * boot->bpbBytesPerSec;
1134eb1c42c1SXin LI 
11350121b42aSDavid E. O'Brien 		if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
11366069db97SKonstantin Belousov 		    || (size_t)read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
11376cf357bcSUlrich Spörlein 			perr("could not read LOST.DIR");
11380121b42aSDavid E. O'Brien 			return FSFATAL;
11390121b42aSDavid E. O'Brien 		}
11400121b42aSDavid E. O'Brien 		p = lfbuf;
11410121b42aSDavid E. O'Brien 	}
11420121b42aSDavid E. O'Brien 
11430121b42aSDavid E. O'Brien 	boot->NumFiles++;
11440121b42aSDavid E. O'Brien 	/* Ensure uniqueness of entry here!				XXX */
11450121b42aSDavid E. O'Brien 	memset(&d, 0, sizeof d);
114633bed970SPedro F. Giffuni 	/* worst case -1 = 4294967295, 10 digits */
114733bed970SPedro F. Giffuni 	len = snprintf(d.name, sizeof(d.name), "%u", head);
11480121b42aSDavid E. O'Brien 	d.flags = 0;
11490121b42aSDavid E. O'Brien 	d.head = head;
11509708ba9fSXin LI 	d.size = length * boot->ClusterSize;
11510121b42aSDavid E. O'Brien 
115233bed970SPedro F. Giffuni 	memcpy(p, d.name, len);
115333bed970SPedro F. Giffuni 	memset(p + len, ' ', 11 - len);
115433bed970SPedro F. Giffuni 	memset(p + 11, 0, 32 - 11);
11550121b42aSDavid E. O'Brien 	p[26] = (u_char)d.head;
11560121b42aSDavid E. O'Brien 	p[27] = (u_char)(d.head >> 8);
11570121b42aSDavid E. O'Brien 	if (boot->ClustMask == CLUST32_MASK) {
11580121b42aSDavid E. O'Brien 		p[20] = (u_char)(d.head >> 16);
11590121b42aSDavid E. O'Brien 		p[21] = (u_char)(d.head >> 24);
11600121b42aSDavid E. O'Brien 	}
11610121b42aSDavid E. O'Brien 	p[28] = (u_char)d.size;
11620121b42aSDavid E. O'Brien 	p[29] = (u_char)(d.size >> 8);
11630121b42aSDavid E. O'Brien 	p[30] = (u_char)(d.size >> 16);
11640121b42aSDavid E. O'Brien 	p[31] = (u_char)(d.size >> 24);
11650121b42aSDavid E. O'Brien 	if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
11666069db97SKonstantin Belousov 	    || (size_t)write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
11676cf357bcSUlrich Spörlein 		perr("could not write LOST.DIR");
11680121b42aSDavid E. O'Brien 		return FSFATAL;
11690121b42aSDavid E. O'Brien 	}
11700121b42aSDavid E. O'Brien 	return FSDIRMOD;
11710121b42aSDavid E. O'Brien }
11720121b42aSDavid E. O'Brien 
11730121b42aSDavid E. O'Brien void
finishlf(void)1174b70cd7eeSWarner Losh finishlf(void)
11750121b42aSDavid E. O'Brien {
11760121b42aSDavid E. O'Brien 	if (lfbuf)
11770121b42aSDavid E. O'Brien 		free(lfbuf);
11780121b42aSDavid E. O'Brien 	lfbuf = NULL;
11790121b42aSDavid E. O'Brien }
1180