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 */
msdos_put_super(struct super_block * sb)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. */
msdos_format_name(const char * name,int len,char * res,struct fat_mount_options * opts)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. */
msdos_find(struct inode * dir,const char * name,int len,struct buffer_head ** bh,struct msdos_dir_entry ** de,loff_t * i_pos)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  */
msdos_hash(struct dentry * dentry,struct qstr * qstr)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  */
msdos_cmp(struct dentry * dentry,struct qstr * a,struct qstr * b)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 */
msdos_lookup(struct inode * dir,struct dentry * dentry)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). */
msdos_add_entry(struct inode * dir,const char * name,struct buffer_head ** bh,struct msdos_dir_entry ** de,loff_t * i_pos,int is_dir,int is_hid)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 */
msdos_create(struct inode * dir,struct dentry * dentry,int mode)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 */
msdos_rmdir(struct inode * dir,struct dentry * dentry)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 */
msdos_mkdir(struct inode * dir,struct dentry * dentry,int mode)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 */
msdos_unlink(struct inode * dir,struct dentry * dentry)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 
do_msdos_rename(struct inode * old_dir,char * old_name,struct dentry * old_dentry,struct inode * new_dir,char * new_name,struct dentry * new_dentry,struct buffer_head * old_bh,struct msdos_dir_entry * old_de,loff_t old_i_pos,int is_hid)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 */
msdos_rename(struct inode * old_dir,struct dentry * old_dentry,struct inode * new_dir,struct dentry * new_dentry)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 
msdos_read_super(struct super_block * sb,void * data,int silent)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 }
601