Moved fat-handler sources to rom/filesys.
[AROS.git] / rom / filesys / fat / names.c
bloba44433110a45e2f28a5522f7b3169c2fac760cdc
1 /*
2 * fat.handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2011 The AROS Development Team
7 * This program is free software; you can redistribute it and/or modify it
8 * under the same terms as AROS itself.
10 * $Id$
13 #include <aros/macros.h>
14 #include <exec/types.h>
15 #include <dos/dos.h>
16 #include <proto/exec.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <ctype.h>
22 #include "fat_fs.h"
23 #include "fat_protos.h"
25 #define DEBUG DEBUG_NAMES
26 #include "debug.h"
28 LONG GetDirEntryShortName(struct DirEntry *de, STRPTR name, ULONG *len) {
29 int i;
30 UBYTE *raw, *c;
32 /* make sure the entry is good */
33 raw = de->e.entry.name;
34 if (raw[0] == 0x00 || raw[0] == 0xe5 || raw[0] == 0x20) {
35 D(bug("[fat] entry name has first byte 0x%02x, returning empty short name\n", raw[0]));
36 *name = '\0';
37 len = 0;
38 return 0;
41 D(bug("[fat] extracting short name for name '"); RawPutChars(raw, 11);
42 bug("' (index %ld)\n", de->index));
44 /* copy the chars into the return string */
45 c = name;
46 for (i = 0; i < 11; i++) {
47 *c = tolower(raw[i]);
50 * fat names are weird. the name FOO.BAR is stored as "FOO BAR".
51 * in that case we've already copied in all the spaces, and we have to
52 * backup and insert the dot.
54 * note that spaces (0x20) are allowed in the filename, just not as the
55 * first char. see FATdoc 1.03 p24 for the details. most people don't
56 * know that about fat names. the point of this is to say that we
57 * can't just flick forward in our copying at the first sight of a
58 * space, it's technically incorrect.
60 if (i == 7) {
61 /* backtrack to first non-space. this is safe because the first
62 * char won't be space, we checked above */
63 while (*c == 0x20) c--;
65 /* forward one and drop in the dot */
66 c++;
67 *c = '.';
70 /* move along */
71 c++;
74 /* remove any trailing spaces, and perhaps a trailing . */
75 while (c[-1] == 0x20) c--;
76 if (c[-1] == '.') c--;
78 /* apply official hack for Japanese names */
79 if (*name == 0x05) *name = 0xe5;
81 /* all done */
82 *c = '\0';
83 *len = strlen(name);
85 D(bug("[fat] extracted short name '"); RawPutChars(name, *len); bug("'\n"));
87 return 0;
90 LONG GetDirEntryLongName(struct DirEntry *short_de, STRPTR name, ULONG *len) {
91 UBYTE buf[256];
92 int i;
93 UBYTE *raw, *c;
94 UBYTE checksum;
95 struct DirHandle dh;
96 struct DirEntry de;
97 LONG index;
98 UBYTE order;
99 LONG err;
101 /* make sure the entry is good */
102 raw = short_de->e.entry.name;
103 if (raw[0] == 0x00 || raw[0] == 0xe5 || raw[0] == 0x20) {
104 D(bug("[fat] entry name has first byte 0x%02x, returning empty long name\n", raw[0]));
105 *name = '\0';
106 len = 0;
107 return 0;
110 D(bug("[fat] looking for long name for name '%.11s' (index %ld)\n", raw, short_de->index));
112 /* compute the short name checksum. this value is held in every associated
113 * long name entry to help us identify it. see FATdoc 1.03 p28 */
114 CALC_SHORT_NAME_CHECKSUM(raw, checksum);
116 D(bug("[fat] short name checksum is 0x%02x\n", checksum));
118 /* get a handle on the directory */
119 InitDirHandle(short_de->sb, short_de->cluster, &dh, FALSE);
121 /* loop over the long name entries */
122 c = buf;
123 order = 1;
124 index = short_de->index - 1;
125 while (index >= 0) {
126 D(bug("[fat] looking for long name order 0x%02x in entry %ld\n", order, index));
128 if ((err = GetDirEntry(&dh, index, &de)) != 0)
129 break;
131 /* make sure it's valid */
132 if (!((de.e.entry.attr & ATTR_LONG_NAME_MASK) == ATTR_LONG_NAME) ||
133 (de.e.long_entry.order & ~0x40) != order ||
134 de.e.long_entry.checksum != checksum) {
136 D(bug("[fat] bad long name entry %ld (attr 0x%02x order 0x%02x checksum 0x%02x)\n",
137 index, de.e.entry.attr, de.e.long_entry.order, de.e.long_entry.checksum));
139 err = ERROR_OBJECT_NOT_FOUND;
140 break;
143 /* copy the characters into the name buffer. note that filename
144 * entries can have null-termination, but don't have to. we take the
145 * easy way out - copy everything, and bolt on an additional null just
146 * in case. */
148 /* XXX these are in UTF-16, but we're just taking the bottom byte.
149 * that works well enough but is still a hack. if our dos ever
150 * supports unicode this should be revisited */
151 for (i = 0; i < 5; i++) {
152 *c = glob->from_unicode[AROS_LE2WORD(de.e.long_entry.name1[i])];
153 c++;
155 for (i = 0; i < 6; i++) {
156 *c = glob->from_unicode[AROS_LE2WORD(de.e.long_entry.name2[i])];
157 c++;
159 for (i = 0; i < 2; i++) {
160 *c = glob->from_unicode[AROS_LE2WORD(de.e.long_entry.name3[i])];
161 c++;
164 /* if this is the last entry, clean up and get us out of here */
165 if (de.e.long_entry.order & 0x40) {
166 *c = 0;
167 *len = strlen((char *) buf);
168 CopyMem(buf, name, *len);
170 D(bug("[fat] extracted long name '%s'\n", buf));
172 ReleaseDirHandle(&dh);
174 return 0;
177 index--;
178 order++;
181 ReleaseDirHandle(&dh);
183 D(bug("[fat] long name construction failed\n"));
185 return ERROR_OBJECT_NOT_FOUND;
188 /* set the name of an entry. this will set the long name too. it assumes
189 * that there is room before the entry to store the long filename. if there
190 * isn't the whole thing will fail */
191 LONG SetDirEntryName(struct DirEntry *short_de, STRPTR name, ULONG len) {
192 UBYTE basis[11];
193 ULONG nlong;
194 ULONG src, dst, i, left;
195 ULONG seq = 0, cur = 0;
196 UBYTE tail[8];
197 struct DirHandle dh;
198 struct DirEntry de;
199 LONG err;
200 UBYTE checksum;
201 UBYTE order;
203 D(bug("[fat] setting name for entry index %ld to '", short_de->index);
204 RawPutChars(name, len); bug("'\n"));
206 nlong = NumLongNameEntries(name, len);
207 D(bug("[fat] name requires %ld long name entries\n", nlong));
209 /* first we generate the "basis name" of the passed in name. XXX we just
210 * take the first eight characters and any three-letter extension and mash
211 * them together. FATDoc 1.03 p30-31 outlines a more comprehensive
212 * algorithm that handles unicode, but we're not doing unicode yet */
214 dst = 0;
216 /* strip off leading spaces and periods */
217 for (src = 0; src < len; src++)
218 if (name[src] != ' ' && name[src] != '.')
219 break;
221 /* copy the first eight chars in, ignoring spaces and stopping at period */
222 if (src != len) {
223 while (src < len && dst < 8 && name[src] != '.') {
224 if (name[src] != ' ') {
225 basis[dst] = toupper(name[src]);
226 if (basis[dst] != name[src])
227 seq = 1;
228 dst++;
230 src++;
234 /* if there was more bytes available, then we need a tail later */
235 if (src < len && name[src] != '.')
236 seq = 1;
238 /* make a note of the length of the left side. this gets used further down
239 * to determine the position to add the tail */
240 left = dst;
242 /* remember the current value of src for the multiple-dot check below */
243 i = src;
245 /* pad the rest of the left side with spaces */
246 for (; dst < 8; dst++)
247 basis[dst] = ' ';
249 /* now go to the end and track back looking for a dot */
250 for (src = len-1; src >= 0 && name[src] != '.'; src--);
252 /* found it */
253 if (src != 0) {
254 /* if this isn't the same dot we found earlier, then we need a tail */
255 if (src != i)
256 seq = 1;
258 /* first char after the dot */
259 src++;
261 /* copy it in */
262 while(src < len && dst < 11) {
263 if (name[src] != ' ') {
264 basis[dst] = toupper(name[src]);
265 if (basis[dst] != name[src])
266 seq = 1;
267 dst++;
269 src++;
273 /* if there were more bytes available, then we'll need a tail later */
274 if (src < len)
275 seq = 1;
277 /* pad the rest of the right side with spaces */
278 for (; dst < 11; dst++)
279 basis[dst] = ' ';
281 D(bug("[fat] basis name is '%.11s'\n", basis));
283 /* get a fresh handle on the current directory */
284 InitDirHandle(short_de->sb, short_de->cluster, &dh, FALSE);
286 /* if the name will require one or more entries, then our basis name is
287 * actually some conversion of the real name, and we have to look to make
288 * sure it's not in use */
289 if (nlong > 0) {
290 D(bug("[fat] searching for basis name to confirm that it's not in use\n"));
292 /* loop over the entries and compare them with the basis until we find
293 * a gap */
294 while (1) {
295 /* build a new tail if necessary */
296 if (cur != seq) {
297 sprintf(tail, "~%lu", (unsigned long)seq);
298 while (left + strlen(tail) > 8) left--;
299 CopyMem(tail, &basis[left], strlen(tail));
300 cur = seq;
302 D(bug("[fat] new basis name is '%.11s'\n", basis));
305 /* get the next entry, and bail if we hit the end of the dir */
306 if ((err = GetNextDirEntry(&dh, &de)) == ERROR_OBJECT_NOT_FOUND)
307 break;
309 /* abort on any other error */
310 if (err != 0) {
311 ReleaseDirHandle(&dh);
312 return err;
315 /* compare the two names */
316 D(bug("[fat] comparing '%.11s' with '%.11s'\n", basis,
317 de.e.entry.name));
318 for (i = 0; i < 11; i++)
319 if (de.e.entry.name[i] != basis[i])
320 break;
322 /* if we reached the end, then our current basis is in use and we
323 * need to generate a new one and start again */
324 if (i == 11) {
325 seq++;
326 RESET_DIRHANDLE(&dh)
330 D(bug("[fat] basis name '%.11s' not in use, using it\n", basis));
333 /* copy the new name into the original entry. we don't write it out -
334 * we'll leave that for the caller to do, it's his entry */
335 CopyMem(basis, short_de->e.entry.name, 11);
337 /* we can stop here if no long name is required */
338 if (nlong == 0) {
339 D(bug("[fat] copied short name and long name not required, we're done\n"));
340 ReleaseDirHandle(&dh);
341 return 0;
344 /* compute the short name checksum */
345 CALC_SHORT_NAME_CHECKSUM(basis, checksum);
347 D(bug("[fat] short name checksum is 0x%02x\n", checksum));
349 /* now we loop back over the previous entries and fill them in with
350 * long name components */
351 src = 0;
352 de.index = short_de->index;
353 order = 1;
354 while (src < len) {
355 /* get the previous entry */
356 if ((err = GetDirEntry(&dh, de.index-1, &de)) != 0) {
357 ReleaseDirHandle(&dh);
358 return err;
361 /* it must be unused (or end of directory) */
362 if (de.e.entry.name[0] != 0xe5 && de.e.entry.name[0] != 0x00) {
363 D(bug("[fat] index %ld appears to be in use, aborting long name\n", de.index));
365 /* clean up any long name entries we already added */
366 while ((err = GetDirEntry(&dh, de.index+1, &de)) == 0 &&
367 (de.e.entry.attr & ATTR_LONG_NAME_MASK)) {
368 de.e.entry.name[0] = 0xe5;
369 if ((err = UpdateDirEntry(&de)) != 0) {
370 /* XXX corrupt */
371 ReleaseDirHandle(&dh);
372 return err;
376 ReleaseDirHandle(&dh);
377 return ERROR_NO_FREE_STORE;
380 D(bug("[fat] building long name entry %ld\n", de.index));
382 /* copy bytes in */
383 for (dst = 0; dst < 5; dst++) {
384 de.e.long_entry.name1[dst] =
385 src < len ? AROS_WORD2LE(glob->to_unicode[name[src++]]) :
386 (src++ == len ? 0x0000 : 0xffff);
389 for (dst = 0; dst < 6; dst++) {
390 de.e.long_entry.name2[dst] =
391 src < len ? AROS_WORD2LE(glob->to_unicode[name[src++]]) :
392 (src++ == len ? 0x0000 : 0xffff);
395 for (dst = 0; dst < 2; dst++) {
396 de.e.long_entry.name3[dst] =
397 src < len ? AROS_WORD2LE(glob->to_unicode[name[src++]]) :
398 (src++ == len ? 0x0000 : 0xffff);
401 /* setup the rest of the entry */
402 de.e.long_entry.order = order++;
403 de.e.long_entry.attr = ATTR_LONG_NAME;
404 de.e.long_entry.type = 0;
405 de.e.long_entry.checksum = checksum;
406 de.e.long_entry.first_cluster_lo = 0;
408 /* if we've reached the end then this is the last entry */
409 if (src >= len)
410 de.e.long_entry.order |= 0x40;
412 /* write the entry out */
413 UpdateDirEntry(&de);
415 D(bug("[fat] wrote long name entry %ld order 0x%02x\n", de.index, de.e.long_entry.order));
418 ReleaseDirHandle(&dh);
420 D(bug("[fat] successfully wrote short & long names\n"));
422 /* Set hidden flags on .info files */
423 if (strcmp(name + len - 5, ".info") == 0)
424 short_de->e.entry.attr |= ATTR_HIDDEN;
426 return 0;
429 /* return the number of long name entries that are required to store this name */
430 ULONG NumLongNameEntries(STRPTR name, ULONG len) {
431 #if UPPERCASE_SHORT_NAMES
432 ULONG i, left;
434 /* XXX because we don't handle unicode this is pretty simple - thirteen
435 * characters per long entry. if we ever support unicode, then this
436 * function will need to be changed to deal with each character and keep a
437 * running total */
439 /* if the name is standard 8.3 (or less) then we don't need any long name
440 * entries - the name can be contained within the standard entry */
441 if (len <= 12) {
442 left = 0;
444 for (i = 0; i < 8 && i < len; i++) {
445 if (name[i] == '.')
446 break;
447 if (name[i] != toupper(name[i]))
448 break;
449 left++;
452 if (i == len)
453 return 0;
455 if (name[i] == '.') {
456 for (i = 0; i < 3 && left + 1 + i < len; i++)
457 if (name[left+1+i] != toupper(name[left+1+i]))
458 break;
460 if (left + 1 + i == len)
461 return 0;
464 #endif
466 return ((len-1) / 13) + 1;