Import 2.1.118
[davej-history.git] / fs / fat / dir.c
blob1c44247a00a5e0227b831614d13b6f404f6af076
1 /*
2 * linux/fs/fat/dir.c
4 * directory handling functions for fat-based filesystems
6 * Written 1992,1993 by Werner Almesberger
8 * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
10 * VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu>
11 * Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
14 #define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
16 #include <linux/version.h>
17 #include <linux/fs.h>
18 #include <linux/msdos_fs.h>
19 #include <linux/nls.h>
20 #include <linux/kernel.h>
21 #include <linux/errno.h>
22 #include <linux/stat.h>
23 #include <linux/string.h>
24 #include <linux/ioctl.h>
25 #include <linux/dirent.h>
26 #include <linux/mm.h>
28 #include <asm/uaccess.h>
30 #include "msbuffer.h"
32 #define PRINTK(X)
34 static ssize_t fat_dir_read(struct file * filp, char * buf,
35 size_t count, loff_t *ppos)
37 return -EISDIR;
40 struct file_operations fat_dir_operations = {
41 NULL, /* lseek - default */
42 fat_dir_read, /* read */
43 NULL, /* write - bad */
44 fat_readdir, /* readdir */
45 NULL, /* select v2.0.x/poll v2.1.x - default */
46 fat_dir_ioctl, /* ioctl - default */
47 NULL, /* mmap */
48 NULL, /* no special open code */
49 NULL, /* flush */
50 NULL, /* no special release code */
51 file_fsync /* fsync */
55 * Convert Unicode 16 to UTF8, translated Unicode, or ASCII.
56 * If uni_xlate is enabled and we
57 * can't get a 1:1 conversion, use a colon as an escape character since
58 * it is normally invalid on the vfat filesystem. The following three
59 * characters are a sort of uuencoded 16 bit Unicode value. This lets
60 * us do a full dump and restore of Unicode filenames. We could get
61 * into some trouble with long Unicode names, but ignore that right now.
63 static int
64 uni16_to_x8(unsigned char *ascii, unsigned char *uni, int uni_xlate,
65 struct nls_table *nls)
67 unsigned char *ip, *op;
68 unsigned char ch, cl;
69 unsigned char *uni_page;
70 unsigned short val;
72 ip = uni;
73 op = ascii;
75 while (*ip || ip[1]) {
76 cl = *ip++;
77 ch = *ip++;
79 uni_page = nls->page_uni2charset[ch];
80 if (uni_page && uni_page[cl]) {
81 *op++ = uni_page[cl];
82 } else {
83 if (uni_xlate == 1) {
84 *op++ = ':';
85 val = (cl << 8) + ch;
86 op[2] = fat_uni2esc[val & 0x3f];
87 val >>= 6;
88 op[1] = fat_uni2esc[val & 0x3f];
89 val >>= 6;
90 *op = fat_uni2esc[val & 0x3f];
91 op += 3;
92 } else {
93 *op++ = '?';
97 *op = 0;
98 return (op - ascii);
101 #if 0
102 static void dump_de(struct msdos_dir_entry *de)
104 int i;
105 unsigned char *p = (unsigned char *) de;
106 printk("[");
108 for (i = 0; i < 32; i++, p++) {
109 printk("%02x ", *p);
111 printk("]\n");
113 #endif
114 int fat_readdirx(
115 struct inode *inode,
116 struct file *filp,
117 void *dirent,
118 fat_filldir_t fat_filldir,
119 filldir_t filldir,
120 int shortnames,
121 int longnames,
122 int both)
124 struct super_block *sb = inode->i_sb;
125 int ino,i,i2,last;
126 char c;
127 struct buffer_head *bh;
128 struct msdos_dir_entry *de;
129 unsigned long oldpos = filp->f_pos;
130 unsigned long spos;
131 int is_long;
132 char longname[275];
133 unsigned char long_len = 0; /* Make compiler warning go away */
134 unsigned char alias_checksum = 0; /* Make compiler warning go away */
135 unsigned char long_slots = 0;
136 int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
137 int utf8 = MSDOS_SB(sb)->options.utf8;
138 unsigned char *unicode = NULL;
139 struct nls_table *nls = MSDOS_SB(sb)->nls_io;
141 if (!inode || !S_ISDIR(inode->i_mode))
142 return -EBADF;
143 /* Fake . and .. for the root directory. */
144 if (inode->i_ino == MSDOS_ROOT_INO) {
145 while (oldpos < 2) {
146 if (fat_filldir(filldir, dirent, "..", oldpos+1, 0, oldpos, oldpos, 0, MSDOS_ROOT_INO) < 0)
147 return 0;
148 oldpos++;
149 filp->f_pos++;
151 if (oldpos == 2)
152 filp->f_pos = 0;
154 if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1))
155 return -ENOENT;
157 bh = NULL;
158 longname[0] = longname[1] = 0;
159 is_long = 0;
160 ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
161 while (ino > -1) {
162 #if 0
163 dump_de(de);
164 #endif
165 /* Check for long filename entry */
166 if (MSDOS_SB(sb)->options.isvfat && (de->name[0] == (__s8) DELETED_FLAG)) {
167 is_long = 0;
168 oldpos = filp->f_pos;
169 } else if (MSDOS_SB(sb)->options.isvfat && de->attr == ATTR_EXT) {
170 int get_new_entry;
171 struct msdos_dir_slot *ds;
172 int offset;
173 unsigned char id;
174 unsigned char slot;
175 unsigned char slots = 0;
177 if (!unicode) {
178 unicode = (unsigned char *)
179 __get_free_page(GFP_KERNEL);
180 if (!unicode)
181 return -ENOMEM;
184 offset = 0;
185 ds = (struct msdos_dir_slot *) de;
186 id = ds->id;
187 if (id & 0x40) {
188 slots = id & ~0x40;
189 long_slots = slots;
190 is_long = 1;
191 alias_checksum = ds->alias_checksum;
194 get_new_entry = 1;
195 slot = slots;
196 while (slot > 0) {
197 PRINTK(("1. get_new_entry: %d\n", get_new_entry));
198 if (ds->attr != ATTR_EXT) {
199 is_long = 0;
200 get_new_entry = 0;
201 break;
203 if ((ds->id & ~0x40) != slot) {
204 is_long = 0;
205 break;
207 if (ds->alias_checksum != alias_checksum) {
208 is_long = 0;
209 break;
211 slot--;
212 offset = slot * 26;
213 PRINTK(("2. get_new_entry: %d\n", get_new_entry));
214 memcpy(&unicode[offset], ds->name0_4, 10);
215 offset += 10;
216 memcpy(&unicode[offset], ds->name5_10, 12);
217 offset += 12;
218 memcpy(&unicode[offset], ds->name11_12, 4);
219 offset += 4;
221 if (ds->id & 0x40) {
222 unicode[offset] = 0;
223 unicode[offset+1] = 0;
225 if (slot > 0) {
226 ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
227 PRINTK(("4. get_new_entry: %d\n", get_new_entry));
228 if (ino == -1) {
229 is_long = 0;
230 get_new_entry = 0;
231 break;
233 ds = (struct msdos_dir_slot *) de;
235 PRINTK(("5. get_new_entry: %d\n", get_new_entry));
237 } else if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
238 char bufname[14];
239 char *ptname = bufname;
240 int dotoffset = 0;
241 int was_long = is_long;
243 if (is_long) {
244 unsigned char sum;
245 for (sum = 0, i = 0; i < 11; i++) {
246 sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
249 if (sum != alias_checksum) {
250 PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum));
251 is_long = 0;
252 long_slots = 0;
254 if (utf8) {
255 long_len = utf8_wcstombs(longname, (__u16 *) unicode, sizeof(longname));
256 } else {
257 long_len = uni16_to_x8(longname, unicode, uni_xlate, nls);
261 if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
262 bufname[0] = '.';
263 dotoffset = 1;
264 ptname = bufname+1;
266 for (i = 0, last = 0; i < 8; i++) {
267 if (!(c = de->name[i])) break;
268 if (c >= 'A' && c <= 'Z') c += 32;
269 /* see namei.c, msdos_format_name */
270 if (c == 0x05) c = 0xE5;
271 if (c != ' ')
272 last = i+1;
273 ptname[i] = c;
275 i = last;
276 ptname[i] = '.';
277 i++;
278 for (i2 = 0; i2 < 3; i2++) {
279 if (!(c = de->ext[i2])) break;
280 if (c >= 'A' && c <= 'Z') c += 32;
281 if (c != ' ')
282 last = i+1;
283 ptname[i] = c;
284 i++;
286 if ((i = last) != 0) {
287 if (!strcmp(de->name,MSDOS_DOT))
288 ino = inode->i_ino;
289 else if (!strcmp(de->name,MSDOS_DOTDOT))
290 ino = fat_parent_ino(inode,0);
292 if (shortnames || !is_long) {
293 if (both)
294 bufname[i+dotoffset] = '\0';
295 spos = oldpos;
296 if (was_long) {
297 spos = filp->f_pos - sizeof(struct msdos_dir_entry);
298 } else {
299 long_slots = 0;
301 if (fat_filldir(filldir, dirent, bufname, i+dotoffset, 0, oldpos, spos, long_slots, ino) < 0) {
302 filp->f_pos = oldpos;
303 break;
306 if (is_long && longnames) {
307 if (both) {
308 memcpy(&longname[long_len+1], bufname, i+dotoffset);
309 long_len += i+dotoffset;
311 spos = filp->f_pos - sizeof(struct msdos_dir_entry);
312 if (fat_filldir(filldir, dirent, longname, long_len, 1, oldpos, spos, long_slots, ino) < 0) {
313 filp->f_pos = oldpos;
314 break;
317 oldpos = filp->f_pos;
319 is_long = 0;
320 } else {
321 is_long = 0;
322 oldpos = filp->f_pos;
324 ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
326 if (bh)
327 fat_brelse(sb, bh);
328 if (unicode) {
329 free_page((unsigned long) unicode);
331 return 0;
334 static int fat_filldir(
335 filldir_t filldir,
336 void * buf,
337 const char * name,
338 int name_len,
339 int is_long,
340 off_t offset,
341 off_t short_offset,
342 int long_slots,
343 ino_t ino)
345 return filldir(buf, name, name_len, offset, ino);
348 int fat_readdir(
349 struct file *filp,
350 void *dirent,
351 filldir_t filldir)
353 struct inode *inode = filp->f_dentry->d_inode;
354 return fat_readdirx(inode, filp, dirent, fat_filldir, filldir,
355 0, 1, 0);
358 static int vfat_ioctl_fill(
359 filldir_t filldir,
360 void * buf,
361 const char * name,
362 int name_len,
363 int is_long,
364 off_t offset,
365 off_t short_offset,
366 int long_slots,
367 ino_t ino)
369 struct dirent *d1 = (struct dirent *)buf;
370 struct dirent *d2 = d1 + 1;
371 int len, slen;
372 int dotdir;
374 get_user(len, &d1->d_reclen);
375 if (len != 0) {
376 return -1;
379 if ((name_len == 1 && name[0] == '.') ||
380 (name_len == 2 && name[0] == '.' && name[1] == '.')) {
381 dotdir = 1;
382 len = name_len;
383 } else {
384 dotdir = 0;
385 len = strlen(name);
387 if (len != name_len) {
388 copy_to_user(d2->d_name, name, len);
389 put_user(0, d2->d_name + len);
390 put_user(len, &d2->d_reclen);
391 put_user(ino, &d2->d_ino);
392 put_user(offset, &d2->d_off);
393 slen = name_len - len;
394 copy_to_user(d1->d_name, name+len+1, slen);
395 put_user(0, d1->d_name+slen);
396 put_user(slen, &d1->d_reclen);
397 } else {
398 put_user(0, d2->d_name);
399 put_user(0, &d2->d_reclen);
400 copy_to_user(d1->d_name, name, len);
401 put_user(0, d1->d_name+len);
402 put_user(len, &d1->d_reclen);
404 PRINTK(("FAT d1=%p d2=%p len=%d, name_len=%d\n",
405 d1, d2, len, name_len));
407 return 0;
410 int fat_dir_ioctl(struct inode * inode, struct file * filp,
411 unsigned int cmd, unsigned long arg)
413 int err;
415 * We want to provide an interface for Samba to be able
416 * to get the short filename for a given long filename.
417 * Samba should use this ioctl instead of readdir() to
418 * get the information it needs.
420 switch (cmd) {
421 case VFAT_IOCTL_READDIR_BOTH: {
422 struct dirent *d1 = (struct dirent *)arg;
423 err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
424 if (err)
425 return err;
426 put_user(0, &d1->d_reclen);
427 return fat_readdirx(inode,filp,(void *)arg,
428 vfat_ioctl_fill, NULL, 0, 1, 1);
430 case VFAT_IOCTL_READDIR_SHORT: {
431 struct dirent *d1 = (struct dirent *)arg;
432 put_user(0, &d1->d_reclen);
433 err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
434 if (err)
435 return err;
436 return fat_readdirx(inode,filp,(void *)arg,
437 vfat_ioctl_fill, NULL, 1, 0, 1);
439 default:
440 /* forward ioctl to CVF extension */
441 if (MSDOS_SB(inode->i_sb)->cvf_format &&
442 MSDOS_SB(inode->i_sb)->cvf_format->cvf_dir_ioctl)
443 return MSDOS_SB(inode->i_sb)->cvf_format
444 ->cvf_dir_ioctl(inode,filp,cmd,arg);
445 return -EINVAL;
448 return 0;
452 * Overrides for Emacs so that we follow Linus's tabbing style.
453 * Emacs will notice this stuff at the end of the file and automatically
454 * adjust the settings for this buffer only. This must remain at the end
455 * of the file.
456 * ---------------------------------------------------------------------------
457 * Local variables:
458 * c-indent-level: 8
459 * c-brace-imaginary-offset: 0
460 * c-brace-offset: -8
461 * c-argdecl-indent: 8
462 * c-label-offset: -8
463 * c-continued-statement-offset: 8
464 * c-continued-brace-offset: 0
465 * End: