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