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
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.