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

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

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

  1 /*
  2  *  linux/fs/msdos/namei.c
  3  *
  4  *  Written 1992,1993 by Werner Almesberger
  5  *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
  6  *  Rewritten for constant inumbers 1999 by Al Viro
  7  */
  8 
  9 
 10 #define __NO_VERSION__
 11 #include <linux/module.h>
 12 
 13 #include <linux/sched.h>
 14 #include <linux/msdos_fs.h>
 15 #include <linux/errno.h>
 16 #include <linux/string.h>
 17 
 18 #include <asm/uaccess.h>
 19 
 20 #include "../fat/msbuffer.h"
 21 
 22 #define MSDOS_DEBUG 0
 23 #define PRINTK(x)
 24 
 25 /* MS-DOS "device special files" */
 26 
 27 static const char *reserved_names[] = {
 28     "CON     ","PRN     ","NUL     ","AUX     ",
 29     "LPT1    ","LPT2    ","LPT3    ","LPT4    ",
 30     "COM1    ","COM2    ","COM3    ","COM4    ",
 31     NULL };
 32 
 33 
 34 /* Characters that are undesirable in an MS-DOS file name */
 35   
 36 static char bad_chars[] = "*?<>|\"";
 37 static char bad_if_strict_pc[] = "+=,; ";
 38 static char bad_if_strict_atari[] = " "; /* GEMDOS is less restrictive */
 39 #define bad_if_strict(opts) ((opts)->atari ? bad_if_strict_atari : bad_if_strict_pc)
 40 
 41 /* Must die */
 42 void msdos_put_super(struct super_block *sb)
 43 {
 44         fat_put_super(sb);
 45 }
 46 
 47 /***** Formats an MS-DOS file name. Rejects invalid names. */
 48 static int msdos_format_name(const char *name,int len,
 49         char *res,struct fat_mount_options *opts)
 50         /* conv is relaxed/normal/strict, name is proposed name,
 51          * len is the length of the proposed name, res is the result name,
 52          * dotsOK is if hidden files get dots.
 53          */
 54 {
 55         char *walk;
 56         const char **reserved;
 57         unsigned char c;
 58         int space;
 59 
 60         if (name[0] == '.') {  /* dotfile because . and .. already done */
 61                 if (opts->dotsOK) {
 62                         /* Get rid of dot - test for it elsewhere */
 63                         name++; len--;
 64                 }
 65                 else if (!opts->atari) return -EINVAL;
 66         }
 67         /* disallow names that _really_ start with a dot for MS-DOS, GEMDOS does
 68          * not care */
 69         space = !opts->atari;
 70         c = 0;
 71         for (walk = res; len && walk-res < 8; walk++) {
 72                 c = *name++;
 73                 len--;
 74                 if (opts->conversion != 'r' && strchr(bad_chars,c))
 75                         return -EINVAL;
 76                 if (opts->conversion == 's' && strchr(bad_if_strict(opts),c))
 77                         return -EINVAL;
 78                 if (c >= 'A' && c <= 'Z' && opts->conversion == 's')
 79                         return -EINVAL;
 80                 if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
 81 /*  0xE5 is legal as a first character, but we must substitute 0x05     */
 82 /*  because 0xE5 marks deleted files.  Yes, DOS really does this.       */
 83 /*  It seems that Microsoft hacked DOS to support non-US characters     */
 84 /*  after the 0xE5 character was already in use to mark deleted files.  */
 85                 if((res==walk) && (c==0xE5)) c=0x05;
 86                 if (c == '.') break;
 87                 space = (c == ' ');
 88                 *walk = (!opts->nocase && c >= 'a' && c <= 'z') ? c-32 : c;
 89         }
 90         if (space) return -EINVAL;
 91         if (opts->conversion == 's' && len && c != '.') {
 92                 c = *name++;
 93                 len--;
 94                 if (c != '.') return -EINVAL;
 95         }
 96         while (c != '.' && len--) c = *name++;
 97         if (c == '.') {
 98                 while (walk-res < 8) *walk++ = ' ';
 99                 while (len > 0 && walk-res < MSDOS_NAME) {
100                         c = *name++;
101                         len--;
102                         if (opts->conversion != 'r' && strchr(bad_chars,c))
103                                 return -EINVAL;
104                         if (opts->conversion == 's' &&
105                             strchr(bad_if_strict(opts),c))
106                                 return -EINVAL;
107                         if (c < ' ' || c == ':' || c == '\\')
108                                 return -EINVAL;
109                         if (c == '.') {
110                                 if (opts->conversion == 's')
111                                         return -EINVAL;
112                                 break;
113                         }
114                         if (c >= 'A' && c <= 'Z' && opts->conversion == 's')
115                                 return -EINVAL;
116                         space = c == ' ';
117                         *walk++ = (!opts->nocase && c >= 'a' && c <= 'z') ? c-32 : c;
118                 }
119                 if (space) return -EINVAL;
120                 if (opts->conversion == 's' && len) return -EINVAL;
121         }
122         while (walk-res < MSDOS_NAME) *walk++ = ' ';
123         if (!opts->atari)
124                 /* GEMDOS is less stupid and has no reserved names */
125                 for (reserved = reserved_names; *reserved; reserved++)
126                         if (!strncmp(res,*reserved,8)) return -EINVAL;
127         return 0;
128 }
129 
130 /***** Locates a directory entry.  Uses unformatted name. */
131 static int msdos_find(struct inode *dir,const char *name,int len,
132     struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
133 {
134         int res;
135         char dotsOK;
136         char msdos_name[MSDOS_NAME];
137 
138         dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK;
139         res = msdos_format_name(name,len, msdos_name,&MSDOS_SB(dir->i_sb)->options);
140         if (res < 0)
141                 return -ENOENT;
142         res = fat_scan(dir,msdos_name,bh,de,ino);
143         if (!res && dotsOK) {
144                 if (name[0]=='.') {
145                         if (!((*de)->attr & ATTR_HIDDEN))
146                                 res = -ENOENT;
147                 } else {
148                         if ((*de)->attr & ATTR_HIDDEN)
149                                 res = -ENOENT;
150                 }
151         }
152         return res;
153 
154 }
155 
156 /*
157  * Compute the hash for the msdos name corresponding to the dentry.
158  * Note: if the name is invalid, we leave the hash code unchanged so
159  * that the existing dentry can be used. The msdos fs routines will
160  * return ENOENT or EINVAL as appropriate.
161  */
162 static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
163 {
164         struct fat_mount_options *options = & (MSDOS_SB(dentry->d_sb)->options);
165         int error;
166         char msdos_name[MSDOS_NAME];
167         
168         error = msdos_format_name(qstr->name, qstr->len, msdos_name, options);
169         if (!error)
170                 qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
171         return 0;
172 }
173 
174 /*
175  * Compare two msdos names. If either of the names are invalid,
176  * we fall back to doing the standard name comparison.
177  */
178 static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
179 {
180         struct fat_mount_options *options = & (MSDOS_SB(dentry->d_sb)->options);
181         int error;
182         char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
183 
184         error = msdos_format_name(a->name, a->len, a_msdos_name, options);
185         if (error)
186                 goto old_compare;
187         error = msdos_format_name(b->name, b->len, b_msdos_name, options);
188         if (error)
189                 goto old_compare;
190         error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
191 out:
192         return error;
193 
194 old_compare:
195         error = 1;
196         if (a->len == b->len)
197                 error = memcmp(a->name, b->name, a->len);
198         goto out;
199 }
200 
201 
202 static struct dentry_operations msdos_dentry_operations = {
203         d_hash:         msdos_hash,
204         d_compare:      msdos_cmp,
205 };
206 
207 /*
208  * AV. Wrappers for FAT sb operations. Is it wise?
209  */
210 
211 /***** Get inode using directory and name */
212 struct dentry *msdos_lookup(struct inode *dir,struct dentry *dentry)
213 {
214         struct super_block *sb = dir->i_sb;
215         struct inode *inode = NULL;
216         struct msdos_dir_entry *de;
217         struct buffer_head *bh = NULL;
218         int ino,res;
219         
220         PRINTK (("msdos_lookup\n"));
221 
222         dentry->d_op = &msdos_dentry_operations;
223 
224         res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &bh,
225                         &de, &ino);
226 
227         if (res == -ENOENT)
228                 goto add;
229         if (res < 0)
230                 goto out;
231         inode = fat_build_inode(sb, de, ino, &res);
232         if (res)
233                 goto out;
234 add:
235         d_add(dentry, inode);
236         res = 0;
237 out:
238         if (bh)
239                 fat_brelse(sb, bh);
240         return ERR_PTR(res);
241 }
242 
243 /***** Creates a directory entry (name is already formatted). */
244 static int msdos_add_entry(struct inode *dir, const char *name,
245                            struct buffer_head **bh,
246                            struct msdos_dir_entry **de,
247                            int *ino,
248                            int is_dir, int is_hid)
249 {
250         struct super_block *sb = dir->i_sb;
251         int res;
252 
253         if ((res = fat_add_entries(dir, 1, bh, de, ino))<0)
254                 return res;
255         /*
256          * XXX all times should be set by caller upon successful completion.
257          */
258         dir->i_ctime = dir->i_mtime = CURRENT_TIME;
259         mark_inode_dirty(dir);
260         memcpy((*de)->name,name,MSDOS_NAME);
261         (*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
262         if (is_hid)
263                 (*de)->attr |= ATTR_HIDDEN;
264         (*de)->start = 0;
265         (*de)->starthi = 0;
266         fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date);
267         (*de)->size = 0;
268         fat_mark_buffer_dirty(sb, *bh);
269         return 0;
270 }
271 
272 /*
273  * AV. Huh??? It's exported. Oughtta check usage.
274  */
275 
276 /***** Create a file */
277 int msdos_create(struct inode *dir,struct dentry *dentry,int mode)
278 {
279         struct super_block *sb = dir->i_sb;
280         struct buffer_head *bh;
281         struct msdos_dir_entry *de;
282         struct inode *inode;
283         int ino,res,is_hid;
284         char msdos_name[MSDOS_NAME];
285 
286         res = msdos_format_name(dentry->d_name.name,dentry->d_name.len,
287                                 msdos_name, &MSDOS_SB(sb)->options);
288         if (res < 0)
289                 return res;
290         is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
291         /* Have to do it due to foo vs. .foo conflicts */
292         if (fat_scan(dir,msdos_name,&bh,&de,&ino) >= 0) {
293                 fat_brelse(sb, bh);
294                 return -EINVAL;
295         }
296         inode = NULL;
297         res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 0, is_hid);
298         if (res)
299                 return res;
300         inode = fat_build_inode(dir->i_sb, de, ino, &res);
301         fat_brelse(sb, bh);
302         if (!inode)
303                 return res;
304         inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
305         mark_inode_dirty(inode);
306         d_instantiate(dentry, inode);
307         return 0;
308 }
309 
310 /***** Remove a directory */
311 int msdos_rmdir(struct inode *dir, struct dentry *dentry)
312 {
313         struct super_block *sb = dir->i_sb;
314         struct inode *inode = dentry->d_inode;
315         int res,ino;
316         struct buffer_head *bh;
317         struct msdos_dir_entry *de;
318 
319         bh = NULL;
320         res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
321                                 &bh, &de, &ino);
322         if (res < 0)
323                 goto rmdir_done;
324         /*
325          * Check whether the directory is not in use, then check
326          * whether it is empty.
327          */
328         res = fat_dir_empty(inode);
329         if (res)
330                 goto rmdir_done;
331 
332         de->name[0] = DELETED_FLAG;
333         fat_mark_buffer_dirty(sb, bh);
334         fat_detach(inode);
335         inode->i_nlink = 0;
336         inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
337         dir->i_nlink--;
338         mark_inode_dirty(inode);
339         mark_inode_dirty(dir);
340         res = 0;
341 
342 rmdir_done:
343         fat_brelse(sb, bh);
344         return res;
345 }
346 
347 /***** Make a directory */
348 int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode)
349 {
350         struct super_block *sb = dir->i_sb;
351         struct buffer_head *bh;
352         struct msdos_dir_entry *de;
353         struct inode *inode;
354         int res,is_hid;
355         char msdos_name[MSDOS_NAME];
356         int ino;
357 
358         res = msdos_format_name(dentry->d_name.name,dentry->d_name.len,
359                                 msdos_name, &MSDOS_SB(sb)->options);
360         if (res < 0)
361                 return res;
362         is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
363         /* foo vs .foo situation */
364         if (fat_scan(dir,msdos_name,&bh,&de,&ino) >= 0)
365                 goto out_exist;
366 
367         res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 1, is_hid);
368         if (res)
369                 goto out_unlock;
370         inode = fat_build_inode(dir->i_sb, de, ino, &res);
371         if (!inode) {
372                 fat_brelse(sb, bh);
373                 goto out_unlock;
374         }
375         res = 0;
376 
377         dir->i_nlink++;
378         inode->i_nlink = 2; /* no need to mark them dirty */
379 
380         res = fat_new_dir(inode, dir, 0);
381         if (res)
382                 goto mkdir_error;
383 
384         fat_brelse(sb, bh);
385         d_instantiate(dentry, inode);
386         res = 0;
387 
388 out_unlock:
389         return res;
390 
391 mkdir_error:
392         printk("msdos_mkdir: error=%d, attempting cleanup\n", res);
393         inode->i_nlink = 0;
394         inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
395         dir->i_nlink--;
396         mark_inode_dirty(inode);
397         mark_inode_dirty(dir);
398         de->name[0] = DELETED_FLAG;
399         fat_mark_buffer_dirty(sb, bh);
400         fat_brelse(sb, bh);
401         fat_detach(inode);
402         iput(inode);
403         goto out_unlock;
404 
405 out_exist:
406         fat_brelse(sb, bh);
407         res = -EINVAL;
408         goto out_unlock;
409 }
410 
411 /***** Unlink a file */
412 int msdos_unlink( struct inode *dir, struct dentry *dentry)
413 {
414         struct super_block *sb = dir->i_sb;
415         struct inode *inode = dentry->d_inode;
416         int res,ino;
417         struct buffer_head *bh;
418         struct msdos_dir_entry *de;
419 
420         bh = NULL;
421         res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
422                         &bh, &de, &ino);
423         if (res < 0)
424                 goto unlink_done;
425 
426         de->name[0] = DELETED_FLAG;
427         fat_mark_buffer_dirty(sb, bh);
428         fat_detach(inode);
429         fat_brelse(sb, bh);
430         inode->i_nlink = 0;
431         inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
432         mark_inode_dirty(inode);
433         mark_inode_dirty(dir);
434         res = 0;
435 unlink_done:
436         return res;
437 }
438 
439 static int do_msdos_rename(struct inode *old_dir, char *old_name,
440     struct dentry *old_dentry,
441     struct inode *new_dir,char *new_name, struct dentry *new_dentry,
442     struct buffer_head *old_bh,
443     struct msdos_dir_entry *old_de, int old_ino, int is_hid)
444 {
445         struct super_block *sb = old_dir->i_sb;
446         struct buffer_head *new_bh=NULL,*dotdot_bh=NULL;
447         struct msdos_dir_entry *new_de,*dotdot_de;
448         struct inode *old_inode,*new_inode;
449         int new_ino,dotdot_ino;
450         int error;
451         int is_dir;
452 
453         old_inode = old_dentry->d_inode;
454         new_inode = new_dentry->d_inode;
455         is_dir = S_ISDIR(old_inode->i_mode);
456 
457         if (fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino)>=0 &&!new_inode)
458                 goto degenerate_case;
459         if (is_dir) {
460                 if (new_inode) {
461                         error = fat_dir_empty(new_inode);
462                         if (error)
463                                 goto out;
464                 }
465                 error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh,
466                                 &dotdot_de, &dotdot_ino);
467                 if (error < 0) {
468                         printk(KERN_WARNING
469                                 "MSDOS: %s/%s, get dotdot failed, ret=%d\n",
470                                 old_dentry->d_parent->d_name.name,
471                                 old_dentry->d_name.name, error);
472                         goto out;
473                 }
474         }
475         if (!new_bh) {
476                 error = msdos_add_entry(new_dir, new_name, &new_bh, &new_de,
477                                         &new_ino, is_dir, is_hid);
478                 if (error)
479                         goto out;
480         }
481         new_dir->i_version = ++event;
482 
483         /* There we go */
484 
485         if (new_inode)
486                 fat_detach(new_inode);
487         old_de->name[0] = DELETED_FLAG;
488         fat_mark_buffer_dirty(sb, old_bh);
489         fat_detach(old_inode);
490         fat_attach(old_inode, new_ino);
491         if (is_hid)
492                 MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
493         else
494                 MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
495         mark_inode_dirty(old_inode);
496         old_dir->i_version = ++event;
497         old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
498         mark_inode_dirty(old_dir);
499         if (new_inode) {
500                 new_inode->i_nlink--;
501                 new_inode->i_ctime = CURRENT_TIME;
502                 mark_inode_dirty(new_inode);
503         }
504         if (dotdot_bh) {
505                 dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart);
506                 dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16);
507                 fat_mark_buffer_dirty(sb, dotdot_bh);
508                 old_dir->i_nlink--;
509                 mark_inode_dirty(old_dir);
510                 if (new_inode) {
511                         new_inode->i_nlink--;
512                         mark_inode_dirty(new_inode);
513                 } else {
514                         new_dir->i_nlink++;
515                         mark_inode_dirty(new_dir);
516                 }
517         }
518         error = 0;
519 out:
520         fat_brelse(sb, new_bh);
521         fat_brelse(sb, dotdot_bh);
522         return error;
523 
524 degenerate_case:
525         error = -EINVAL;
526         if (new_de!=old_de)
527                 goto out;
528         if (is_hid)
529                 MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
530         else
531                 MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
532         mark_inode_dirty(old_inode);
533         old_dir->i_version = ++event;
534         old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
535         mark_inode_dirty(old_dir);
536         return 0;
537 }
538 
539 /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
540 int msdos_rename(struct inode *old_dir,struct dentry *old_dentry,
541                  struct inode *new_dir,struct dentry *new_dentry)
542 {
543         struct super_block *sb = old_dir->i_sb;
544         struct buffer_head *old_bh;
545         struct msdos_dir_entry *old_de;
546         int old_ino, error;
547         int is_hid,old_hid; /* if new file and old file are hidden */
548         char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
549 
550         error = msdos_format_name(old_dentry->d_name.name,
551                                   old_dentry->d_name.len,old_msdos_name,
552                                   &MSDOS_SB(old_dir->i_sb)->options);
553         if (error < 0)
554                 goto rename_done;
555         error = msdos_format_name(new_dentry->d_name.name,
556                                   new_dentry->d_name.len,new_msdos_name,
557                                   &MSDOS_SB(new_dir->i_sb)->options);
558         if (error < 0)
559                 goto rename_done;
560 
561         is_hid  = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.');
562         old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.');
563         error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de, &old_ino);
564         if (error < 0)
565                 goto rename_done;
566 
567         error = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
568                                 new_dir, new_msdos_name, new_dentry,
569                                 old_bh, old_de, (ino_t)old_ino, is_hid);
570         fat_brelse(sb, old_bh);
571 
572 rename_done:
573         return error;
574 }
575 
576 
577 /* The public inode operations for the msdos fs */
578 struct inode_operations msdos_dir_inode_operations = {
579         create:         msdos_create,
580         lookup:         msdos_lookup,
581         unlink:         msdos_unlink,
582         mkdir:          msdos_mkdir,
583         rmdir:          msdos_rmdir,
584         rename:         msdos_rename,
585         setattr:        fat_notify_change,
586 };
587 
588 struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
589 {
590         struct super_block *res;
591 
592         MSDOS_SB(sb)->options.isvfat = 0;
593         res = fat_read_super(sb, data, silent, &msdos_dir_inode_operations);
594         if (res)
595                 sb->s_root->d_op = &msdos_dentry_operations;
596         return res;
597 }
598 

~ [ 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.