vserver 1.9.3
[linux-2.6.git] / fs / fat / dir.c
1 /*
2  *  linux/fs/fat/dir.c
3  *
4  *  directory handling functions for fat-based filesystems
5  *
6  *  Written 1992,1993 by Werner Almesberger
7  *
8  *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
9  *
10  *  VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu>
11  *  Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
12  *  Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV
13  *  Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de>
14  */
15
16 #include <linux/slab.h>
17 #include <linux/time.h>
18 #include <linux/msdos_fs.h>
19 #include <linux/dirent.h>
20 #include <linux/smp_lock.h>
21 #include <linux/buffer_head.h>
22
23 #include <asm/uaccess.h>
24
25 struct file_operations fat_dir_operations = {
26         .read           = generic_read_dir,
27         .readdir        = fat_readdir,
28         .ioctl          = fat_dir_ioctl,
29         .fsync          = file_fsync,
30 };
31
32 /*
33  * Convert Unicode 16 to UTF8, translated Unicode, or ASCII.
34  * If uni_xlate is enabled and we can't get a 1:1 conversion, use a
35  * colon as an escape character since it is normally invalid on the vfat
36  * filesystem. The following four characters are the hexadecimal digits
37  * of Unicode value. This lets us do a full dump and restore of Unicode
38  * filenames. We could get into some trouble with long Unicode names,
39  * but ignore that right now.
40  * Ahem... Stack smashing in ring 0 isn't fun. Fixed.
41  */
42 static int
43 uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate,
44             struct nls_table *nls)
45 {
46         wchar_t *ip, ec;
47         unsigned char *op, nc;
48         int charlen;
49         int k;
50
51         ip = uni;
52         op = ascii;
53
54         while (*ip) {
55                 ec = *ip++;
56                 if ( (charlen = nls->uni2char(ec, op, NLS_MAX_CHARSET_SIZE)) > 0) {
57                         op += charlen;
58                 } else {
59                         if (uni_xlate == 1) {
60                                 *op = ':';
61                                 for (k = 4; k > 0; k--) {
62                                         nc = ec & 0xF;
63                                         op[k] = nc > 9  ? nc + ('a' - 10)
64                                                         : nc + '0';
65                                         ec >>= 4;
66                                 }
67                                 op += 5;
68                         } else {
69                                 *op++ = '?';
70                         }
71                 }
72                 /* We have some slack there, so it's OK */
73                 if (op>ascii+256) {
74                         op = ascii + 256;
75                         break;
76                 }
77         }
78         *op = 0;
79         return (op - ascii);
80 }
81
82 #if 0
83 static void dump_de(struct msdos_dir_entry *de)
84 {
85         int i;
86         unsigned char *p = (unsigned char *) de;
87         printk("[");
88
89         for (i = 0; i < 32; i++, p++) {
90                 printk("%02x ", *p);
91         }
92         printk("]\n");
93 }
94 #endif
95
96 static inline int
97 fat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni)
98 {
99         int charlen;
100
101         charlen = t->char2uni(c, clen, uni);
102         if (charlen < 0) {
103                 *uni = 0x003f;  /* a question mark */
104                 charlen = 1;
105         }
106         return charlen;
107 }
108
109 static inline int
110 fat_short2lower_uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni)
111 {
112         int charlen;
113         wchar_t wc;
114
115         charlen = t->char2uni(c, clen, &wc);
116         if (charlen < 0) {
117                 *uni = 0x003f;  /* a question mark */
118                 charlen = 1;
119         } else if (charlen <= 1) {
120                 unsigned char nc = t->charset2lower[*c];
121                 
122                 if (!nc)
123                         nc = *c;
124                 
125                 if ( (charlen = t->char2uni(&nc, 1, uni)) < 0) {
126                         *uni = 0x003f;  /* a question mark */
127                         charlen = 1;
128                 }
129         } else
130                 *uni = wc;
131         
132         return charlen;
133 }
134
135 static inline int
136 fat_shortname2uni(struct nls_table *nls, unsigned char *buf, int buf_size,
137                   wchar_t *uni_buf, unsigned short opt, int lower)
138 {
139         int len = 0;
140
141         if (opt & VFAT_SFN_DISPLAY_LOWER)
142                 len =  fat_short2lower_uni(nls, buf, buf_size, uni_buf);
143         else if (opt & VFAT_SFN_DISPLAY_WIN95)
144                 len = fat_short2uni(nls, buf, buf_size, uni_buf);
145         else if (opt & VFAT_SFN_DISPLAY_WINNT) {
146                 if (lower)
147                         len = fat_short2lower_uni(nls, buf, buf_size, uni_buf);
148                 else 
149                         len = fat_short2uni(nls, buf, buf_size, uni_buf);
150         } else
151                 len = fat_short2uni(nls, buf, buf_size, uni_buf);
152
153         return len;
154 }
155
156 /*
157  * Return values: negative -> error, 0 -> not found, positive -> found,
158  * value is the total amount of slots, including the shortname entry.
159  */
160 int fat_search_long(struct inode *inode, const unsigned char *name,
161                     int name_len, int anycase, loff_t *spos, loff_t *lpos)
162 {
163         struct super_block *sb = inode->i_sb;
164         struct buffer_head *bh = NULL;
165         struct msdos_dir_entry *de;
166         struct nls_table *nls_io = MSDOS_SB(sb)->nls_io;
167         struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk;
168         wchar_t bufuname[14];
169         unsigned char xlate_len, long_slots;
170         wchar_t *unicode = NULL;
171         unsigned char work[8], bufname[260];    /* 256 + 4 */
172         int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
173         int utf8 = MSDOS_SB(sb)->options.utf8;
174         unsigned short opt_shortname = MSDOS_SB(sb)->options.shortname;
175         int chl, i, j, last_u, res = 0;
176         loff_t i_pos, cpos = 0;
177
178         while(1) {
179                 if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos) == -1)
180                         goto EODir;
181 parse_record:
182                 long_slots = 0;
183                 if (de->name[0] == DELETED_FLAG)
184                         continue;
185                 if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
186                         continue;
187                 if (de->attr != ATTR_EXT && IS_FREE(de->name))
188                         continue;
189                 if (de->attr == ATTR_EXT) {
190                         struct msdos_dir_slot *ds;
191                         unsigned char id;
192                         unsigned char slot;
193                         unsigned char slots;
194                         unsigned char sum;
195                         unsigned char alias_checksum;
196
197                         if (!unicode) {
198                                 unicode = (wchar_t *)
199                                         __get_free_page(GFP_KERNEL);
200                                 if (!unicode) {
201                                         brelse(bh);
202                                         return -ENOMEM;
203                                 }
204                         }
205 parse_long:
206                         slots = 0;
207                         ds = (struct msdos_dir_slot *) de;
208                         id = ds->id;
209                         if (!(id & 0x40))
210                                 continue;
211                         slots = id & ~0x40;
212                         if (slots > 20 || !slots)       /* ceil(256 * 2 / 26) */
213                                 continue;
214                         long_slots = slots;
215                         alias_checksum = ds->alias_checksum;
216
217                         slot = slots;
218                         while (1) {
219                                 int offset;
220
221                                 slot--;
222                                 offset = slot * 13;
223                                 fat16_towchar(unicode + offset, ds->name0_4, 5);
224                                 fat16_towchar(unicode + offset + 5, ds->name5_10, 6);
225                                 fat16_towchar(unicode + offset + 11, ds->name11_12, 2);
226
227                                 if (ds->id & 0x40) {
228                                         unicode[offset + 13] = 0;
229                                 }
230                                 if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos)<0)
231                                         goto EODir;
232                                 if (slot == 0)
233                                         break;
234                                 ds = (struct msdos_dir_slot *) de;
235                                 if (ds->attr !=  ATTR_EXT)
236                                         goto parse_record;
237                                 if ((ds->id & ~0x40) != slot)
238                                         goto parse_long;
239                                 if (ds->alias_checksum != alias_checksum)
240                                         goto parse_long;
241                         }
242                         if (de->name[0] == DELETED_FLAG)
243                                 continue;
244                         if (de->attr ==  ATTR_EXT)
245                                 goto parse_long;
246                         if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
247                                 continue;
248                         for (sum = 0, i = 0; i < 11; i++)
249                                 sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
250                         if (sum != alias_checksum)
251                                 long_slots = 0;
252                 }
253
254                 memcpy(work, de->name, sizeof(de->name));
255                 /* see namei.c, msdos_format_name */
256                 if (work[0] == 0x05)
257                         work[0] = 0xE5;
258                 for (i = 0, j = 0, last_u = 0; i < 8;) {
259                         if (!work[i]) break;
260                         chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
261                                                 &bufuname[j++], opt_shortname,
262                                                 de->lcase & CASE_LOWER_BASE);
263                         if (chl <= 1) {
264                                 if (work[i] != ' ')
265                                         last_u = j;
266                         } else {
267                                 last_u = j;
268                         }
269                         i += chl;
270                 }
271                 j = last_u;
272                 fat_short2uni(nls_disk, ".", 1, &bufuname[j++]);
273                 for (i = 0; i < 3;) {
274                         if (!de->ext[i]) break;
275                         chl = fat_shortname2uni(nls_disk, &de->ext[i], 3 - i,
276                                                 &bufuname[j++], opt_shortname,
277                                                 de->lcase & CASE_LOWER_EXT);
278                         if (chl <= 1) {
279                                 if (de->ext[i] != ' ')
280                                         last_u = j;
281                         } else {
282                                 last_u = j;
283                         }
284                         i += chl;
285                 }
286                 if (!last_u)
287                         continue;
288
289                 bufuname[last_u] = 0x0000;
290                 xlate_len = utf8
291                         ?utf8_wcstombs(bufname, bufuname, sizeof(bufname))
292                         :uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
293                 if (xlate_len == name_len)
294                         if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
295                             (anycase && !nls_strnicmp(nls_io, name, bufname,
296                                                                 xlate_len)))
297                                 goto Found;
298
299                 if (long_slots) {
300                         xlate_len = utf8
301                                 ?utf8_wcstombs(bufname, unicode, sizeof(bufname))
302                                 :uni16_to_x8(bufname, unicode, uni_xlate, nls_io);
303                         if (xlate_len != name_len)
304                                 continue;
305                         if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
306                             (anycase && !nls_strnicmp(nls_io, name, bufname,
307                                                                 xlate_len)))
308                                 goto Found;
309                 }
310         }
311
312 Found:
313         res = long_slots + 1;
314         *spos = cpos - sizeof(struct msdos_dir_entry);
315         *lpos = cpos - res*sizeof(struct msdos_dir_entry);
316 EODir:
317         brelse(bh);
318         if (unicode) {
319                 free_page((unsigned long) unicode);
320         }
321         return res;
322 }
323
324 static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent,
325                         filldir_t filldir, int shortnames, int both)
326 {
327         struct super_block *sb = inode->i_sb;
328         struct buffer_head *bh;
329         struct msdos_dir_entry *de;
330         struct nls_table *nls_io = MSDOS_SB(sb)->nls_io;
331         struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk;
332         wchar_t bufuname[14];
333         unsigned char long_slots;
334         wchar_t *unicode = NULL;
335         unsigned char c, work[8], bufname[56], *ptname = bufname;
336         unsigned long lpos, dummy, *furrfu = &lpos;
337         int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
338         int isvfat = MSDOS_SB(sb)->options.isvfat;
339         int utf8 = MSDOS_SB(sb)->options.utf8;
340         int nocase = MSDOS_SB(sb)->options.nocase;
341         unsigned short opt_shortname = MSDOS_SB(sb)->options.shortname;
342         unsigned long inum;
343         int chi, chl, i, i2, j, last, last_u, dotoffset = 0;
344         loff_t i_pos, cpos;
345         int ret = 0;
346         
347         lock_kernel();
348
349         cpos = filp->f_pos;
350         /* Fake . and .. for the root directory. */
351         if (inode->i_ino == MSDOS_ROOT_INO) {
352                 while (cpos < 2) {
353                         if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO, DT_DIR) < 0)
354                                 goto out;
355                         cpos++;
356                         filp->f_pos++;
357                 }
358                 if (cpos == 2) {
359                         dummy = 2;
360                         furrfu = &dummy;
361                         cpos = 0;
362                 }
363         }
364         if (cpos & (sizeof(struct msdos_dir_entry)-1)) {
365                 ret = -ENOENT;
366                 goto out;
367         }
368
369         bh = NULL;
370 GetNew:
371         long_slots = 0;
372         if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos) == -1)
373                 goto EODir;
374         /* Check for long filename entry */
375         if (isvfat) {
376                 if (de->name[0] == DELETED_FLAG)
377                         goto RecEnd;
378                 if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
379                         goto RecEnd;
380                 if (de->attr != ATTR_EXT && IS_FREE(de->name))
381                         goto RecEnd;
382         } else {
383                 if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name))
384                         goto RecEnd;
385         }
386
387         if (isvfat && de->attr == ATTR_EXT) {
388                 struct msdos_dir_slot *ds;
389                 unsigned char id;
390                 unsigned char slot;
391                 unsigned char slots;
392                 unsigned char sum;
393                 unsigned char alias_checksum;
394
395                 if (!unicode) {
396                         unicode = (wchar_t *)
397                                 __get_free_page(GFP_KERNEL);
398                         if (!unicode) {
399                                 filp->f_pos = cpos;
400                                 brelse(bh);
401                                 ret = -ENOMEM;
402                                 goto out;
403                         }
404                 }
405 ParseLong:
406                 slots = 0;
407                 ds = (struct msdos_dir_slot *) de;
408                 id = ds->id;
409                 if (!(id & 0x40))
410                         goto RecEnd;
411                 slots = id & ~0x40;
412                 if (slots > 20 || !slots)       /* ceil(256 * 2 / 26) */
413                         goto RecEnd;
414                 long_slots = slots;
415                 alias_checksum = ds->alias_checksum;
416
417                 slot = slots;
418                 while (1) {
419                         int offset;
420
421                         slot--;
422                         offset = slot * 13;
423                         fat16_towchar(unicode + offset, ds->name0_4, 5);
424                         fat16_towchar(unicode + offset + 5, ds->name5_10, 6);
425                         fat16_towchar(unicode + offset + 11, ds->name11_12, 2);
426
427                         if (ds->id & 0x40) {
428                                 unicode[offset + 13] = 0;
429                         }
430                         if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos) == -1)
431                                 goto EODir;
432                         if (slot == 0)
433                                 break;
434                         ds = (struct msdos_dir_slot *) de;
435                         if (ds->attr !=  ATTR_EXT)
436                                 goto RecEnd;    /* XXX */
437                         if ((ds->id & ~0x40) != slot)
438                                 goto ParseLong;
439                         if (ds->alias_checksum != alias_checksum)
440                                 goto ParseLong;
441                 }
442                 if (de->name[0] == DELETED_FLAG)
443                         goto RecEnd;
444                 if (de->attr ==  ATTR_EXT)
445                         goto ParseLong;
446                 if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
447                         goto RecEnd;
448                 for (sum = 0, i = 0; i < 11; i++)
449                         sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
450                 if (sum != alias_checksum)
451                         long_slots = 0;
452         }
453
454         if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
455                 *ptname++ = '.';
456                 dotoffset = 1;
457         }
458
459         memcpy(work, de->name, sizeof(de->name));
460         /* see namei.c, msdos_format_name */
461         if (work[0] == 0x05)
462                 work[0] = 0xE5;
463         for (i = 0, j = 0, last = 0, last_u = 0; i < 8;) {
464                 if (!(c = work[i])) break;
465                 chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
466                                         &bufuname[j++], opt_shortname,
467                                         de->lcase & CASE_LOWER_BASE);
468                 if (chl <= 1) {
469                         ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c;
470                         if (c != ' ') {
471                                 last = i;
472                                 last_u = j;
473                         }
474                 } else {
475                         last_u = j;
476                         for (chi = 0; chi < chl && i < 8; chi++) {
477                                 ptname[i] = work[i];
478                                 i++; last = i;
479                         }
480                 }
481         }
482         i = last;
483         j = last_u;
484         fat_short2uni(nls_disk, ".", 1, &bufuname[j++]);
485         ptname[i++] = '.';
486         for (i2 = 0; i2 < 3;) {
487                 if (!(c = de->ext[i2])) break;
488                 chl = fat_shortname2uni(nls_disk, &de->ext[i2], 3 - i2,
489                                         &bufuname[j++], opt_shortname,
490                                         de->lcase & CASE_LOWER_EXT);
491                 if (chl <= 1) {
492                         i2++;
493                         ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c;
494                         if (c != ' ') {
495                                 last = i;
496                                 last_u = j;
497                         }
498                 } else {
499                         last_u = j;
500                         for (chi = 0; chi < chl && i2 < 3; chi++) {
501                                 ptname[i++] = de->ext[i2++];
502                                 last = i;
503                         }
504                 }
505         }
506         if (!last)
507                 goto RecEnd;
508
509         i = last + dotoffset;
510         j = last_u;
511
512         lpos = cpos - (long_slots+1)*sizeof(struct msdos_dir_entry);
513         if (!memcmp(de->name,MSDOS_DOT,11))
514                 inum = inode->i_ino;
515         else if (!memcmp(de->name,MSDOS_DOTDOT,11)) {
516                 inum = parent_ino(filp->f_dentry);
517         } else {
518                 struct inode *tmp = fat_iget(sb, i_pos);
519                 if (tmp) {
520                         inum = tmp->i_ino;
521                         iput(tmp);
522                 } else
523                         inum = iunique(sb, MSDOS_ROOT_INO);
524         }
525
526         if (isvfat) {
527                 bufuname[j] = 0x0000;
528                 i = utf8 ? utf8_wcstombs(bufname, bufuname, sizeof(bufname))
529                          : uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
530         }
531
532         if (!long_slots||shortnames) {
533                 if (both)
534                         bufname[i] = '\0';
535                 if (filldir(dirent, bufname, i, *furrfu, inum,
536                             (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
537                         goto FillFailed;
538         } else {
539                 unsigned char longname[275];
540                 int long_len = utf8
541                         ? utf8_wcstombs(longname, unicode, sizeof(longname))
542                         : uni16_to_x8(longname, unicode, uni_xlate,
543                                       nls_io);
544                 if (both) {
545                         memcpy(&longname[long_len+1], bufname, i);
546                         long_len += i;
547                 }
548                 if (filldir(dirent, longname, long_len, *furrfu, inum,
549                             (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
550                         goto FillFailed;
551         }
552
553 RecEnd:
554         furrfu = &lpos;
555         filp->f_pos = cpos;
556         goto GetNew;
557 EODir:
558         filp->f_pos = cpos;
559 FillFailed:
560         if (bh)
561                 brelse(bh);
562         if (unicode) {
563                 free_page((unsigned long) unicode);
564         }
565 out:
566         unlock_kernel();
567         return ret;
568 }
569
570 int fat_readdir(struct file *filp, void *dirent, filldir_t filldir)
571 {
572         struct inode *inode = filp->f_dentry->d_inode;
573         return fat_readdirx(inode, filp, dirent, filldir, 0, 0);
574 }
575
576 struct fat_ioctl_filldir_callback {
577         struct dirent __user *dirent;
578         int result;
579 };
580
581 static int fat_ioctl_filldir(void *__buf, const char * name, int name_len,
582                              loff_t offset, ino_t ino, unsigned int d_type)
583 {
584         struct fat_ioctl_filldir_callback *buf = __buf;
585         struct dirent __user *d1 = buf->dirent;
586         struct dirent __user *d2 = d1 + 1;
587         int len, slen;
588         int dotdir;
589
590         if (buf->result)
591                 return -EINVAL;
592         buf->result++;
593
594         if ((name_len == 1 && name[0] == '.') ||
595             (name_len == 2 && name[0] == '.' && name[1] == '.')) {
596                 dotdir = 1;
597                 len = name_len;
598         } else {
599                 dotdir = 0;
600                 len = strlen(name);
601         }
602         if (len != name_len) {
603                 slen = name_len - len;
604                 if (copy_to_user(d2->d_name, name, len)         ||
605                     put_user(0, d2->d_name + len)               ||
606                     put_user(len, &d2->d_reclen)                ||
607                     put_user(ino, &d2->d_ino)                   ||
608                     put_user(offset, &d2->d_off)                ||
609                     copy_to_user(d1->d_name, name+len+1, slen)  ||
610                     put_user(0, d1->d_name+slen)                ||
611                     put_user(slen, &d1->d_reclen))
612                         goto efault;
613         } else {
614                 if (put_user(0, d2->d_name)                     ||
615                     put_user(0, &d2->d_reclen)                  ||
616                     copy_to_user(d1->d_name, name, len)         ||
617                     put_user(0, d1->d_name+len)                 ||
618                     put_user(len, &d1->d_reclen))
619                         goto efault;
620         }
621         return 0;
622 efault:
623         buf->result = -EFAULT;
624         return -EFAULT;
625 }
626
627 int fat_dir_ioctl(struct inode * inode, struct file * filp,
628                   unsigned int cmd, unsigned long arg)
629 {
630         struct fat_ioctl_filldir_callback buf;
631         struct dirent __user *d1;
632         int ret, shortname, both;
633
634         switch (cmd) {
635         case VFAT_IOCTL_READDIR_SHORT:
636                 shortname = 1;
637                 both = 1;
638                 break;
639         case VFAT_IOCTL_READDIR_BOTH:
640                 shortname = 0;
641                 both = 1;
642                 break;
643         default:
644                 return -EINVAL;
645         }
646
647         d1 = (struct dirent __user *)arg;
648         if (!access_ok(VERIFY_WRITE, d1, sizeof(struct dirent[2])))
649                 return -EFAULT;
650         /*
651          * Yes, we don't need this put_user() absolutely. However old
652          * code didn't return the right value. So, app use this value,
653          * in order to check whether it is EOF.
654          */
655         if (put_user(0, &d1->d_reclen))
656                 return -EFAULT;
657
658         buf.dirent = d1;
659         buf.result = 0;
660         down(&inode->i_sem);
661         ret = -ENOENT;
662         if (!IS_DEADDIR(inode)) {
663                 ret = fat_readdirx(inode, filp, &buf, fat_ioctl_filldir,
664                                    shortname, both);
665         }
666         up(&inode->i_sem);
667         if (ret >= 0)
668                 ret = buf.result;
669         return ret;
670 }
671
672 /* This assumes that size of cluster is above the 32*slots */
673
674 int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh,
675                   struct msdos_dir_entry **de, loff_t *i_pos)
676 {
677         struct super_block *sb = dir->i_sb;
678         loff_t offset, curr;
679         int row;
680         struct buffer_head *new_bh;
681
682         offset = curr = 0;
683         *bh = NULL;
684         row = 0;
685         while (fat_get_entry(dir, &curr, bh, de, i_pos) > -1) {
686                 /* check the maximum size of directory */
687                 if (curr >= FAT_MAX_DIR_SIZE) {
688                         brelse(*bh);
689                         return -ENOSPC;
690                 }
691
692                 if (IS_FREE((*de)->name)) {
693                         if (++row == slots)
694                                 return offset;
695                 } else {
696                         row = 0;
697                         offset = curr;
698                 }
699         }
700         if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32)) 
701                 return -ENOSPC;
702         new_bh = fat_extend_dir(dir);
703         if (IS_ERR(new_bh))
704                 return PTR_ERR(new_bh);
705         brelse(new_bh);
706         do {
707                 fat_get_entry(dir, &curr, bh, de, i_pos);
708         } while (++row < slots);
709
710         return offset;
711 }
712
713 int fat_new_dir(struct inode *dir, struct inode *parent, int is_vfat)
714 {
715         struct buffer_head *bh;
716         struct msdos_dir_entry *de;
717         __le16 date, time;
718
719         bh = fat_extend_dir(dir);
720         if (IS_ERR(bh))
721                 return PTR_ERR(bh);
722
723         /* zeroed out, so... */
724         fat_date_unix2dos(dir->i_mtime.tv_sec,&time,&date);
725         de = (struct msdos_dir_entry*)&bh->b_data[0];
726         memcpy(de[0].name,MSDOS_DOT,MSDOS_NAME);
727         memcpy(de[1].name,MSDOS_DOTDOT,MSDOS_NAME);
728         de[0].attr = de[1].attr = ATTR_DIR;
729         de[0].time = de[1].time = time;
730         de[0].date = de[1].date = date;
731         if (is_vfat) {  /* extra timestamps */
732                 de[0].ctime = de[1].ctime = time;
733                 de[0].adate = de[0].cdate =
734                         de[1].adate = de[1].cdate = date;
735         }
736         de[0].start = CT_LE_W(MSDOS_I(dir)->i_logstart);
737         de[0].starthi = CT_LE_W(MSDOS_I(dir)->i_logstart>>16);
738         de[1].start = CT_LE_W(MSDOS_I(parent)->i_logstart);
739         de[1].starthi = CT_LE_W(MSDOS_I(parent)->i_logstart>>16);
740         mark_buffer_dirty(bh);
741         brelse(bh);
742         dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
743         mark_inode_dirty(dir);
744
745         return 0;
746 }
747
748 static int fat_get_short_entry(struct inode *dir, loff_t *pos,
749                                struct buffer_head **bh,
750                                struct msdos_dir_entry **de, loff_t *i_pos)
751 {
752         while (fat_get_entry(dir, pos, bh, de, i_pos) >= 0) {
753                 /* free entry or long name entry or volume label */
754                 if (!IS_FREE((*de)->name) && !((*de)->attr & ATTR_VOLUME))
755                         return 0;
756         }
757         return -ENOENT;
758 }
759
760 /* See if directory is empty */
761 int fat_dir_empty(struct inode *dir)
762 {
763         struct buffer_head *bh;
764         struct msdos_dir_entry *de;
765         loff_t cpos, i_pos;
766         int result = 0;
767
768         bh = NULL;
769         cpos = 0;
770         while (fat_get_short_entry(dir, &cpos, &bh, &de, &i_pos) >= 0) {
771                 if (strncmp(de->name, MSDOS_DOT   , MSDOS_NAME) &&
772                     strncmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) {
773                         result = -ENOTEMPTY;
774                         break;
775                 }
776         }
777         brelse(bh);
778         return result;
779 }
780
781 /*
782  * fat_subdirs counts the number of sub-directories of dir. It can be run
783  * on directories being created.
784  */
785 int fat_subdirs(struct inode *dir)
786 {
787         struct buffer_head *bh;
788         struct msdos_dir_entry *de;
789         loff_t cpos, i_pos;
790         int count = 0;
791
792         bh = NULL;
793         cpos = 0;
794         while (fat_get_short_entry(dir, &cpos, &bh, &de, &i_pos) >= 0) {
795                 if (de->attr & ATTR_DIR)
796                         count++;
797         }
798         brelse(bh);
799         return count;
800 }
801
802 /*
803  * Scans a directory for a given file (name points to its formatted name).
804  * Returns an error code or zero.
805  */
806 int fat_scan(struct inode *dir, const unsigned char *name,
807              struct buffer_head **bh, struct msdos_dir_entry **de,
808              loff_t *i_pos)
809 {
810         loff_t cpos;
811
812         *bh = NULL;
813         cpos = 0;
814         while (fat_get_short_entry(dir, &cpos, bh, de, i_pos) >= 0) {
815                 if (!strncmp((*de)->name, name, MSDOS_NAME))
816                         return 0;
817         }
818         return -ENOENT;
819 }