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