Import 2.1.81
[davej-history.git] / fs / fat / dir.c
blob7ba11ec2d858a13f5a635875f86112c89bc26187
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, /* no special release code */
50 file_fsync /* fsync */
54 * Convert Unicode 16 to UTF8, translated unicode, or ascii.
55 * If uni_xlate is enabled and we
56 * can't get a 1:1 conversion, use a colon as an escape character since
57 * it is normally invalid on the vfat filesystem. The following three
58 * characters are a sort of uuencoded 16 bit Unicode value. This lets
59 * us do a full dump and restore of Unicode filenames. We could get
60 * into some trouble with long Unicode names, but ignore that right now.
62 static int
63 uni16_to_x8(unsigned char *ascii, unsigned char *uni, int uni_xlate,
64 struct nls_table *nls)
66 unsigned char *ip, *op;
67 unsigned char ch, cl;
68 unsigned char *uni_page;
69 unsigned short val;
71 ip = uni;
72 op = ascii;
74 while (*ip || ip[1]) {
75 cl = *ip++;
76 ch = *ip++;
78 uni_page = nls->page_uni2charset[ch];
79 if (uni_page && uni_page[cl]) {
80 *op++ = uni_page[cl];
81 } else {
82 if (uni_xlate == 1) {
83 *op++ = ':';
84 val = (cl << 8) + ch;
85 op[2] = fat_uni2esc[val & 0x3f];
86 val >>= 6;
87 op[1] = fat_uni2esc[val & 0x3f];
88 val >>= 6;
89 *op = fat_uni2esc[val & 0x3f];
90 op += 3;
91 } else {
92 *op++ = '?';
96 *op = 0;
97 return (op - ascii);
100 #if 0
101 static void dump_de(struct msdos_dir_entry *de)
103 int i;
104 unsigned char *p = (unsigned char *) de;
105 printk("[");
107 for (i = 0; i < 32; i++, p++) {
108 printk("%02x ", *p);
110 printk("]\n");
112 #endif
113 int fat_readdirx(
114 struct inode *inode,
115 struct file *filp,
116 void *dirent,
117 fat_filldir_t fat_filldir,
118 filldir_t filldir,
119 int shortnames,
120 int longnames,
121 int both)
123 struct super_block *sb = inode->i_sb;
124 int ino,i,i2,last;
125 char c;
126 struct buffer_head *bh;
127 struct msdos_dir_entry *de;
128 unsigned long oldpos = filp->f_pos;
129 unsigned long spos;
130 int is_long;
131 char longname[275];
132 unsigned char long_len = 0; /* Make compiler warning go away */
133 unsigned char alias_checksum = 0; /* Make compiler warning go away */
134 unsigned char long_slots = 0;
135 int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
136 int utf8 = MSDOS_SB(sb)->options.utf8;
137 unsigned char *unicode = NULL;
138 struct nls_table *nls = MSDOS_SB(sb)->nls_io;
140 if (!inode || !S_ISDIR(inode->i_mode))
141 return -EBADF;
142 /* Fake . and .. for the root directory. */
143 if (inode->i_ino == MSDOS_ROOT_INO) {
144 while (oldpos < 2) {
145 if (fat_filldir(filldir, dirent, "..", oldpos+1, 0, oldpos, oldpos, 0, MSDOS_ROOT_INO) < 0)
146 return 0;
147 oldpos++;
148 filp->f_pos++;
150 if (oldpos == 2)
151 filp->f_pos = 0;
153 if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1))
154 return -ENOENT;
156 bh = NULL;
157 longname[0] = longname[1] = 0;
158 is_long = 0;
159 ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
160 while (ino > -1) {
161 #if 0
162 dump_de(de);
163 #endif
164 /* Check for long filename entry */
165 if (MSDOS_SB(sb)->options.isvfat && (de->name[0] == (__s8) DELETED_FLAG)) {
166 is_long = 0;
167 oldpos = filp->f_pos;
168 } else if (MSDOS_SB(sb)->options.isvfat && de->attr == ATTR_EXT) {
169 int get_new_entry;
170 struct msdos_dir_slot *ds;
171 int offset;
172 unsigned char id;
173 unsigned char slot;
174 unsigned char slots = 0;
176 if (!unicode) {
177 unicode = (unsigned char *)
178 __get_free_page(GFP_KERNEL);
179 if (!unicode)
180 return -ENOMEM;
183 offset = 0;
184 ds = (struct msdos_dir_slot *) de;
185 id = ds->id;
186 if (id & 0x40) {
187 slots = id & ~0x40;
188 long_slots = slots;
189 is_long = 1;
190 alias_checksum = ds->alias_checksum;
193 get_new_entry = 1;
194 slot = slots;
195 while (slot > 0) {
196 PRINTK(("1. get_new_entry: %d\n", get_new_entry));
197 if (ds->attr != ATTR_EXT) {
198 is_long = 0;
199 get_new_entry = 0;
200 break;
202 if ((ds->id & ~0x40) != slot) {
203 is_long = 0;
204 break;
206 if (ds->alias_checksum != alias_checksum) {
207 is_long = 0;
208 break;
210 slot--;
211 offset = slot * 26;
212 PRINTK(("2. get_new_entry: %d\n", get_new_entry));
213 memcpy(&unicode[offset], ds->name0_4, 10);
214 offset += 10;
215 memcpy(&unicode[offset], ds->name5_10, 12);
216 offset += 12;
217 memcpy(&unicode[offset], ds->name11_12, 4);
218 offset += 4;
220 if (ds->id & 0x40) {
221 unicode[offset] = 0;
222 unicode[offset+1] = 0;
224 if (slot > 0) {
225 ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
226 PRINTK(("4. get_new_entry: %d\n", get_new_entry));
227 if (ino == -1) {
228 is_long = 0;
229 get_new_entry = 0;
230 break;
232 ds = (struct msdos_dir_slot *) de;
234 PRINTK(("5. get_new_entry: %d\n", get_new_entry));
236 } else if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
237 char bufname[14];
238 char *ptname = bufname;
239 int dotoffset = 0;
240 int was_long = is_long;
242 if (is_long) {
243 unsigned char sum;
244 for (sum = 0, i = 0; i < 11; i++) {
245 sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
248 if (sum != alias_checksum) {
249 PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum));
250 is_long = 0;
251 long_slots = 0;
253 if (utf8) {
254 long_len = utf8_wcstombs(longname, (__u16 *) unicode, sizeof(longname));
255 } else {
256 long_len = uni16_to_x8(longname, unicode, uni_xlate, nls);
260 if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
261 bufname[0] = '.';
262 dotoffset = 1;
263 ptname = bufname+1;
265 for (i = 0, last = 0; i < 8; i++) {
266 if (!(c = de->name[i])) break;
267 if (c >= 'A' && c <= 'Z') c += 32;
268 /* see namei.c, msdos_format_name */
269 if (c == 0x05) c = 0xE5;
270 if (c != ' ')
271 last = i+1;
272 ptname[i] = c;
274 i = last;
275 ptname[i] = '.';
276 i++;
277 for (i2 = 0; i2 < 3; i2++) {
278 if (!(c = de->ext[i2])) break;
279 if (c >= 'A' && c <= 'Z') c += 32;
280 if (c != ' ')
281 last = i+1;
282 ptname[i] = c;
283 i++;
285 if ((i = last) != 0) {
286 if (!strcmp(de->name,MSDOS_DOT))
287 ino = inode->i_ino;
288 else if (!strcmp(de->name,MSDOS_DOTDOT))
289 ino = fat_parent_ino(inode,0);
291 if (shortnames || !is_long) {
292 if (both)
293 bufname[i+dotoffset] = '\0';
294 spos = oldpos;
295 if (was_long) {
296 spos = filp->f_pos - sizeof(struct msdos_dir_entry);
297 } else {
298 long_slots = 0;
300 if (fat_filldir(filldir, dirent, bufname, i+dotoffset, 0, oldpos, spos, long_slots, ino) < 0) {
301 filp->f_pos = oldpos;
302 break;
305 if (is_long && longnames) {
306 if (both) {
307 memcpy(&longname[long_len+1], bufname, i+dotoffset);
308 long_len += i+dotoffset;
310 spos = filp->f_pos - sizeof(struct msdos_dir_entry);
311 if (fat_filldir(filldir, dirent, longname, long_len, 1, oldpos, spos, long_slots, ino) < 0) {
312 filp->f_pos = oldpos;
313 break;
316 oldpos = filp->f_pos;
318 is_long = 0;
319 } else {
320 is_long = 0;
321 oldpos = filp->f_pos;
323 ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
325 if (bh)
326 fat_brelse(sb, bh);
327 if (unicode) {
328 free_page((unsigned long) unicode);
330 return 0;
333 static int fat_filldir(
334 filldir_t filldir,
335 void * buf,
336 const char * name,
337 int name_len,
338 int is_long,
339 off_t offset,
340 off_t short_offset,
341 int long_slots,
342 ino_t ino)
344 return filldir(buf, name, name_len, offset, ino);
347 int fat_readdir(
348 struct file *filp,
349 void *dirent,
350 filldir_t filldir)
352 struct inode *inode = filp->f_dentry->d_inode;
353 return fat_readdirx(inode, filp, dirent, fat_filldir, filldir,
354 0, 1, 0);
357 static int vfat_ioctl_fill(
358 filldir_t filldir,
359 void * buf,
360 const char * name,
361 int name_len,
362 int is_long,
363 off_t offset,
364 off_t short_offset,
365 int long_slots,
366 ino_t ino)
368 struct dirent *d1 = (struct dirent *)buf;
369 struct dirent *d2 = d1 + 1;
370 int len, slen;
371 int dotdir;
373 get_user(len, &d1->d_reclen);
374 if (len != 0) {
375 return -1;
378 if ((name_len == 1 && name[0] == '.') ||
379 (name_len == 2 && name[0] == '.' && name[1] == '.')) {
380 dotdir = 1;
381 len = name_len;
382 } else {
383 dotdir = 0;
384 len = strlen(name);
386 if (len != name_len) {
387 copy_to_user(d2->d_name, name, len);
388 put_user(0, d2->d_name + len);
389 put_user(len, &d2->d_reclen);
390 put_user(ino, &d2->d_ino);
391 put_user(offset, &d2->d_off);
392 slen = name_len - len;
393 copy_to_user(d1->d_name, name+len+1, slen);
394 put_user(0, d1->d_name+slen);
395 put_user(slen, &d1->d_reclen);
396 } else {
397 put_user(0, d2->d_name);
398 put_user(0, &d2->d_reclen);
399 copy_to_user(d1->d_name, name, len);
400 put_user(0, d1->d_name+len);
401 put_user(len, &d1->d_reclen);
403 PRINTK(("FAT d1=%p d2=%p len=%d, name_len=%d\n",
404 d1, d2, len, name_len));
406 return 0;
409 int fat_dir_ioctl(struct inode * inode, struct file * filp,
410 unsigned int cmd, unsigned long arg)
412 int err;
414 * We want to provide an interface for Samba to be able
415 * to get the short filename for a given long filename.
416 * Samba should use this ioctl instead of readdir() to
417 * get the information it needs.
419 switch (cmd) {
420 case VFAT_IOCTL_READDIR_BOTH: {
421 struct dirent *d1 = (struct dirent *)arg;
422 err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
423 if (err)
424 return err;
425 put_user(0, &d1->d_reclen);
426 return fat_readdirx(inode,filp,(void *)arg,
427 vfat_ioctl_fill, NULL, 0, 1, 1);
429 case VFAT_IOCTL_READDIR_SHORT: {
430 struct dirent *d1 = (struct dirent *)arg;
431 put_user(0, &d1->d_reclen);
432 err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
433 if (err)
434 return err;
435 return fat_readdirx(inode,filp,(void *)arg,
436 vfat_ioctl_fill, NULL, 1, 0, 1);
438 default:
439 /* forward ioctl to CVF extension */
440 if (MSDOS_SB(inode->i_sb)->cvf_format &&
441 MSDOS_SB(inode->i_sb)->cvf_format->cvf_dir_ioctl)
442 return MSDOS_SB(inode->i_sb)->cvf_format
443 ->cvf_dir_ioctl(inode,filp,cmd,arg);
444 return -EINVAL;
447 return 0;
451 * Overrides for Emacs so that we follow Linus's tabbing style.
452 * Emacs will notice this stuff at the end of the file and automatically
453 * adjust the settings for this buffer only. This must remain at the end
454 * of the file.
455 * ---------------------------------------------------------------------------
456 * Local variables:
457 * c-indent-level: 8
458 * c-brace-imaginary-offset: 0
459 * c-brace-offset: -8
460 * c-argdecl-indent: 8
461 * c-label-offset: -8
462 * c-continued-statement-offset: 8
463 * c-continued-brace-offset: 0
464 * End: