~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/fs/vfat/namei.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *  linux/fs/vfat/namei.c
  3  *
  4  *  Written 1992,1993 by Werner Almesberger
  5  *
  6  *  Windows95/Windows NT compatible extended MSDOS filesystem
  7  *    by Gordon Chaffee Copyright (C) 1995.  Send bug reports for the
  8  *    VFAT filesystem to <chaffee@cs.berkeley.edu>.  Specify
  9  *    what file operation caused you trouble and if you can duplicate
 10  *    the problem, send a script that demonstrates it.
 11  *
 12  *  Short name translation 1999 by Wolfram Pienkoss <wp@bszh.de>
 13  */
 14 
 15 #define __NO_VERSION__
 16 #include <linux/module.h>
 17 
 18 #include <linux/sched.h>
 19 #include <linux/msdos_fs.h>
 20 #include <linux/nls.h>
 21 #include <linux/kernel.h>
 22 #include <linux/errno.h>
 23 #include <linux/string.h>
 24 #include <linux/ctype.h>
 25 #include <linux/stat.h>
 26 #include <linux/mm.h>
 27 #include <linux/malloc.h>
 28 
 29 #include "../fat/msbuffer.h"
 30 
 31 #define DEBUG_LEVEL 0
 32 #if (DEBUG_LEVEL >= 1)
 33 #  define PRINTK1(x) printk x
 34 #else
 35 #  define PRINTK1(x)
 36 #endif
 37 #if (DEBUG_LEVEL >= 2)
 38 #  define PRINTK2(x) printk x
 39 #else
 40 #  define PRINTK2(x)
 41 #endif
 42 #if (DEBUG_LEVEL >= 3)
 43 #  define PRINTK3(x) printk x
 44 #else
 45 #  define PRINTK3(x)
 46 #endif
 47 
 48 #ifndef DEBUG
 49 # define CHECK_STACK
 50 #else
 51 # define CHECK_STACK check_stack(__FILE__, __LINE__)
 52 #endif
 53 
 54 static int vfat_hashi(struct dentry *parent, struct qstr *qstr);
 55 static int vfat_hash(struct dentry *parent, struct qstr *qstr);
 56 static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b);
 57 static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b);
 58 static int vfat_revalidate(struct dentry *dentry, int);
 59 
 60 static struct dentry_operations vfat_dentry_ops[4] = {
 61         {
 62                 d_hash:         vfat_hashi,
 63                 d_compare:      vfat_cmpi,
 64         },
 65         {
 66                 d_revalidate:   vfat_revalidate,
 67                 d_hash:         vfat_hashi,
 68                 d_compare:      vfat_cmpi,
 69         },
 70         {
 71                 d_hash:         vfat_hash,
 72                 d_compare:      vfat_cmp,
 73         },
 74         {
 75                 d_revalidate:   vfat_revalidate,
 76                 d_hash:         vfat_hash,
 77                 d_compare:      vfat_cmp,
 78         }
 79 };
 80 
 81 static int vfat_revalidate(struct dentry *dentry, int flags)
 82 {
 83         PRINTK1(("vfat_revalidate: %s\n", dentry->d_name.name));
 84         spin_lock(&dcache_lock);
 85         if (dentry->d_time == dentry->d_parent->d_inode->i_version) {
 86                 spin_unlock(&dcache_lock);
 87                 return 1;
 88         }
 89         spin_unlock(&dcache_lock);
 90         return 0;
 91 }
 92 
 93 static int simple_getbool(char *s, int *setval)
 94 {
 95         if (s) {
 96                 if (!strcmp(s,"1") || !strcmp(s,"yes") || !strcmp(s,"true")) {
 97                         *setval = 1;
 98                 } else if (!strcmp(s,"") || !strcmp(s,"no") || !strcmp(s,"false")) {
 99                         *setval = 0;
100                 } else {
101                         return 0;
102                 }
103         } else {
104                 *setval = 1;
105         }
106         return 1;
107 }
108 
109 static int parse_options(char *options, struct fat_mount_options *opts)
110 {
111         char *this_char,*value,save,*savep;
112         int ret, val;
113 
114         opts->unicode_xlate = opts->posixfs = 0;
115         opts->numtail = 1;
116         opts->utf8 = 0;
117 
118         if (!options) return 1;
119         save = 0;
120         savep = NULL;
121         ret = 1;
122         for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
123                 if ((value = strchr(this_char,'=')) != NULL) {
124                         save = *value;
125                         savep = value;
126                         *value++ = 0;
127                 }
128                 if (!strcmp(this_char,"utf8")) {
129                         ret = simple_getbool(value, &val);
130                         if (ret) opts->utf8 = val;
131                 } else if (!strcmp(this_char,"uni_xlate")) {
132                         ret = simple_getbool(value, &val);
133                         if (ret) opts->unicode_xlate = val;
134                 } else if (!strcmp(this_char,"posix")) {
135                         ret = simple_getbool(value, &val);
136                         if (ret) opts->posixfs = val;
137                 } else if (!strcmp(this_char,"nonumtail")) {
138                         ret = simple_getbool(value, &val);
139                         if (ret) {
140                                 opts->numtail = !val;
141                         }
142                 }
143                 if (this_char != options)
144                         *(this_char-1) = ',';
145                 if (value) {
146                         *savep = save;
147                 }
148                 if (ret == 0) {
149                         return 0;
150                 }
151         }
152         if (opts->unicode_xlate) {
153                 opts->utf8 = 0;
154         }
155         return 1;
156 }
157 
158 static inline unsigned char
159 vfat_getlower(struct nls_table *t, unsigned char c)
160 {
161         return t->charset2lower[c];
162 }
163 
164 static inline unsigned char
165 vfat_tolower(struct nls_table *t, unsigned char c)
166 {
167         unsigned char nc = t->charset2lower[c];
168 
169         return nc ? nc : c;
170 }
171 
172 static inline unsigned char
173 vfat_getupper(struct nls_table *t, unsigned char c)
174 {
175         return t->charset2upper[c];
176 }
177 
178 static inline unsigned char
179 vfat_toupper(struct nls_table *t, unsigned char c)
180 {
181         unsigned char nc = t->charset2upper[c];
182 
183         return nc ? nc : c;
184 }
185 
186 static int
187 vfat_strnicmp(struct nls_table *t, const unsigned char *s1,
188                                         const unsigned char *s2, int len)
189 {
190         while(len--)
191                 if (vfat_tolower(t, *s1++) != vfat_tolower(t, *s2++))
192                         return 1;
193 
194         return 0;
195 }
196 
197 static inline int
198 vfat_uni2short(struct nls_table *t, wchar_t uc, unsigned char *op, int bound)
199 {
200         int charlen;
201 
202         if ( (charlen = t->uni2char(uc, op, bound)) < 0)
203                 charlen = 0;
204 
205         return charlen;
206 }
207 
208 static inline int
209 vfat_uni2upper_short(struct nls_table *t, wchar_t uc, char *op, int bound)
210 {
211         int chi, chl;
212 
213         if ( (chl = t->uni2char(uc, op, bound)) < 0)
214                 chl = 0;
215 
216         for (chi = 0; chi < chl; chi++)
217                 op[chi] = vfat_toupper(t, op[chi]);
218 
219         return chl;
220 }
221 
222 /*
223  * Compute the hash for the vfat name corresponding to the dentry.
224  * Note: if the name is invalid, we leave the hash code unchanged so
225  * that the existing dentry can be used. The vfat fs routines will
226  * return ENOENT or EINVAL as appropriate.
227  */
228 static int vfat_hash(struct dentry *dentry, struct qstr *qstr)
229 {
230         const char *name;
231         int len;
232 
233         len = qstr->len;
234         name = qstr->name;
235         while (len && name[len-1] == '.')
236                 len--;
237 
238         qstr->hash = full_name_hash(name, len);
239 
240         return 0;
241 }
242 
243 /*
244  * Compute the hash for the vfat name corresponding to the dentry.
245  * Note: if the name is invalid, we leave the hash code unchanged so
246  * that the existing dentry can be used. The vfat fs routines will
247  * return ENOENT or EINVAL as appropriate.
248  */
249 static int vfat_hashi(struct dentry *dentry, struct qstr *qstr)
250 {
251         struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;
252         const char *name;
253         int len;
254         unsigned long hash;
255 
256         len = qstr->len;
257         name = qstr->name;
258         while (len && name[len-1] == '.')
259                 len--;
260 
261         hash = init_name_hash();
262         while (len--)
263                 hash = partial_name_hash(vfat_tolower(t, *name++), hash);
264         qstr->hash = end_name_hash(hash);
265 
266         return 0;
267 }
268 
269 /*
270  * Case insensitive compare of two vfat names.
271  */
272 static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b)
273 {
274         struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;
275         int alen, blen;
276 
277         /* A filename cannot end in '.' or we treat it like it has none */
278         alen = a->len;
279         blen = b->len;
280         while (alen && a->name[alen-1] == '.')
281                 alen--;
282         while (blen && b->name[blen-1] == '.')
283                 blen--;
284         if (alen == blen) {
285                 if (vfat_strnicmp(t, a->name, b->name, alen) == 0)
286                         return 0;
287         }
288         return 1;
289 }
290 
291 /*
292  * Case sensitive compare of two vfat names.
293  */
294 static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
295 {
296         int alen, blen;
297 
298         /* A filename cannot end in '.' or we treat it like it has none */
299         alen = a->len;
300         blen = b->len;
301         while (alen && a->name[alen-1] == '.')
302                 alen--;
303         while (blen && b->name[blen-1] == '.')
304                 blen--;
305         if (alen == blen) {
306                 if (strncmp(a->name, b->name, alen) == 0)
307                         return 0;
308         }
309         return 1;
310 }
311 
312 #ifdef DEBUG
313 
314 static void
315 check_stack(const char *fname, int lineno)
316 {
317         int stack_level;
318         char *pg_dir;
319 
320         stack_level = (long)(&pg_dir)-current->kernel_stack_page;
321         if (stack_level < 0)
322                 printk("*-*-*-* vfat kstack overflow in %s line %d: SL=%d\n",
323                        fname, lineno, stack_level);
324         else if (stack_level < 500)
325                 printk("*-*-*-* vfat kstack low in %s line %d: SL=%d\n",
326                        fname, lineno, stack_level);
327 #if 0
328         else
329                 printk("------- vfat kstack ok in %s line %d: SL=%d\n",
330                        fname, lineno, stack_level);
331 #endif
332 #if 0
333         if (*(unsigned long *) current->kernel_stack_page != STACK_MAGIC) {
334                 printk("******* vfat stack corruption detected in %s at line %d\n",
335                        fname, lineno);
336         }
337 #endif
338 }
339 
340 static int debug = 0;
341 static void dump_fat(struct super_block *sb,int start)
342 {
343         printk("[");
344         while (start) {
345                 printk("%d ",start);
346                 start = fat_access(sb,start,-1);
347                 if (!start) {
348                         printk("ERROR");
349                         break;
350                 }
351                 if (start == -1) break;
352         }
353         printk("]\n");
354 }
355 
356 static void dump_de(struct msdos_dir_entry *de)
357 {
358         int i;
359         unsigned char *p = (unsigned char *) de;
360         printk("[");
361 
362         for (i = 0; i < 32; i++, p++) {
363                 printk("%02x ", *p);
364         }
365         printk("]\n");
366 }
367 
368 #endif
369 
370 /* MS-DOS "device special files" */
371 
372 static const char *reserved3_names[] = {
373         "con     ", "prn     ", "nul     ", "aux     ", NULL
374 };
375 
376 static const char *reserved4_names[] = {
377         "com1    ", "com2    ", "com3    ", "com4    ", "com5    ",
378         "com6    ", "com7    ", "com8    ", "com9    ",
379         "lpt1    ", "lpt2    ", "lpt3    ", "lpt4    ", "lpt5    ",
380         "lpt6    ", "lpt7    ", "lpt8    ", "lpt9    ",
381         NULL };
382 
383 
384 /* Characters that are undesirable in an MS-DOS file name */
385 
386 static char bad_chars[] = "*?<>|\":/\\";
387 static char replace_chars[] = "[];,+=";
388 
389 /* Checks the validity of a long MS-DOS filename */
390 /* Returns negative number on error, 0 for a normal
391  * return, and 1 for . or .. */
392 
393 static int vfat_valid_longname(const char *name, int len, int xlate)
394 {
395         const char **reserved, *walk;
396         unsigned char c;
397         int i, baselen;
398 
399         if (len && name[len-1] == ' ') return -EINVAL;
400         if (len >= 256) return -EINVAL;
401         for (i = 0; i < len; i++) {
402                 c = name[i];
403                 if (xlate && c == ':') continue;
404                 if (strchr(bad_chars,c)) {
405                         return -EINVAL;
406                 }
407         }
408         if (len < 3) return 0;
409 
410         for (walk = name; *walk != 0 && *walk != '.'; walk++);
411         baselen = walk - name;
412 
413         if (baselen == 3) {
414                 for (reserved = reserved3_names; *reserved; reserved++) {
415                         if (!strnicmp(name,*reserved,baselen))
416                                 return -EINVAL;
417                 }
418         } else if (baselen == 4) {
419                 for (reserved = reserved4_names; *reserved; reserved++) {
420                         if (!strnicmp(name,*reserved,baselen))
421                                 return -EINVAL;
422                 }
423         }
424         return 0;
425 }
426 
427 static int vfat_valid_shortname(struct nls_table *nls, wchar_t *name, int len)
428 {
429         wchar_t *walk;
430         unsigned char c, charbuf[NLS_MAX_CHARSET_SIZE];
431         int chl, chi;
432         int space;
433 
434         if (vfat_uni2upper_short(nls, *name, charbuf, NLS_MAX_CHARSET_SIZE) == 0)
435                 return -EINVAL;
436 
437         if (IS_FREE(charbuf))
438                 return -EINVAL;
439 
440         chl = 0;
441         c = 0;
442         space = 1; /* disallow names starting with a dot */
443         for (walk = name; len && walk-name < 8;) {
444                 len--;
445                 chl = nls->uni2char(*walk++, charbuf, NLS_MAX_CHARSET_SIZE);
446                 if (chl < 0)
447                         return -EINVAL;
448 
449                 for (chi = 0; chi < chl; chi++) {
450                         c = vfat_getupper(nls, charbuf[chi]);
451                         if (!c) return -EINVAL;
452                         if (charbuf[chi] != vfat_tolower(nls, c)) return -EINVAL;
453                         if (strchr(replace_chars,c)) return -EINVAL;
454                         if (c < ' '|| c==':') return -EINVAL;
455                         if (c == '.') goto dot;
456                         space = c == ' ';
457                 }
458         }
459 dot:;
460         if (space) return -EINVAL;
461         if (len && c != '.') {
462                 len--;
463                 if (vfat_uni2upper_short(nls, *walk++, charbuf, NLS_MAX_CHARSET_SIZE) == 1) {
464                         if (charbuf[0] != '.') return -EINVAL;
465                 } else
466                         return -EINVAL;
467                 c = '.';
468         }
469         if (c == '.') {
470                 if (len >= 4) return -EINVAL;
471                 while (len > 0) {
472                         len--;
473                         chl = nls->uni2char(*walk++, charbuf, NLS_MAX_CHARSET_SIZE);
474                         if (chl < 0)
475                                 return -EINVAL;
476                         for (chi = 0; chi < chl; chi++) {
477                                 c = vfat_getupper(nls, charbuf[chi]);
478                                 if (!c) return -EINVAL;
479                                 if (charbuf[chi] != vfat_tolower(nls, c)) return -EINVAL;
480                                 if (strchr(replace_chars,c))
481                                         return -EINVAL;
482                                 if (c < ' ' || c == '.'|| c==':')
483                                         return -EINVAL;
484                                 space = c == ' ';
485                         }
486                 }
487                 if (space) return -EINVAL;
488         }
489 
490         return 0;
491 }
492 
493 static int vfat_find_form(struct inode *dir,char *name)
494 {
495         struct msdos_dir_entry *de;
496         struct buffer_head *bh = NULL;
497         int ino,res;
498 
499         res=fat_scan(dir,name,&bh,&de,&ino);
500         fat_brelse(dir->i_sb, bh);
501         if (res<0)
502                 return -ENOENT;
503         return 0;
504 }
505 
506 static int vfat_format_name(struct nls_table *nls, wchar_t *name,
507                                 int len, char *res)
508 {
509         char *walk;
510         unsigned char charbuf[NLS_MAX_CHARSET_SIZE];
511         int chi, chl;
512         int space;
513 
514         if (vfat_uni2upper_short(nls, *name, charbuf, NLS_MAX_CHARSET_SIZE) == 0)
515                 return -EINVAL;
516 
517         if (IS_FREE(charbuf))
518                 return -EINVAL;
519 
520         space = 1; /* disallow names starting with a dot */
521         for (walk = res; len--; ) {
522                 chl = vfat_uni2upper_short(nls, *name++, charbuf, NLS_MAX_CHARSET_SIZE);
523                 if (chl == 0)
524                         return -EINVAL;
525                 for (chi = 0; chi < chl; chi++){
526                         if (charbuf[chi] == '.') goto dot;
527                         if (!charbuf[chi]) return -EINVAL;
528                         if (walk-res == 8) return -EINVAL;
529                         if (strchr(replace_chars,charbuf[chi])) return -EINVAL;
530                         if (charbuf[chi] < ' '|| charbuf[chi]==':') return -EINVAL;
531                         space = charbuf[chi] == ' ';
532                         *walk = charbuf[chi];
533                         walk++;
534                 }
535         }
536 dot:;
537         if (space) return -EINVAL;
538         if (len >= 0) {
539                 while (walk-res < 8) *walk++ = ' ';
540                 while (len > 0 && walk-res < MSDOS_NAME) {
541                         chl = vfat_uni2upper_short(nls, *name++, charbuf, NLS_MAX_CHARSET_SIZE);
542                         if (len < chl)
543                                 chl = len;
544                         len -= chl;
545                         for (chi = 0; chi < chl; chi++){
546                                 if (!charbuf[chi]) return -EINVAL;
547                                 if (strchr(replace_chars,charbuf[chi]))
548                                         return -EINVAL;
549                                 if (charbuf[chi] < ' ' || charbuf[chi] == '.'|| charbuf[chi]==':')
550                                         return -EINVAL;
551                                 space = charbuf[chi] == ' ';
552                                 *walk++ = charbuf[chi];
553                         }
554                 }
555                 if (space) return -EINVAL;
556                 if (len) return -EINVAL;
557         }
558         while (walk-res < MSDOS_NAME) *walk++ = ' ';
559 
560         return 0;
561 }
562 
563 static char skip_chars[] = ".:\"?<>| ";
564 
565 /* Given a valid longname, create a unique shortname.  Make sure the
566  * shortname does not exist
567  */
568 static int vfat_create_shortname(struct inode *dir, struct nls_table *nls,
569                                         wchar_t *name, int len,
570                                         char *name_res)
571 {
572         wchar_t *ip, *op, *ext_start, *end, *name_start;
573         wchar_t msdos_name[13];
574         char base[9], ext[4], buf[8], *p;
575         unsigned char charbuf[NLS_MAX_CHARSET_SIZE];
576         int chl, chi;
577         int sz, extlen, baselen, i;
578 
579         PRINTK2(("Entering vfat_create_shortname\n"));
580         chl = 0;
581         sz = 0;                 /* Make compiler happy */
582         if (len <= 12) {
583                 /* Do a case insensitive search if the name would be a valid
584                  * shortname if is were all capitalized.  However, do not
585                  * allow spaces in short names because Win95 scandisk does
586                  * not like that */
587                 for (i = 0, op = &msdos_name[0], ip = name; ; i++, ip++, op++) {
588                         if (i == len) {
589                                 if (vfat_format_name(nls, &msdos_name[0], len,
590                                                         name_res) < 0)
591                                         break;
592                                 PRINTK3(("vfat_create_shortname 1\n"));
593                                 if (vfat_find_form(dir, name_res) < 0)
594                                         return 0;
595                                 return -EEXIST;
596                         }
597                         chl = vfat_uni2upper_short(nls, *ip, charbuf, NLS_MAX_CHARSET_SIZE);
598                         for (chi = 0; chi < chl; chi++){
599                                 if (charbuf[chi] == ' ')
600                                         break;
601                         }
602                         if (chi < chl)
603                                 break;
604 
605                         *op = *ip;
606                 }
607         }
608 
609         PRINTK3(("vfat_create_shortname 3\n"));
610         /* Now, we need to create a shortname from the long name */
611         ext_start = end = &name[len];
612         while (--ext_start >= name) {
613                 chl = vfat_uni2upper_short(nls, *ext_start, charbuf, NLS_MAX_CHARSET_SIZE);
614                 for (chi = 0; chi < chl; chi++) {
615                         if (charbuf[chi] == '.') {
616                                 if (ext_start == end - 1) {
617                                         sz = len;
618                                         ext_start = NULL;
619                                 }
620                                 goto stop0;
621                         }
622                 }
623         }
624 stop0:; 
625         if (ext_start == name - 1) {
626                 sz = len;
627                 ext_start = NULL;
628         } else if (ext_start) {
629                 /*
630                  * Names which start with a dot could be just
631                  * an extension eg. "...test".  In this case Win95
632                  * uses the extension as the name and sets no extension.
633                  */
634                 name_start = &name[0];
635                 while (name_start < ext_start)
636                 {
637                         chl = vfat_uni2upper_short(nls, *name_start, charbuf, NLS_MAX_CHARSET_SIZE);
638                         if (chl == 0)
639                                 break;
640                         for (chi = 0; chi < chl; chi++)
641                                 if (!strchr(skip_chars, charbuf[chi])) {
642                                         goto stop1;
643                                 }
644                         name_start++;
645                 }
646 stop1:;         
647                 if (name_start != ext_start) {
648                         sz = ext_start - name;
649                         ext_start++;
650                 } else {
651                         sz = len;
652                         ext_start=NULL;
653                 }
654         }
655 
656         for (baselen = i = 0, p = base, ip = name; i < sz && baselen < 8; i++, ip++)
657         {
658                 chl = vfat_uni2upper_short(nls, *ip, charbuf, NLS_MAX_CHARSET_SIZE);
659                 if (chl == 0){
660                         *p++ = '_';
661                         baselen++;
662                         continue;
663                 }
664 
665                 for (chi = 0; chi < chl; chi++){
666                         if (!strchr(skip_chars, charbuf[chi])){
667                                 if (strchr(replace_chars, charbuf[chi]))
668                                         *p = '_';
669                                 else
670                                         *p = charbuf[chi];
671                                 p++; baselen++;
672                         }
673                 }
674         }
675         if (baselen == 0) {
676                 return -EINVAL;
677         }
678 
679         extlen = 0;
680         if (ext_start) {
681                 for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) {
682                         chl = vfat_uni2upper_short(nls, *ip, charbuf, NLS_MAX_CHARSET_SIZE);
683                         if (chl == 0) {
684                                 *p++ = '_';
685                                 extlen++;
686                                 continue;
687                         }
688 
689                         for (chi = 0; chi < chl; chi++) {
690                                 if (!strchr(skip_chars, charbuf[chi])) {
691                                         if (strchr(replace_chars, charbuf[chi]))
692                                                 *p = '_';
693                                         else
694                                                 *p = charbuf[chi];
695                                         p++; extlen++;
696                                 }
697                         }
698                 }
699         }
700         ext[extlen] = '\0';
701         base[baselen] = '\0';
702 
703         /* Yes, it can happen. ".\xe5" would do it. */
704         if (IS_FREE(base))
705                 base[0]='_';
706 
707         /* OK, at this point we know that base is not longer than 8 symbols,
708          * ext is not longer than 3, base is nonempty, both don't contain
709          * any bad symbols (lowercase transformed to uppercase).
710          */
711 
712         memset(name_res, ' ', MSDOS_NAME);
713         memcpy(name_res,base,baselen);
714         memcpy(name_res+8,ext,extlen);
715         if (MSDOS_SB(dir->i_sb)->options.numtail == 0)
716                 if (vfat_find_form(dir, name_res) < 0)
717                         return 0;
718 
719         /*
720          * Try to find a unique extension.  This used to
721          * iterate through all possibilities sequentially,
722          * but that gave extremely bad performance.  Windows
723          * only tries a few cases before using random
724          * values for part of the base.
725          */
726 
727         if (baselen>6)
728                 baselen = 6;
729         name_res[baselen] = '~';
730         for (i = 1; i < 10; i++) {
731                 name_res[baselen+1] = i + '';
732                 if (vfat_find_form(dir, name_res) < 0)
733                         return 0;
734         }
735 
736         i = jiffies & 0xffff;
737         sz = (jiffies >> 16) & 0x7;
738         if (baselen>2)
739                 baselen = 2;
740         name_res[baselen+4] = '~';
741         name_res[baselen+5] = '1' + sz;
742         while (1) {
743                 sprintf(buf, "%04X", i);
744                 memcpy(&name_res[baselen], buf, 4);
745                 if (vfat_find_form(dir, name_res) < 0)
746                         break;
747                 i -= 11;
748         }
749         return 0;
750 }
751 
752 /* Translate a string, including coded sequences into Unicode */
753 static int
754 xlate_to_uni(const char *name, int len, char *outname, int *longlen, int *outlen,
755              int escape, int utf8, struct nls_table *nls)
756 {
757         const unsigned char *ip;
758         unsigned char nc;
759         char *op;
760         unsigned int ec;
761         int i, k, fill;
762         int charlen;
763 
764         if (utf8) {
765                 *outlen = utf8_mbstowcs((__u16 *) outname, name, PAGE_SIZE);
766                 if (name[len-1] == '.')
767                         *outlen-=2;
768                 op = &outname[*outlen * sizeof(__u16)];
769         } else {
770                 if (name[len-1] == '.') 
771                         len--;
772                 if (nls) {
773                         for (i = 0, ip = name, op = outname, *outlen = 0;
774                              i < len && *outlen <= 260; *outlen += 1)
775                         {
776                                 if (escape && (*ip == ':')) {
777                                         if (i > len - 5)
778                                                 return -EINVAL;
779                                         ec = 0;
780                                         for (k = 1; k < 5; k++) {
781                                                 nc = ip[k];
782                                                 ec <<= 4;
783                                                 if (nc >= '' && nc <= '9') {
784                                                         ec |= nc - '';
785                                                         continue;
786                                                 }
787                                                 if (nc >= 'a' && nc <= 'f') {
788                                                         ec |= nc - ('a' - 10);
789                                                         continue;
790                                                 }
791                                                 if (nc >= 'A' && nc <= 'F') {
792                                                         ec |= nc - ('A' - 10);
793                                                         continue;
794                                                 }
795                                                 return -EINVAL;
796                                         }
797                                         *op++ = ec & 0xFF;
798                                         *op++ = ec >> 8;
799                                         ip += 5;
800                                         i += 5;
801                                 } else {
802                                         if ((charlen = nls->char2uni(ip, len-i, (wchar_t *)op)) < 0)
803                                                 return -EINVAL;
804 
805                                         ip += charlen;
806                                         i += charlen;
807                                         op += 2;
808                                 }
809                         }
810                 } else {
811                         for (i = 0, ip = name, op = outname, *outlen = 0;
812                              i < len && *outlen <= 260; i++, *outlen += 1)
813                         {
814                                 *op++ = *ip++;
815                                 *op++ = 0;
816                         }
817                 }
818         }
819         if (*outlen > 260)
820                 return -ENAMETOOLONG;
821 
822         *longlen = *outlen;
823         if (*outlen % 13) {
824                 *op++ = 0;
825                 *op++ = 0;
826                 *outlen += 1;
827                 if (*outlen % 13) {
828                         fill = 13 - (*outlen % 13);
829                         for (i = 0; i < fill; i++) {
830                                 *op++ = 0xff;
831                                 *op++ = 0xff;
832                         }
833                         *outlen += fill;
834                 }
835         }
836 
837         return 0;
838 }
839 
840 static int
841 vfat_fill_slots(struct inode *dir, struct msdos_dir_slot *ds, const char *name,
842                 int len, int *slots, int uni_xlate)
843 {
844         struct nls_table *nls_io, *nls_disk;
845         wchar_t *uname;
846         struct msdos_dir_slot *ps;
847         struct msdos_dir_entry *de;
848         unsigned long page;
849         unsigned char cksum;
850         const char *ip;
851         char *uniname, msdos_name[MSDOS_NAME];
852         int res, utf8, slot, ulen, unilen, i;
853         loff_t offset;
854 
855         de = (struct msdos_dir_entry *) ds;
856         utf8 = MSDOS_SB(dir->i_sb)->options.utf8;
857         nls_io = MSDOS_SB(dir->i_sb)->nls_io;
858         nls_disk = MSDOS_SB(dir->i_sb)->nls_disk;
859 
860         if (name[len-1] == '.') len--;
861         if(!(page = __get_free_page(GFP_KERNEL)))
862                 return -ENOMEM;
863         uniname = (char *) page;
864 
865         res = xlate_to_uni(name, len, uniname, &ulen, &unilen, uni_xlate,
866                                                                 utf8, nls_io);
867         if (res < 0)
868                 goto out_free;
869 
870         uname = (wchar_t *) page;
871         if (vfat_valid_shortname(nls_disk, uname, ulen) >= 0) {
872                 res = vfat_format_name(nls_disk, uname, ulen, de->name);
873                 if (!res)
874                         goto out_free;
875         }
876 
877         res = vfat_create_shortname(dir, nls_disk, uname, ulen, msdos_name);
878         if (res)
879                 goto out_free;
880 
881         *slots = unilen / 13;
882         for (cksum = i = 0; i < 11; i++) {
883                 cksum = (((cksum&1)<<7)|((cksum&0xfe)>>1)) + msdos_name[i];
884         }
885         PRINTK3(("vfat_fill_slots 3: slots=%d\n",*slots));
886 
887         for (ps = ds, slot = *slots; slot > 0; slot--, ps++) {
888                 ps->id = slot;
889                 ps->attr = ATTR_EXT;
890                 ps->reserved = 0;
891                 ps->alias_checksum = cksum;
892                 ps->start = 0;
893                 offset = (slot - 1) * 26;
894                 ip = &uniname[offset];
895                 memcpy(ps->name0_4, ip, 10);
896                 memcpy(ps->name5_10, ip+10, 12);
897                 memcpy(ps->name11_12, ip+22, 4);
898         }
899         ds[0].id |= 0x40;
900 
901         de = (struct msdos_dir_entry *) ps;
902         PRINTK3(("vfat_fill_slots 9\n"));
903         strncpy(de->name, msdos_name, MSDOS_NAME);
904         (*slots)++;
905 
906 out_free:
907         free_page(page);
908         return res;
909 }
910 
911 /* We can't get "." or ".." here - VFS takes care of those cases */
912 
913 static int vfat_build_slots(struct inode *dir,const char *name,int len,
914      struct msdos_dir_slot *ds, int *slots)
915 {
916         int res, xlate;
917 
918         xlate = MSDOS_SB(dir->i_sb)->options.unicode_xlate;
919         *slots = 1;
920         res = vfat_valid_longname(name, len, xlate);
921         if (res < 0)
922                 return res;
923         return vfat_fill_slots(dir, ds, name, len, slots, xlate);
924 }
925 
926 static int vfat_add_entry(struct inode *dir,struct qstr* qname,
927         int is_dir,struct vfat_slot_info *sinfo_out,
928         struct buffer_head **bh, struct msdos_dir_entry **de)
929 {
930         struct super_block *sb = dir->i_sb;
931         struct msdos_dir_slot *ps;
932         loff_t offset;
933         struct msdos_dir_slot *ds;
934         int slots, slot;
935         int res;
936         struct msdos_dir_entry *de1;
937         struct buffer_head *bh1;
938         int ino;
939         int len;
940         loff_t dummy;
941 
942         ds = (struct msdos_dir_slot *)
943             kmalloc(sizeof(struct msdos_dir_slot)*MSDOS_SLOTS, GFP_KERNEL);
944         if (ds == NULL) return -ENOMEM;
945 
946         len = qname->len;
947         while (len && qname->name[len-1] == '.')
948                 len--;
949         res = fat_search_long(dir, qname->name, len,
950                         (MSDOS_SB(sb)->options.name_check != 's') ||
951                         !MSDOS_SB(sb)->options.posixfs,
952                         &dummy, &dummy);
953         if (res > 0) /* found */
954                 res = -EEXIST;
955         if (res)
956                 goto cleanup;
957 
958         res = vfat_build_slots(dir, qname->name, len, ds, &slots);
959         if (res < 0) goto cleanup;
960 
961         offset = fat_add_entries(dir, slots, &bh1, &de1, &ino);
962         if (offset < 0) {
963                 res = offset;
964                 goto cleanup;
965         }
966         fat_brelse(sb, bh1);
967 
968         /* Now create the new entry */
969         *bh = NULL;
970         for (slot = 0, ps = ds; slot < slots; slot++, ps++) {
971                 if (fat_get_entry(dir,&offset,bh,de, &sinfo_out->ino) < 0) {
972                         res = -EIO;
973                         goto cleanup;
974                 }
975                 memcpy(*de, ps, sizeof(struct msdos_dir_slot));
976                 fat_mark_buffer_dirty(sb, *bh);
977         }
978 
979         dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
980         mark_inode_dirty(dir);
981 
982         fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date);
983         (*de)->ctime_ms = 0;
984         (*de)->ctime = (*de)->time;
985         (*de)->adate = (*de)->cdate = (*de)->date;
986         (*de)->start = 0;
987         (*de)->starthi = 0;
988         (*de)->size = 0;
989         (*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
990         (*de)->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT;
991 
992 
993         fat_mark_buffer_dirty(sb, *bh);
994 
995         /* slots can't be less than 1 */
996         sinfo_out->long_slots = slots - 1;
997         sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots;
998         res = 0;
999 
1000 cleanup:
1001         kfree(ds);
1002         return res;
1003 }
1004 
1005 static int vfat_find(struct inode *dir,struct qstr* qname,
1006         struct vfat_slot_info *sinfo, struct buffer_head **last_bh,
1007         struct msdos_dir_entry **last_de)
1008 {
1009         struct super_block *sb = dir->i_sb;
1010         loff_t offset;
1011         int res,len;
1012 
1013         len = qname->len;
1014         while (len && qname->name[len-1] == '.') 
1015                 len--;
1016         res = fat_search_long(dir, qname->name, len,
1017                         (MSDOS_SB(sb)->options.name_check != 's'),
1018                         &offset,&sinfo->longname_offset);
1019         if (res>0) {
1020                 sinfo->long_slots = res-1;
1021                 if (fat_get_entry(dir,&offset,last_bh,last_de,&sinfo->ino)>=0)
1022                         return 0;
1023                 res = -EIO;
1024         } 
1025         return res ? res : -ENOENT;
1026 }
1027 
1028 struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry)
1029 {
1030         int res;
1031         struct vfat_slot_info sinfo;
1032         struct inode *inode;
1033         struct dentry *alias;
1034         struct buffer_head *bh = NULL;
1035         struct msdos_dir_entry *de;
1036         int table;
1037         
1038         PRINTK2(("vfat_lookup: name=%s, len=%d\n", 
1039                  dentry->d_name.name, dentry->d_name.len));
1040 
1041         table = (MSDOS_SB(dir->i_sb)->options.name_check == 's') ? 2 : 0;
1042         dentry->d_op = &vfat_dentry_ops[table];
1043 
1044         inode = NULL;
1045         res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de);
1046         if (res < 0) {
1047                 table++;
1048                 goto error;
1049         }
1050         inode = fat_build_inode(dir->i_sb, de, sinfo.ino, &res);
1051         fat_brelse(dir->i_sb, bh);
1052         if (res)
1053                 return ERR_PTR(res);
1054         alias = d_find_alias(inode);
1055         if (alias) {
1056                 if (d_invalidate(alias)==0)
1057                         dput(alias);
1058                 else {
1059                         iput(inode);
1060                         return alias;
1061                 }
1062                 
1063         }
1064 error:
1065         dentry->d_op = &vfat_dentry_ops[table];
1066         dentry->d_time = dentry->d_parent->d_inode->i_version;
1067         d_add(dentry,inode);
1068         return NULL;
1069 }
1070 
1071 int vfat_create(struct inode *dir,struct dentry* dentry,int mode)
1072 {
1073         struct super_block *sb = dir->i_sb;
1074         struct inode *inode = NULL;
1075         struct buffer_head *bh = NULL;
1076         struct msdos_dir_entry *de;
1077         struct vfat_slot_info sinfo;
1078         int res;
1079 
1080         res = vfat_add_entry(dir, &dentry->d_name, 0, &sinfo, &bh, &de);
1081         if (res < 0)
1082                 return res;
1083         inode = fat_build_inode(sb, de, sinfo.ino, &res);
1084         fat_brelse(sb, bh);
1085         if (!inode)
1086                 return res;
1087         inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
1088         mark_inode_dirty(inode);
1089         inode->i_version = ++event;
1090         dir->i_version = event;
1091         dentry->d_time = dentry->d_parent->d_inode->i_version;
1092         d_instantiate(dentry,inode);
1093         return 0;
1094 }
1095 
1096 static void vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo,
1097      struct buffer_head *bh, struct msdos_dir_entry *de)
1098 {
1099         struct super_block *sb = dir->i_sb;
1100         loff_t offset;
1101         int i,ino;
1102 
1103         /* remove the shortname */
1104         dir->i_mtime = CURRENT_TIME;
1105         dir->i_atime = CURRENT_TIME;
1106         dir->i_version = ++event;
1107         mark_inode_dirty(dir);
1108         de->name[0] = DELETED_FLAG;
1109         fat_mark_buffer_dirty(sb, bh);
1110         /* remove the longname */
1111         offset = sinfo->longname_offset; de = NULL;
1112         for (i = sinfo->long_slots; i > 0; --i) {
1113                 if (fat_get_entry(dir, &offset, &bh, &de, &ino) < 0)
1114                         continue;
1115                 de->name[0] = DELETED_FLAG;
1116                 de->attr = 0;
1117                 fat_mark_buffer_dirty(sb, bh);
1118         }
1119         if (bh) fat_brelse(sb, bh);
1120 }
1121 
1122 int vfat_rmdir(struct inode *dir,struct dentry* dentry)
1123 {
1124         int res;
1125         struct vfat_slot_info sinfo;
1126         struct buffer_head *bh = NULL;
1127         struct msdos_dir_entry *de;
1128 
1129         res = fat_dir_empty(dentry->d_inode);
1130         if (res)
1131                 return res;
1132 
1133         res = vfat_find(dir,&dentry->d_name,&sinfo, &bh, &de);
1134         if (res<0)
1135                 return res;
1136         dentry->d_inode->i_nlink = 0;
1137         dentry->d_inode->i_mtime = CURRENT_TIME;
1138         dentry->d_inode->i_atime = CURRENT_TIME;
1139         fat_detach(dentry->d_inode);
1140         mark_inode_dirty(dentry->d_inode);
1141         /* releases bh */
1142         vfat_remove_entry(dir,&sinfo,bh,de);
1143         dir->i_nlink--;
1144         return 0;
1145 }
1146 
1147 int vfat_unlink(struct inode *dir, struct dentry* dentry)
1148 {
1149         int res;
1150         struct vfat_slot_info sinfo;
1151         struct buffer_head *bh = NULL;
1152         struct msdos_dir_entry *de;
1153 
1154         PRINTK1(("vfat_unlink: %s\n", dentry->d_name.name));
1155         res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de);
1156         if (res < 0)
1157                 return res;
1158         dentry->d_inode->i_nlink = 0;
1159         dentry->d_inode->i_mtime = CURRENT_TIME;
1160         dentry->d_inode->i_atime = CURRENT_TIME;
1161         fat_detach(dentry->d_inode);
1162         mark_inode_dirty(dentry->d_inode);
1163         /* releases bh */
1164         vfat_remove_entry(dir,&sinfo,bh,de);
1165 
1166         return res;
1167 }
1168 
1169 
1170 int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode)
1171 {
1172         struct super_block *sb = dir->i_sb;
1173         struct inode *inode = NULL;
1174         struct vfat_slot_info sinfo;
1175         struct buffer_head *bh = NULL;
1176         struct msdos_dir_entry *de;
1177         int res;
1178 
1179         res = vfat_add_entry(dir, &dentry->d_name, 1, &sinfo, &bh, &de);
1180         if (res < 0)
1181                 return res;
1182         inode = fat_build_inode(sb, de, sinfo.ino, &res);
1183         if (!inode)
1184                 goto out;
1185         inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
1186         mark_inode_dirty(inode);
1187         inode->i_version = ++event;
1188         dir->i_version = event;
1189         dir->i_nlink++;
1190         inode->i_nlink = 2; /* no need to mark them dirty */
1191         res = fat_new_dir(inode, dir, 1);
1192         if (res < 0)
1193                 goto mkdir_failed;
1194         dentry->d_time = dentry->d_parent->d_inode->i_version;
1195         d_instantiate(dentry,inode);
1196 out:
1197         fat_brelse(sb, bh);
1198         return res;
1199 
1200 mkdir_failed:
1201         inode->i_nlink = 0;
1202         inode->i_mtime = CURRENT_TIME;
1203         inode->i_atime = CURRENT_TIME;
1204         fat_detach(inode);
1205         mark_inode_dirty(inode);
1206         /* releases bh */
1207         vfat_remove_entry(dir,&sinfo,bh,de);
1208         iput(inode);
1209         dir->i_nlink--;
1210         return res;
1211 }
1212  
1213 int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
1214                 struct inode *new_dir,struct dentry *new_dentry)
1215 {
1216         struct super_block *sb = old_dir->i_sb;
1217         struct buffer_head *old_bh,*new_bh,*dotdot_bh;
1218         struct msdos_dir_entry *old_de,*new_de,*dotdot_de;
1219         int dotdot_ino;
1220         struct inode *old_inode, *new_inode;
1221         int res, is_dir;
1222         struct vfat_slot_info old_sinfo,sinfo;
1223 
1224         old_bh = new_bh = dotdot_bh = NULL;
1225         old_inode = old_dentry->d_inode;
1226         new_inode = new_dentry->d_inode;
1227         res = vfat_find(old_dir,&old_dentry->d_name,&old_sinfo,&old_bh,&old_de);
1228         PRINTK3(("vfat_rename 2\n"));
1229         if (res < 0) goto rename_done;
1230 
1231         is_dir = S_ISDIR(old_inode->i_mode);
1232 
1233         if (is_dir && (res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
1234                                 &dotdot_de,&dotdot_ino)) < 0)
1235                 goto rename_done;
1236 
1237         if (new_dentry->d_inode) {
1238                 res = vfat_find(new_dir,&new_dentry->d_name,&sinfo,&new_bh,
1239                                 &new_de);
1240                 if (res < 0 || MSDOS_I(new_inode)->i_location != sinfo.ino) {
1241                         /* WTF??? Cry and fail. */
1242                         printk(KERN_WARNING "vfat_rename: fs corrupted\n");
1243                         goto rename_done;
1244                 }
1245 
1246                 if (is_dir) {
1247                         res = fat_dir_empty(new_inode);
1248                         if (res)
1249                                 goto rename_done;
1250                 }
1251                 fat_detach(new_inode);
1252         } else {
1253                 res = vfat_add_entry(new_dir,&new_dentry->d_name,is_dir,&sinfo,
1254                                         &new_bh,&new_de);
1255                 if (res < 0) goto rename_done;
1256         }
1257 
1258         new_dir->i_version = ++event;
1259 
1260         /* releases old_bh */
1261         vfat_remove_entry(old_dir,&old_sinfo,old_bh,old_de);
1262         old_bh=NULL;
1263         fat_detach(old_inode);
1264         fat_attach(old_inode, sinfo.ino);
1265         mark_inode_dirty(old_inode);
1266 
1267         old_dir->i_version = ++event;
1268         old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
1269         mark_inode_dirty(old_dir);
1270         if (new_inode) {
1271                 new_inode->i_nlink--;
1272                 new_inode->i_ctime=CURRENT_TIME;
1273         }
1274 
1275         if (is_dir) {
1276                 int start = MSDOS_I(new_dir)->i_logstart;
1277                 dotdot_de->start = CT_LE_W(start);
1278                 dotdot_de->starthi = CT_LE_W(start>>16);
1279                 fat_mark_buffer_dirty(sb, dotdot_bh);
1280                 old_dir->i_nlink--;
1281                 if (new_inode) {
1282                         new_inode->i_nlink--;
1283                 } else {
1284                         new_dir->i_nlink++;
1285                         mark_inode_dirty(new_dir);
1286                 }
1287         }
1288 
1289 rename_done:
1290         fat_brelse(sb, dotdot_bh);
1291         fat_brelse(sb, old_bh);
1292         fat_brelse(sb, new_bh);
1293         return res;
1294 
1295 }
1296 
1297 
1298 /* Public inode operations for the VFAT fs */
1299 struct inode_operations vfat_dir_inode_operations = {
1300         create:         vfat_create,
1301         lookup:         vfat_lookup,
1302         unlink:         vfat_unlink,
1303         mkdir:          vfat_mkdir,
1304         rmdir:          vfat_rmdir,
1305         rename:         vfat_rename,
1306         setattr:        fat_notify_change,
1307 };
1308 
1309 struct super_block *vfat_read_super(struct super_block *sb,void *data,
1310                                     int silent)
1311 {
1312         struct super_block *res;
1313   
1314         MSDOS_SB(sb)->options.isvfat = 1;
1315 
1316         res = fat_read_super(sb, data, silent, &vfat_dir_inode_operations);
1317         if (res == NULL)
1318                 return NULL;
1319 
1320         if (parse_options((char *) data, &(MSDOS_SB(sb)->options))) {
1321                 MSDOS_SB(sb)->options.dotsOK = 0;
1322                 if (MSDOS_SB(sb)->options.posixfs) {
1323                         MSDOS_SB(sb)->options.name_check = 's';
1324                 }
1325                 if (MSDOS_SB(sb)->options.name_check != 's') {
1326                         sb->s_root->d_op = &vfat_dentry_ops[0];
1327                 } else {
1328                         sb->s_root->d_op = &vfat_dentry_ops[2];
1329                 }
1330         }
1331 
1332         return res;
1333 }
1334 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.