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