179455Sobrien/*
279455Sobrien * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
379455Sobrien * Copyright (c) 1995 Martin Husemann
479455Sobrien * Some structure declaration borrowed from Paul Popelka
579455Sobrien * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference.
679455Sobrien *
779455Sobrien * Redistribution and use in source and binary forms, with or without
879455Sobrien * modification, are permitted provided that the following conditions
979455Sobrien * are met:
1079455Sobrien * 1. Redistributions of source code must retain the above copyright
1179455Sobrien *    notice, this list of conditions and the following disclaimer.
1279455Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1379455Sobrien *    notice, this list of conditions and the following disclaimer in the
1479455Sobrien *    documentation and/or other materials provided with the distribution.
1579455Sobrien *
1679455Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
1779455Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1879455Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1979455Sobrien * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2079455Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2179455Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2279455Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2379455Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2479455Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2579455Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2679455Sobrien */
2779455Sobrien
2879455Sobrien
2979455Sobrien#include <sys/cdefs.h>
3079455Sobrien#ifndef lint
31241806Suqs__RCSID("$NetBSD: dir.c,v 1.20 2006/06/05 16:51:18 christos Exp $");
3279455Sobrienstatic const char rcsid[] =
3379455Sobrien  "$FreeBSD$";
3479455Sobrien#endif /* not lint */
3579455Sobrien
3679455Sobrien#include <stdio.h>
3779455Sobrien#include <stdlib.h>
3879455Sobrien#include <string.h>
3979455Sobrien#include <ctype.h>
4079455Sobrien#include <unistd.h>
4179455Sobrien#include <time.h>
4279455Sobrien
4379455Sobrien#include <sys/param.h>
4479455Sobrien
4579455Sobrien#include "ext.h"
4679455Sobrien#include "fsutil.h"
4779455Sobrien
4879455Sobrien#define	SLOT_EMPTY	0x00		/* slot has never been used */
4979455Sobrien#define	SLOT_E5		0x05		/* the real value is 0xe5 */
5079455Sobrien#define	SLOT_DELETED	0xe5		/* file in this slot deleted */
5179455Sobrien
5279455Sobrien#define	ATTR_NORMAL	0x00		/* normal file */
5379455Sobrien#define	ATTR_READONLY	0x01		/* file is readonly */
5479455Sobrien#define	ATTR_HIDDEN	0x02		/* file is hidden */
5579455Sobrien#define	ATTR_SYSTEM	0x04		/* file is a system file */
5679455Sobrien#define	ATTR_VOLUME	0x08		/* entry is a volume label */
5779455Sobrien#define	ATTR_DIRECTORY	0x10		/* entry is a directory name */
5879455Sobrien#define	ATTR_ARCHIVE	0x20		/* file is new or modified */
5979455Sobrien
6079455Sobrien#define	ATTR_WIN95	0x0f		/* long name record */
6179455Sobrien
6279455Sobrien/*
6379455Sobrien * This is the format of the contents of the deTime field in the direntry
6479455Sobrien * structure.
6579455Sobrien * We don't use bitfields because we don't know how compilers for
6679455Sobrien * arbitrary machines will lay them out.
6779455Sobrien */
6879455Sobrien#define DT_2SECONDS_MASK	0x1F	/* seconds divided by 2 */
6979455Sobrien#define DT_2SECONDS_SHIFT	0
7079455Sobrien#define DT_MINUTES_MASK		0x7E0	/* minutes */
7179455Sobrien#define DT_MINUTES_SHIFT	5
7279455Sobrien#define DT_HOURS_MASK		0xF800	/* hours */
7379455Sobrien#define DT_HOURS_SHIFT		11
7479455Sobrien
7579455Sobrien/*
7679455Sobrien * This is the format of the contents of the deDate field in the direntry
7779455Sobrien * structure.
7879455Sobrien */
7979455Sobrien#define DD_DAY_MASK		0x1F	/* day of month */
8079455Sobrien#define DD_DAY_SHIFT		0
8179455Sobrien#define DD_MONTH_MASK		0x1E0	/* month */
8279455Sobrien#define DD_MONTH_SHIFT		5
8379455Sobrien#define DD_YEAR_MASK		0xFE00	/* year - 1980 */
8479455Sobrien#define DD_YEAR_SHIFT		9
8579455Sobrien
8679455Sobrien
8779455Sobrien/* dir.c */
8892839Simpstatic struct dosDirEntry *newDosDirEntry(void);
8992839Simpstatic void freeDosDirEntry(struct dosDirEntry *);
9092839Simpstatic struct dirTodoNode *newDirTodo(void);
9192839Simpstatic void freeDirTodo(struct dirTodoNode *);
9292839Simpstatic char *fullpath(struct dosDirEntry *);
9392839Simpstatic u_char calcShortSum(u_char *);
9492839Simpstatic int delete(int, struct bootblock *, struct fatEntry *, cl_t, int,
9592839Simp    cl_t, int, int);
9692839Simpstatic int removede(int, struct bootblock *, struct fatEntry *, u_char *,
9792839Simp    u_char *, cl_t, cl_t, cl_t, char *, int);
9892839Simpstatic int checksize(struct bootblock *, struct fatEntry *, u_char *,
9992839Simp    struct dosDirEntry *);
10092839Simpstatic int readDosDirSection(int, struct bootblock *, struct fatEntry *,
10192839Simp    struct dosDirEntry *);
10279455Sobrien
10379455Sobrien/*
10479455Sobrien * Manage free dosDirEntry structures.
10579455Sobrien */
10679455Sobrienstatic struct dosDirEntry *freede;
10779455Sobrien
10879455Sobrienstatic struct dosDirEntry *
10992839SimpnewDosDirEntry(void)
11079455Sobrien{
11179455Sobrien	struct dosDirEntry *de;
11279455Sobrien
11379455Sobrien	if (!(de = freede)) {
11479455Sobrien		if (!(de = (struct dosDirEntry *)malloc(sizeof *de)))
11579455Sobrien			return 0;
11679455Sobrien	} else
11779455Sobrien		freede = de->next;
11879455Sobrien	return de;
11979455Sobrien}
12079455Sobrien
12179455Sobrienstatic void
12292839SimpfreeDosDirEntry(struct dosDirEntry *de)
12379455Sobrien{
12479455Sobrien	de->next = freede;
12579455Sobrien	freede = de;
12679455Sobrien}
12779455Sobrien
12879455Sobrien/*
12979455Sobrien * The same for dirTodoNode structures.
13079455Sobrien */
13179455Sobrienstatic struct dirTodoNode *freedt;
13279455Sobrien
13379455Sobrienstatic struct dirTodoNode *
13492839SimpnewDirTodo(void)
13579455Sobrien{
13679455Sobrien	struct dirTodoNode *dt;
13779455Sobrien
13879455Sobrien	if (!(dt = freedt)) {
13979455Sobrien		if (!(dt = (struct dirTodoNode *)malloc(sizeof *dt)))
14079455Sobrien			return 0;
14179455Sobrien	} else
14279455Sobrien		freedt = dt->next;
14379455Sobrien	return dt;
14479455Sobrien}
14579455Sobrien
14679455Sobrienstatic void
14792839SimpfreeDirTodo(struct dirTodoNode *dt)
14879455Sobrien{
14979455Sobrien	dt->next = freedt;
15079455Sobrien	freedt = dt;
15179455Sobrien}
15279455Sobrien
15379455Sobrien/*
15479455Sobrien * The stack of unread directories
15579455Sobrien */
156227081Sedstatic struct dirTodoNode *pendingDirectories = NULL;
15779455Sobrien
15879455Sobrien/*
15979455Sobrien * Return the full pathname for a directory entry.
16079455Sobrien */
16179455Sobrienstatic char *
16292839Simpfullpath(struct dosDirEntry *dir)
16379455Sobrien{
16479455Sobrien	static char namebuf[MAXPATHLEN + 1];
16579455Sobrien	char *cp, *np;
16679455Sobrien	int nl;
16779455Sobrien
16879455Sobrien	cp = namebuf + sizeof namebuf - 1;
16979455Sobrien	*cp = '\0';
17079455Sobrien	do {
17179455Sobrien		np = dir->lname[0] ? dir->lname : dir->name;
17279455Sobrien		nl = strlen(np);
17379455Sobrien		if ((cp -= nl) <= namebuf + 1)
17479455Sobrien			break;
17579455Sobrien		memcpy(cp, np, nl);
17679455Sobrien		*--cp = '/';
17779455Sobrien	} while ((dir = dir->parent) != NULL);
17879455Sobrien	if (dir)
17979455Sobrien		*--cp = '?';
18079455Sobrien	else
18179455Sobrien		cp++;
18279455Sobrien	return cp;
18379455Sobrien}
18479455Sobrien
18579455Sobrien/*
18679455Sobrien * Calculate a checksum over an 8.3 alias name
18779455Sobrien */
18879455Sobrienstatic u_char
18992839SimpcalcShortSum(u_char *p)
19079455Sobrien{
19179455Sobrien	u_char sum = 0;
19279455Sobrien	int i;
19379455Sobrien
19479455Sobrien	for (i = 0; i < 11; i++) {
19579455Sobrien		sum = (sum << 7)|(sum >> 1);	/* rotate right */
19679455Sobrien		sum += p[i];
19779455Sobrien	}
19879455Sobrien
19979455Sobrien	return sum;
20079455Sobrien}
20179455Sobrien
20279455Sobrien/*
20379455Sobrien * Global variables temporarily used during a directory scan
20479455Sobrien */
20579455Sobrienstatic char longName[DOSLONGNAMELEN] = "";
20679455Sobrienstatic u_char *buffer = NULL;
20779455Sobrienstatic u_char *delbuf = NULL;
20879455Sobrien
20979455Sobrienstruct dosDirEntry *rootDir;
21079455Sobrienstatic struct dosDirEntry *lostDir;
21179455Sobrien
21279455Sobrien/*
21379455Sobrien * Init internal state for a new directory scan.
21479455Sobrien */
21579455Sobrienint
21692839SimpresetDosDirSection(struct bootblock *boot, struct fatEntry *fat)
21779455Sobrien{
21879455Sobrien	int b1, b2;
21979455Sobrien	cl_t cl;
22079455Sobrien	int ret = FSOK;
221241806Suqs	size_t len;
22279455Sobrien
223203874Skib	b1 = boot->bpbRootDirEnts * 32;
224203874Skib	b2 = boot->bpbSecPerClust * boot->bpbBytesPerSec;
22579455Sobrien
226241806Suqs	if ((buffer = malloc(len = b1 > b2 ? b1 : b2)) == NULL) {
227241806Suqs		perr("No space for directory buffer (%zu)", len);
22879455Sobrien		return FSFATAL;
22979455Sobrien	}
230203872Skib
231241806Suqs	if ((delbuf = malloc(len = b2)) == NULL) {
232203872Skib		free(buffer);
233241806Suqs		perr("No space for directory delbuf (%zu)", len);
234203872Skib		return FSFATAL;
235203872Skib	}
236203872Skib
237203872Skib	if ((rootDir = newDosDirEntry()) == NULL) {
238203872Skib		free(buffer);
239203872Skib		free(delbuf);
240241806Suqs		perr("No space for directory entry");
241203872Skib		return FSFATAL;
242203872Skib	}
243203872Skib
24479455Sobrien	memset(rootDir, 0, sizeof *rootDir);
24579455Sobrien	if (boot->flags & FAT32) {
246209364Sbrian		if (boot->bpbRootClust < CLUST_FIRST ||
247209364Sbrian		    boot->bpbRootClust >= boot->NumClusters) {
24879455Sobrien			pfatal("Root directory starts with cluster out of range(%u)",
249203874Skib			       boot->bpbRootClust);
25079455Sobrien			return FSFATAL;
25179455Sobrien		}
252203874Skib		cl = fat[boot->bpbRootClust].next;
25379455Sobrien		if (cl < CLUST_FIRST
25479455Sobrien		    || (cl >= CLUST_RSRVD && cl< CLUST_EOFS)
255203874Skib		    || fat[boot->bpbRootClust].head != boot->bpbRootClust) {
25679455Sobrien			if (cl == CLUST_FREE)
25779455Sobrien				pwarn("Root directory starts with free cluster\n");
25879455Sobrien			else if (cl >= CLUST_RSRVD)
25979455Sobrien				pwarn("Root directory starts with cluster marked %s\n",
26079455Sobrien				      rsrvdcltype(cl));
26179455Sobrien			else {
26279455Sobrien				pfatal("Root directory doesn't start a cluster chain");
26379455Sobrien				return FSFATAL;
26479455Sobrien			}
26579455Sobrien			if (ask(1, "Fix")) {
266203874Skib				fat[boot->bpbRootClust].next = CLUST_FREE;
26779455Sobrien				ret = FSFATMOD;
26879455Sobrien			} else
26979455Sobrien				ret = FSFATAL;
27079455Sobrien		}
27179455Sobrien
272203874Skib		fat[boot->bpbRootClust].flags |= FAT_USED;
273203874Skib		rootDir->head = boot->bpbRootClust;
27479455Sobrien	}
27579455Sobrien
27679455Sobrien	return ret;
27779455Sobrien}
27879455Sobrien
27979455Sobrien/*
28079455Sobrien * Cleanup after a directory scan
28179455Sobrien */
28279455Sobrienvoid
28392839SimpfinishDosDirSection(void)
28479455Sobrien{
28579455Sobrien	struct dirTodoNode *p, *np;
28679455Sobrien	struct dosDirEntry *d, *nd;
28779455Sobrien
28879455Sobrien	for (p = pendingDirectories; p; p = np) {
28979455Sobrien		np = p->next;
29079455Sobrien		freeDirTodo(p);
29179455Sobrien	}
29279455Sobrien	pendingDirectories = 0;
29379455Sobrien	for (d = rootDir; d; d = nd) {
29479455Sobrien		if ((nd = d->child) != NULL) {
29579455Sobrien			d->child = 0;
29679455Sobrien			continue;
29779455Sobrien		}
29879455Sobrien		if (!(nd = d->next))
29979455Sobrien			nd = d->parent;
30079455Sobrien		freeDosDirEntry(d);
30179455Sobrien	}
30279455Sobrien	rootDir = lostDir = NULL;
30379455Sobrien	free(buffer);
30479455Sobrien	free(delbuf);
30579455Sobrien	buffer = NULL;
30679455Sobrien	delbuf = NULL;
30779455Sobrien}
30879455Sobrien
30979455Sobrien/*
31079455Sobrien * Delete directory entries between startcl, startoff and endcl, endoff.
31179455Sobrien */
31279455Sobrienstatic int
31392839Simpdelete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl,
31492839Simp    int startoff, cl_t endcl, int endoff, int notlast)
31579455Sobrien{
31679455Sobrien	u_char *s, *e;
31779455Sobrien	off_t off;
318203874Skib	int clsz = boot->bpbSecPerClust * boot->bpbBytesPerSec;
31979455Sobrien
32079455Sobrien	s = delbuf + startoff;
32179455Sobrien	e = delbuf + clsz;
32279455Sobrien	while (startcl >= CLUST_FIRST && startcl < boot->NumClusters) {
32379455Sobrien		if (startcl == endcl) {
32479455Sobrien			if (notlast)
32579455Sobrien				break;
32679455Sobrien			e = delbuf + endoff;
32779455Sobrien		}
328203874Skib		off = startcl * boot->bpbSecPerClust + boot->ClusterOffset;
329203874Skib		off *= boot->bpbBytesPerSec;
33079455Sobrien		if (lseek(f, off, SEEK_SET) != off
33179455Sobrien		    || read(f, delbuf, clsz) != clsz) {
332241806Suqs			perr("Unable to read directory");
33379455Sobrien			return FSFATAL;
33479455Sobrien		}
33579455Sobrien		while (s < e) {
33679455Sobrien			*s = SLOT_DELETED;
33779455Sobrien			s += 32;
33879455Sobrien		}
33979455Sobrien		if (lseek(f, off, SEEK_SET) != off
34079455Sobrien		    || write(f, delbuf, clsz) != clsz) {
341241806Suqs			perr("Unable to write directory");
34279455Sobrien			return FSFATAL;
34379455Sobrien		}
34479455Sobrien		if (startcl == endcl)
34579455Sobrien			break;
34679455Sobrien		startcl = fat[startcl].next;
34779455Sobrien		s = delbuf;
34879455Sobrien	}
34979455Sobrien	return FSOK;
35079455Sobrien}
35179455Sobrien
35279455Sobrienstatic int
35392839Simpremovede(int f, struct bootblock *boot, struct fatEntry *fat, u_char *start,
35492839Simp    u_char *end, cl_t startcl, cl_t endcl, cl_t curcl, char *path, int type)
35579455Sobrien{
35679455Sobrien	switch (type) {
35779455Sobrien	case 0:
35879455Sobrien		pwarn("Invalid long filename entry for %s\n", path);
35979455Sobrien		break;
36079455Sobrien	case 1:
361209364Sbrian		pwarn("Invalid long filename entry at end of directory %s\n",
362209364Sbrian		    path);
36379455Sobrien		break;
36479455Sobrien	case 2:
36579455Sobrien		pwarn("Invalid long filename entry for volume label\n");
36679455Sobrien		break;
36779455Sobrien	}
36879455Sobrien	if (ask(0, "Remove")) {
36979455Sobrien		if (startcl != curcl) {
37079455Sobrien			if (delete(f, boot, fat,
37179455Sobrien				   startcl, start - buffer,
37279455Sobrien				   endcl, end - buffer,
37379455Sobrien				   endcl == curcl) == FSFATAL)
37479455Sobrien				return FSFATAL;
37579455Sobrien			start = buffer;
37679455Sobrien		}
377203872Skib		/* startcl is < CLUST_FIRST for !fat32 root */
378203872Skib		if ((endcl == curcl) || (startcl < CLUST_FIRST))
37979455Sobrien			for (; start < end; start += 32)
38079455Sobrien				*start = SLOT_DELETED;
38179455Sobrien		return FSDIRMOD;
38279455Sobrien	}
38379455Sobrien	return FSERROR;
38479455Sobrien}
38579455Sobrien
38679455Sobrien/*
38779455Sobrien * Check an in-memory file entry
38879455Sobrien */
38979455Sobrienstatic int
39092839Simpchecksize(struct bootblock *boot, struct fatEntry *fat, u_char *p,
39192839Simp    struct dosDirEntry *dir)
39279455Sobrien{
39379455Sobrien	/*
39479455Sobrien	 * Check size on ordinary files
39579455Sobrien	 */
396203872Skib	u_int32_t physicalSize;
39779455Sobrien
39879455Sobrien	if (dir->head == CLUST_FREE)
39979455Sobrien		physicalSize = 0;
40079455Sobrien	else {
40179455Sobrien		if (dir->head < CLUST_FIRST || dir->head >= boot->NumClusters)
40279455Sobrien			return FSERROR;
40379455Sobrien		physicalSize = fat[dir->head].length * boot->ClusterSize;
40479455Sobrien	}
40579455Sobrien	if (physicalSize < dir->size) {
40679455Sobrien		pwarn("size of %s is %u, should at most be %u\n",
40779455Sobrien		      fullpath(dir), dir->size, physicalSize);
40879455Sobrien		if (ask(1, "Truncate")) {
40979455Sobrien			dir->size = physicalSize;
41079455Sobrien			p[28] = (u_char)physicalSize;
41179455Sobrien			p[29] = (u_char)(physicalSize >> 8);
41279455Sobrien			p[30] = (u_char)(physicalSize >> 16);
41379455Sobrien			p[31] = (u_char)(physicalSize >> 24);
41479455Sobrien			return FSDIRMOD;
41579455Sobrien		} else
41679455Sobrien			return FSERROR;
41779455Sobrien	} else if (physicalSize - dir->size >= boot->ClusterSize) {
41879455Sobrien		pwarn("%s has too many clusters allocated\n",
41979455Sobrien		      fullpath(dir));
42079455Sobrien		if (ask(1, "Drop superfluous clusters")) {
42179455Sobrien			cl_t cl;
422268968Spfg			u_int32_t sz, len;
42379455Sobrien
424268968Spfg			for (cl = dir->head, len = sz = 0;
425268968Spfg			    (sz += boot->ClusterSize) < dir->size; len++)
42679455Sobrien				cl = fat[cl].next;
42779455Sobrien			clearchain(boot, fat, fat[cl].next);
42879455Sobrien			fat[cl].next = CLUST_EOF;
429268968Spfg			fat[dir->head].length = len;
43079455Sobrien			return FSFATMOD;
43179455Sobrien		} else
43279455Sobrien			return FSERROR;
43379455Sobrien	}
43479455Sobrien	return FSOK;
43579455Sobrien}
43679455Sobrien
43779455Sobrien/*
43879455Sobrien * Read a directory and
43979455Sobrien *   - resolve long name records
44079455Sobrien *   - enter file and directory records into the parent's list
44179455Sobrien *   - push directories onto the todo-stack
44279455Sobrien */
44379455Sobrienstatic int
44492839SimpreadDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat,
44592839Simp    struct dosDirEntry *dir)
44679455Sobrien{
44779455Sobrien	struct dosDirEntry dirent, *d;
44879455Sobrien	u_char *p, *vallfn, *invlfn, *empty;
44979455Sobrien	off_t off;
45079455Sobrien	int i, j, k, last;
45179455Sobrien	cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0;
45279455Sobrien	char *t;
45379455Sobrien	u_int lidx = 0;
45479455Sobrien	int shortSum;
45579455Sobrien	int mod = FSOK;
45679455Sobrien#define	THISMOD	0x8000			/* Only used within this routine */
45779455Sobrien
45879455Sobrien	cl = dir->head;
45979455Sobrien	if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
46079455Sobrien		/*
46179455Sobrien		 * Already handled somewhere else.
46279455Sobrien		 */
46379455Sobrien		return FSOK;
46479455Sobrien	}
46579455Sobrien	shortSum = -1;
46679455Sobrien	vallfn = invlfn = empty = NULL;
46779455Sobrien	do {
46879455Sobrien		if (!(boot->flags & FAT32) && !dir->parent) {
469203874Skib			last = boot->bpbRootDirEnts * 32;
470209364Sbrian			off = boot->bpbResSectors + boot->bpbFATs *
471209364Sbrian			    boot->FATsecs;
47279455Sobrien		} else {
473203874Skib			last = boot->bpbSecPerClust * boot->bpbBytesPerSec;
474203874Skib			off = cl * boot->bpbSecPerClust + boot->ClusterOffset;
47579455Sobrien		}
47679455Sobrien
477203874Skib		off *= boot->bpbBytesPerSec;
47879455Sobrien		if (lseek(f, off, SEEK_SET) != off
47979455Sobrien		    || read(f, buffer, last) != last) {
480241806Suqs			perr("Unable to read directory");
48179455Sobrien			return FSFATAL;
48279455Sobrien		}
48379455Sobrien		last /= 32;
48479455Sobrien		/*
48579455Sobrien		 * Check `.' and `..' entries here?			XXX
48679455Sobrien		 */
48779455Sobrien		for (p = buffer, i = 0; i < last; i++, p += 32) {
48879455Sobrien			if (dir->fsckflags & DIREMPWARN) {
48979455Sobrien				*p = SLOT_EMPTY;
49079455Sobrien				continue;
49179455Sobrien			}
49279455Sobrien
49379455Sobrien			if (*p == SLOT_EMPTY || *p == SLOT_DELETED) {
49479455Sobrien				if (*p == SLOT_EMPTY) {
49579455Sobrien					dir->fsckflags |= DIREMPTY;
49679455Sobrien					empty = p;
49779455Sobrien					empcl = cl;
49879455Sobrien				}
49979455Sobrien				continue;
50079455Sobrien			}
50179455Sobrien
50279455Sobrien			if (dir->fsckflags & DIREMPTY) {
50379455Sobrien				if (!(dir->fsckflags & DIREMPWARN)) {
50479455Sobrien					pwarn("%s has entries after end of directory\n",
50579455Sobrien					      fullpath(dir));
50679455Sobrien					if (ask(1, "Extend")) {
50779455Sobrien						u_char *q;
50879455Sobrien
50979455Sobrien						dir->fsckflags &= ~DIREMPTY;
51079455Sobrien						if (delete(f, boot, fat,
51179455Sobrien							   empcl, empty - buffer,
51279455Sobrien							   cl, p - buffer, 1) == FSFATAL)
51379455Sobrien							return FSFATAL;
51479455Sobrien						q = empcl == cl ? empty : buffer;
51579455Sobrien						for (; q < p; q += 32)
51679455Sobrien							*q = SLOT_DELETED;
51779455Sobrien						mod |= THISMOD|FSDIRMOD;
51879455Sobrien					} else if (ask(0, "Truncate"))
51979455Sobrien						dir->fsckflags |= DIREMPWARN;
52079455Sobrien				}
52179455Sobrien				if (dir->fsckflags & DIREMPWARN) {
52279455Sobrien					*p = SLOT_DELETED;
52379455Sobrien					mod |= THISMOD|FSDIRMOD;
52479455Sobrien					continue;
52579455Sobrien				} else if (dir->fsckflags & DIREMPTY)
52679455Sobrien					mod |= FSERROR;
52779455Sobrien				empty = NULL;
52879455Sobrien			}
52979455Sobrien
53079455Sobrien			if (p[11] == ATTR_WIN95) {
53179455Sobrien				if (*p & LRFIRST) {
53279455Sobrien					if (shortSum != -1) {
53379455Sobrien						if (!invlfn) {
53479455Sobrien							invlfn = vallfn;
53579455Sobrien							invcl = valcl;
53679455Sobrien						}
53779455Sobrien					}
53879455Sobrien					memset(longName, 0, sizeof longName);
53979455Sobrien					shortSum = p[13];
54079455Sobrien					vallfn = p;
54179455Sobrien					valcl = cl;
54279455Sobrien				} else if (shortSum != p[13]
54379455Sobrien					   || lidx != (*p & LRNOMASK)) {
54479455Sobrien					if (!invlfn) {
54579455Sobrien						invlfn = vallfn;
54679455Sobrien						invcl = valcl;
54779455Sobrien					}
54879455Sobrien					if (!invlfn) {
54979455Sobrien						invlfn = p;
55079455Sobrien						invcl = cl;
55179455Sobrien					}
55279455Sobrien					vallfn = NULL;
55379455Sobrien				}
55479455Sobrien				lidx = *p & LRNOMASK;
55579455Sobrien				t = longName + --lidx * 13;
556209364Sbrian				for (k = 1; k < 11 && t < longName +
557209364Sbrian				    sizeof(longName); k += 2) {
55879455Sobrien					if (!p[k] && !p[k + 1])
55979455Sobrien						break;
56079455Sobrien					*t++ = p[k];
56179455Sobrien					/*
56279455Sobrien					 * Warn about those unusable chars in msdosfs here?	XXX
56379455Sobrien					 */
56479455Sobrien					if (p[k + 1])
56579455Sobrien						t[-1] = '?';
56679455Sobrien				}
56779455Sobrien				if (k >= 11)
56879455Sobrien					for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) {
56979455Sobrien						if (!p[k] && !p[k + 1])
57079455Sobrien							break;
57179455Sobrien						*t++ = p[k];
57279455Sobrien						if (p[k + 1])
57379455Sobrien							t[-1] = '?';
57479455Sobrien					}
57579455Sobrien				if (k >= 26)
57679455Sobrien					for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) {
57779455Sobrien						if (!p[k] && !p[k + 1])
57879455Sobrien							break;
57979455Sobrien						*t++ = p[k];
58079455Sobrien						if (p[k + 1])
58179455Sobrien							t[-1] = '?';
58279455Sobrien					}
58379455Sobrien				if (t >= longName + sizeof(longName)) {
58479455Sobrien					pwarn("long filename too long\n");
58579455Sobrien					if (!invlfn) {
58679455Sobrien						invlfn = vallfn;
58779455Sobrien						invcl = valcl;
58879455Sobrien					}
58979455Sobrien					vallfn = NULL;
59079455Sobrien				}
59179455Sobrien				if (p[26] | (p[27] << 8)) {
59279455Sobrien					pwarn("long filename record cluster start != 0\n");
59379455Sobrien					if (!invlfn) {
59479455Sobrien						invlfn = vallfn;
59579455Sobrien						invcl = cl;
59679455Sobrien					}
59779455Sobrien					vallfn = NULL;
59879455Sobrien				}
59979455Sobrien				continue;	/* long records don't carry further
60079455Sobrien						 * information */
60179455Sobrien			}
60279455Sobrien
60379455Sobrien			/*
60479455Sobrien			 * This is a standard msdosfs directory entry.
60579455Sobrien			 */
60679455Sobrien			memset(&dirent, 0, sizeof dirent);
60779455Sobrien
60879455Sobrien			/*
60979455Sobrien			 * it's a short name record, but we need to know
61079455Sobrien			 * more, so get the flags first.
61179455Sobrien			 */
61279455Sobrien			dirent.flags = p[11];
61379455Sobrien
61479455Sobrien			/*
61579455Sobrien			 * Translate from 850 to ISO here		XXX
61679455Sobrien			 */
61779455Sobrien			for (j = 0; j < 8; j++)
61879455Sobrien				dirent.name[j] = p[j];
61979455Sobrien			dirent.name[8] = '\0';
62079455Sobrien			for (k = 7; k >= 0 && dirent.name[k] == ' '; k--)
62179455Sobrien				dirent.name[k] = '\0';
62279455Sobrien			if (dirent.name[k] != '\0')
62379455Sobrien				k++;
62479455Sobrien			if (dirent.name[0] == SLOT_E5)
62579455Sobrien				dirent.name[0] = 0xe5;
62679455Sobrien
62779455Sobrien			if (dirent.flags & ATTR_VOLUME) {
62879455Sobrien				if (vallfn || invlfn) {
62979455Sobrien					mod |= removede(f, boot, fat,
63079455Sobrien							invlfn ? invlfn : vallfn, p,
63179455Sobrien							invlfn ? invcl : valcl, -1, 0,
63279455Sobrien							fullpath(dir), 2);
63379455Sobrien					vallfn = NULL;
63479455Sobrien					invlfn = NULL;
63579455Sobrien				}
63679455Sobrien				continue;
63779455Sobrien			}
63879455Sobrien
63979455Sobrien			if (p[8] != ' ')
64079455Sobrien				dirent.name[k++] = '.';
64179455Sobrien			for (j = 0; j < 3; j++)
64279455Sobrien				dirent.name[k++] = p[j+8];
64379455Sobrien			dirent.name[k] = '\0';
64479455Sobrien			for (k--; k >= 0 && dirent.name[k] == ' '; k--)
64579455Sobrien				dirent.name[k] = '\0';
64679455Sobrien
64779455Sobrien			if (vallfn && shortSum != calcShortSum(p)) {
64879455Sobrien				if (!invlfn) {
64979455Sobrien					invlfn = vallfn;
65079455Sobrien					invcl = valcl;
65179455Sobrien				}
65279455Sobrien				vallfn = NULL;
65379455Sobrien			}
65479455Sobrien			dirent.head = p[26] | (p[27] << 8);
65579455Sobrien			if (boot->ClustMask == CLUST32_MASK)
65679455Sobrien				dirent.head |= (p[20] << 16) | (p[21] << 24);
65779455Sobrien			dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24);
65879455Sobrien			if (vallfn) {
659203872Skib				strlcpy(dirent.lname, longName,
660203872Skib				    sizeof(dirent.lname));
66179455Sobrien				longName[0] = '\0';
66279455Sobrien				shortSum = -1;
66379455Sobrien			}
66479455Sobrien
66579455Sobrien			dirent.parent = dir;
66679455Sobrien			dirent.next = dir->child;
66779455Sobrien
66879455Sobrien			if (invlfn) {
66979455Sobrien				mod |= k = removede(f, boot, fat,
67079455Sobrien						    invlfn, vallfn ? vallfn : p,
67179455Sobrien						    invcl, vallfn ? valcl : cl, cl,
67279455Sobrien						    fullpath(&dirent), 0);
67379455Sobrien				if (mod & FSFATAL)
67479455Sobrien					return FSFATAL;
67579455Sobrien				if (vallfn
67679455Sobrien				    ? (valcl == cl && vallfn != buffer)
67779455Sobrien				    : p != buffer)
67879455Sobrien					if (k & FSDIRMOD)
67979455Sobrien						mod |= THISMOD;
68079455Sobrien			}
68179455Sobrien
68279455Sobrien			vallfn = NULL; /* not used any longer */
68379455Sobrien			invlfn = NULL;
68479455Sobrien
68579455Sobrien			if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) {
68679455Sobrien				if (dirent.head != 0) {
68779455Sobrien					pwarn("%s has clusters, but size 0\n",
68879455Sobrien					      fullpath(&dirent));
68979455Sobrien					if (ask(1, "Drop allocated clusters")) {
69079455Sobrien						p[26] = p[27] = 0;
69179455Sobrien						if (boot->ClustMask == CLUST32_MASK)
69279455Sobrien							p[20] = p[21] = 0;
69379455Sobrien						clearchain(boot, fat, dirent.head);
69479455Sobrien						dirent.head = 0;
69579455Sobrien						mod |= THISMOD|FSDIRMOD|FSFATMOD;
69679455Sobrien					} else
69779455Sobrien						mod |= FSERROR;
69879455Sobrien				}
69979455Sobrien			} else if (dirent.head == 0
70079455Sobrien				   && !strcmp(dirent.name, "..")
70179455Sobrien				   && dir->parent			/* XXX */
70279455Sobrien				   && !dir->parent->parent) {
70379455Sobrien				/*
70479455Sobrien				 *  Do nothing, the parent is the root
70579455Sobrien				 */
70679455Sobrien			} else if (dirent.head < CLUST_FIRST
70779455Sobrien				   || dirent.head >= boot->NumClusters
70879455Sobrien				   || fat[dirent.head].next == CLUST_FREE
70979455Sobrien				   || (fat[dirent.head].next >= CLUST_RSRVD
71079455Sobrien				       && fat[dirent.head].next < CLUST_EOFS)
71179455Sobrien				   || fat[dirent.head].head != dirent.head) {
71279455Sobrien				if (dirent.head == 0)
71379455Sobrien					pwarn("%s has no clusters\n",
71479455Sobrien					      fullpath(&dirent));
71579455Sobrien				else if (dirent.head < CLUST_FIRST
71679455Sobrien					 || dirent.head >= boot->NumClusters)
71779455Sobrien					pwarn("%s starts with cluster out of range(%u)\n",
71879455Sobrien					      fullpath(&dirent),
71979455Sobrien					      dirent.head);
72079455Sobrien				else if (fat[dirent.head].next == CLUST_FREE)
72179455Sobrien					pwarn("%s starts with free cluster\n",
72279455Sobrien					      fullpath(&dirent));
72379455Sobrien				else if (fat[dirent.head].next >= CLUST_RSRVD)
72479455Sobrien					pwarn("%s starts with cluster marked %s\n",
72579455Sobrien					      fullpath(&dirent),
72679455Sobrien					      rsrvdcltype(fat[dirent.head].next));
72779455Sobrien				else
72879455Sobrien					pwarn("%s doesn't start a cluster chain\n",
72979455Sobrien					      fullpath(&dirent));
73079455Sobrien				if (dirent.flags & ATTR_DIRECTORY) {
73179455Sobrien					if (ask(0, "Remove")) {
73279455Sobrien						*p = SLOT_DELETED;
73379455Sobrien						mod |= THISMOD|FSDIRMOD;
73479455Sobrien					} else
73579455Sobrien						mod |= FSERROR;
73679455Sobrien					continue;
73779455Sobrien				} else {
73879455Sobrien					if (ask(1, "Truncate")) {
73979455Sobrien						p[28] = p[29] = p[30] = p[31] = 0;
74079455Sobrien						p[26] = p[27] = 0;
74179455Sobrien						if (boot->ClustMask == CLUST32_MASK)
74279455Sobrien							p[20] = p[21] = 0;
74379455Sobrien						dirent.size = 0;
74479455Sobrien						mod |= THISMOD|FSDIRMOD;
74579455Sobrien					} else
74679455Sobrien						mod |= FSERROR;
74779455Sobrien				}
74879455Sobrien			}
74979455Sobrien
75079455Sobrien			if (dirent.head >= CLUST_FIRST && dirent.head < boot->NumClusters)
75179455Sobrien				fat[dirent.head].flags |= FAT_USED;
75279455Sobrien
75379455Sobrien			if (dirent.flags & ATTR_DIRECTORY) {
75479455Sobrien				/*
75579455Sobrien				 * gather more info for directories
75679455Sobrien				 */
75779455Sobrien				struct dirTodoNode *n;
75879455Sobrien
75979455Sobrien				if (dirent.size) {
76079455Sobrien					pwarn("Directory %s has size != 0\n",
76179455Sobrien					      fullpath(&dirent));
76279455Sobrien					if (ask(1, "Correct")) {
76379455Sobrien						p[28] = p[29] = p[30] = p[31] = 0;
76479455Sobrien						dirent.size = 0;
76579455Sobrien						mod |= THISMOD|FSDIRMOD;
76679455Sobrien					} else
76779455Sobrien						mod |= FSERROR;
76879455Sobrien				}
76979455Sobrien				/*
77079455Sobrien				 * handle `.' and `..' specially
77179455Sobrien				 */
77279455Sobrien				if (strcmp(dirent.name, ".") == 0) {
77379455Sobrien					if (dirent.head != dir->head) {
77479455Sobrien						pwarn("`.' entry in %s has incorrect start cluster\n",
77579455Sobrien						      fullpath(dir));
77679455Sobrien						if (ask(1, "Correct")) {
77779455Sobrien							dirent.head = dir->head;
77879455Sobrien							p[26] = (u_char)dirent.head;
77979455Sobrien							p[27] = (u_char)(dirent.head >> 8);
78079455Sobrien							if (boot->ClustMask == CLUST32_MASK) {
78179455Sobrien								p[20] = (u_char)(dirent.head >> 16);
78279455Sobrien								p[21] = (u_char)(dirent.head >> 24);
78379455Sobrien							}
78479455Sobrien							mod |= THISMOD|FSDIRMOD;
78579455Sobrien						} else
78679455Sobrien							mod |= FSERROR;
78779455Sobrien					}
78879455Sobrien					continue;
78979455Sobrien				}
79079455Sobrien				if (strcmp(dirent.name, "..") == 0) {
79179455Sobrien					if (dir->parent) {		/* XXX */
79279455Sobrien						if (!dir->parent->parent) {
79379455Sobrien							if (dirent.head) {
79479455Sobrien								pwarn("`..' entry in %s has non-zero start cluster\n",
79579455Sobrien								      fullpath(dir));
79679455Sobrien								if (ask(1, "Correct")) {
79779455Sobrien									dirent.head = 0;
79879455Sobrien									p[26] = p[27] = 0;
79979455Sobrien									if (boot->ClustMask == CLUST32_MASK)
80079455Sobrien										p[20] = p[21] = 0;
80179455Sobrien									mod |= THISMOD|FSDIRMOD;
80279455Sobrien								} else
80379455Sobrien									mod |= FSERROR;
80479455Sobrien							}
80579455Sobrien						} else if (dirent.head != dir->parent->head) {
80679455Sobrien							pwarn("`..' entry in %s has incorrect start cluster\n",
80779455Sobrien							      fullpath(dir));
80879455Sobrien							if (ask(1, "Correct")) {
80979455Sobrien								dirent.head = dir->parent->head;
81079455Sobrien								p[26] = (u_char)dirent.head;
81179455Sobrien								p[27] = (u_char)(dirent.head >> 8);
81279455Sobrien								if (boot->ClustMask == CLUST32_MASK) {
81379455Sobrien									p[20] = (u_char)(dirent.head >> 16);
81479455Sobrien									p[21] = (u_char)(dirent.head >> 24);
81579455Sobrien								}
81679455Sobrien								mod |= THISMOD|FSDIRMOD;
81779455Sobrien							} else
81879455Sobrien								mod |= FSERROR;
81979455Sobrien						}
82079455Sobrien					}
82179455Sobrien					continue;
82279455Sobrien				}
82379455Sobrien
82479455Sobrien				/* create directory tree node */
82579455Sobrien				if (!(d = newDosDirEntry())) {
826241806Suqs					perr("No space for directory");
82779455Sobrien					return FSFATAL;
82879455Sobrien				}
82979455Sobrien				memcpy(d, &dirent, sizeof(struct dosDirEntry));
83079455Sobrien				/* link it into the tree */
83179455Sobrien				dir->child = d;
83279455Sobrien
83379455Sobrien				/* Enter this directory into the todo list */
83479455Sobrien				if (!(n = newDirTodo())) {
835241806Suqs					perr("No space for todo list");
83679455Sobrien					return FSFATAL;
83779455Sobrien				}
83879455Sobrien				n->next = pendingDirectories;
83979455Sobrien				n->dir = d;
84079455Sobrien				pendingDirectories = n;
84179455Sobrien			} else {
84279455Sobrien				mod |= k = checksize(boot, fat, p, &dirent);
84379455Sobrien				if (k & FSDIRMOD)
84479455Sobrien					mod |= THISMOD;
84579455Sobrien			}
84679455Sobrien			boot->NumFiles++;
84779455Sobrien		}
848203872Skib
849203872Skib		if (!(boot->flags & FAT32) && !dir->parent)
850203872Skib			break;
851203872Skib
85279455Sobrien		if (mod & THISMOD) {
85379455Sobrien			last *= 32;
85479455Sobrien			if (lseek(f, off, SEEK_SET) != off
85579455Sobrien			    || write(f, buffer, last) != last) {
856241806Suqs				perr("Unable to write directory");
85779455Sobrien				return FSFATAL;
85879455Sobrien			}
85979455Sobrien			mod &= ~THISMOD;
86079455Sobrien		}
86179455Sobrien	} while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters);
86279455Sobrien	if (invlfn || vallfn)
86379455Sobrien		mod |= removede(f, boot, fat,
86479455Sobrien				invlfn ? invlfn : vallfn, p,
86579455Sobrien				invlfn ? invcl : valcl, -1, 0,
86679455Sobrien				fullpath(dir), 1);
867203872Skib
868203872Skib	/* The root directory of non fat32 filesystems is in a special
869203872Skib	 * area and may have been modified above without being written out.
870203872Skib	 */
871203872Skib	if ((mod & FSDIRMOD) && !(boot->flags & FAT32) && !dir->parent) {
872203872Skib		last *= 32;
873203872Skib		if (lseek(f, off, SEEK_SET) != off
874203872Skib		    || write(f, buffer, last) != last) {
875241806Suqs			perr("Unable to write directory");
876203872Skib			return FSFATAL;
877203872Skib		}
878203872Skib		mod &= ~THISMOD;
879203872Skib	}
88079455Sobrien	return mod & ~THISMOD;
88179455Sobrien}
88279455Sobrien
88379455Sobrienint
88492839SimphandleDirTree(int dosfs, struct bootblock *boot, struct fatEntry *fat)
88579455Sobrien{
88679455Sobrien	int mod;
88779455Sobrien
88879455Sobrien	mod = readDosDirSection(dosfs, boot, fat, rootDir);
88979455Sobrien	if (mod & FSFATAL)
89079455Sobrien		return FSFATAL;
89179455Sobrien
89279455Sobrien	/*
89379455Sobrien	 * process the directory todo list
89479455Sobrien	 */
89579455Sobrien	while (pendingDirectories) {
89679455Sobrien		struct dosDirEntry *dir = pendingDirectories->dir;
89779455Sobrien		struct dirTodoNode *n = pendingDirectories->next;
89879455Sobrien
89979455Sobrien		/*
90079455Sobrien		 * remove TODO entry now, the list might change during
90179455Sobrien		 * directory reads
90279455Sobrien		 */
90379455Sobrien		freeDirTodo(pendingDirectories);
90479455Sobrien		pendingDirectories = n;
90579455Sobrien
90679455Sobrien		/*
90779455Sobrien		 * handle subdirectory
90879455Sobrien		 */
90979455Sobrien		mod |= readDosDirSection(dosfs, boot, fat, dir);
91079455Sobrien		if (mod & FSFATAL)
91179455Sobrien			return FSFATAL;
91279455Sobrien	}
91379455Sobrien
91479455Sobrien	return mod;
91579455Sobrien}
91679455Sobrien
91779455Sobrien/*
91879455Sobrien * Try to reconnect a FAT chain into dir
91979455Sobrien */
92079455Sobrienstatic u_char *lfbuf;
92179455Sobrienstatic cl_t lfcl;
92279455Sobrienstatic off_t lfoff;
92379455Sobrien
92479455Sobrienint
92592839Simpreconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t head)
92679455Sobrien{
92779455Sobrien	struct dosDirEntry d;
92879455Sobrien	u_char *p;
92979455Sobrien
93079455Sobrien	if (!ask(1, "Reconnect"))
93179455Sobrien		return FSERROR;
93279455Sobrien
93379455Sobrien	if (!lostDir) {
93479455Sobrien		for (lostDir = rootDir->child; lostDir; lostDir = lostDir->next) {
93579455Sobrien			if (!strcmp(lostDir->name, LOSTDIR))
93679455Sobrien				break;
93779455Sobrien		}
93879455Sobrien		if (!lostDir) {		/* Create LOSTDIR?		XXX */
93979455Sobrien			pwarn("No %s directory\n", LOSTDIR);
94079455Sobrien			return FSERROR;
94179455Sobrien		}
94279455Sobrien	}
94379455Sobrien	if (!lfbuf) {
94479455Sobrien		lfbuf = malloc(boot->ClusterSize);
94579455Sobrien		if (!lfbuf) {
946241806Suqs			perr("No space for buffer");
94779455Sobrien			return FSFATAL;
94879455Sobrien		}
94979455Sobrien		p = NULL;
95079455Sobrien	} else
95179455Sobrien		p = lfbuf;
95279455Sobrien	while (1) {
95379455Sobrien		if (p)
95479455Sobrien			for (; p < lfbuf + boot->ClusterSize; p += 32)
95579455Sobrien				if (*p == SLOT_EMPTY
95679455Sobrien				    || *p == SLOT_DELETED)
95779455Sobrien					break;
95879455Sobrien		if (p && p < lfbuf + boot->ClusterSize)
95979455Sobrien			break;
96079455Sobrien		lfcl = p ? fat[lfcl].next : lostDir->head;
96179455Sobrien		if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) {
96279455Sobrien			/* Extend LOSTDIR?				XXX */
96379455Sobrien			pwarn("No space in %s\n", LOSTDIR);
96479455Sobrien			return FSERROR;
96579455Sobrien		}
96679455Sobrien		lfoff = lfcl * boot->ClusterSize
967203874Skib		    + boot->ClusterOffset * boot->bpbBytesPerSec;
96879455Sobrien		if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
969203872Skib		    || (size_t)read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
970241806Suqs			perr("could not read LOST.DIR");
97179455Sobrien			return FSFATAL;
97279455Sobrien		}
97379455Sobrien		p = lfbuf;
97479455Sobrien	}
97579455Sobrien
97679455Sobrien	boot->NumFiles++;
97779455Sobrien	/* Ensure uniqueness of entry here!				XXX */
97879455Sobrien	memset(&d, 0, sizeof d);
97979455Sobrien	(void)snprintf(d.name, sizeof(d.name), "%u", head);
98079455Sobrien	d.flags = 0;
98179455Sobrien	d.head = head;
98279455Sobrien	d.size = fat[head].length * boot->ClusterSize;
98379455Sobrien
98479455Sobrien	memset(p, 0, 32);
98579455Sobrien	memset(p, ' ', 11);
98679455Sobrien	memcpy(p, d.name, strlen(d.name));
98779455Sobrien	p[26] = (u_char)d.head;
98879455Sobrien	p[27] = (u_char)(d.head >> 8);
98979455Sobrien	if (boot->ClustMask == CLUST32_MASK) {
99079455Sobrien		p[20] = (u_char)(d.head >> 16);
99179455Sobrien		p[21] = (u_char)(d.head >> 24);
99279455Sobrien	}
99379455Sobrien	p[28] = (u_char)d.size;
99479455Sobrien	p[29] = (u_char)(d.size >> 8);
99579455Sobrien	p[30] = (u_char)(d.size >> 16);
99679455Sobrien	p[31] = (u_char)(d.size >> 24);
99779455Sobrien	fat[head].flags |= FAT_USED;
99879455Sobrien	if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
999203872Skib	    || (size_t)write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
1000241806Suqs		perr("could not write LOST.DIR");
100179455Sobrien		return FSFATAL;
100279455Sobrien	}
100379455Sobrien	return FSDIRMOD;
100479455Sobrien}
100579455Sobrien
100679455Sobrienvoid
100792839Simpfinishlf(void)
100879455Sobrien{
100979455Sobrien	if (lfbuf)
101079455Sobrien		free(lfbuf);
101179455Sobrien	lfbuf = NULL;
101279455Sobrien}
1013