Linux Audio

Check our new training course

Loading...
v3.15
  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#include <linux/module.h>
 10#include <linux/time.h>
 11#include <linux/buffer_head.h>
 12#include "fat.h"
 13
 14/* Characters that are undesirable in an MS-DOS file name */
 15static unsigned char bad_chars[] = "*?<>|\"";
 16static unsigned char bad_if_strict[] = "+=,; ";
 17
 18/***** Formats an MS-DOS file name. Rejects invalid names. */
 19static int msdos_format_name(const unsigned char *name, int len,
 20			     unsigned char *res, struct fat_mount_options *opts)
 21	/*
 22	 * name is the proposed name, len is its length, res is
 23	 * the resulting name, opts->name_check is either (r)elaxed,
 24	 * (n)ormal or (s)trict, opts->dotsOK allows dots at the
 25	 * beginning of name (for hidden files)
 26	 */
 27{
 28	unsigned char *walk;
 29	unsigned char c;
 30	int space;
 31
 32	if (name[0] == '.') {	/* dotfile because . and .. already done */
 33		if (opts->dotsOK) {
 34			/* Get rid of dot - test for it elsewhere */
 35			name++;
 36			len--;
 37		} else
 38			return -EINVAL;
 39	}
 40	/*
 41	 * disallow names that _really_ start with a dot
 42	 */
 43	space = 1;
 44	c = 0;
 45	for (walk = res; len && walk - res < 8; walk++) {
 46		c = *name++;
 47		len--;
 48		if (opts->name_check != 'r' && strchr(bad_chars, c))
 49			return -EINVAL;
 50		if (opts->name_check == 's' && strchr(bad_if_strict, c))
 51			return -EINVAL;
 52		if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
 53			return -EINVAL;
 54		if (c < ' ' || c == ':' || c == '\\')
 55			return -EINVAL;
 56	/*
 57	 * 0xE5 is legal as a first character, but we must substitute
 58	 * 0x05 because 0xE5 marks deleted files.  Yes, DOS really
 59	 * does this.
 60	 * It seems that Microsoft hacked DOS to support non-US
 61	 * characters after the 0xE5 character was already in use to
 62	 * mark deleted files.
 63	 */
 64		if ((res == walk) && (c == 0xE5))
 65			c = 0x05;
 66		if (c == '.')
 67			break;
 68		space = (c == ' ');
 69		*walk = (!opts->nocase && c >= 'a' && c <= 'z') ? c - 32 : c;
 70	}
 71	if (space)
 72		return -EINVAL;
 73	if (opts->name_check == 's' && len && c != '.') {
 74		c = *name++;
 75		len--;
 76		if (c != '.')
 77			return -EINVAL;
 78	}
 79	while (c != '.' && len--)
 80		c = *name++;
 81	if (c == '.') {
 82		while (walk - res < 8)
 83			*walk++ = ' ';
 84		while (len > 0 && walk - res < MSDOS_NAME) {
 85			c = *name++;
 86			len--;
 87			if (opts->name_check != 'r' && strchr(bad_chars, c))
 88				return -EINVAL;
 89			if (opts->name_check == 's' &&
 90			    strchr(bad_if_strict, c))
 91				return -EINVAL;
 92			if (c < ' ' || c == ':' || c == '\\')
 93				return -EINVAL;
 94			if (c == '.') {
 95				if (opts->name_check == 's')
 96					return -EINVAL;
 97				break;
 98			}
 99			if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
100				return -EINVAL;
101			space = c == ' ';
102			if (!opts->nocase && c >= 'a' && c <= 'z')
103				*walk++ = c - 32;
104			else
105				*walk++ = c;
106		}
107		if (space)
108			return -EINVAL;
109		if (opts->name_check == 's' && len)
110			return -EINVAL;
111	}
112	while (walk - res < MSDOS_NAME)
113		*walk++ = ' ';
114
115	return 0;
116}
117
118/***** Locates a directory entry.  Uses unformatted name. */
119static int msdos_find(struct inode *dir, const unsigned char *name, int len,
120		      struct fat_slot_info *sinfo)
121{
122	struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
123	unsigned char msdos_name[MSDOS_NAME];
124	int err;
125
126	err = msdos_format_name(name, len, msdos_name, &sbi->options);
127	if (err)
128		return -ENOENT;
129
130	err = fat_scan(dir, msdos_name, sinfo);
131	if (!err && sbi->options.dotsOK) {
132		if (name[0] == '.') {
133			if (!(sinfo->de->attr & ATTR_HIDDEN))
134				err = -ENOENT;
135		} else {
136			if (sinfo->de->attr & ATTR_HIDDEN)
137				err = -ENOENT;
138		}
139		if (err)
140			brelse(sinfo->bh);
141	}
142	return err;
143}
144
145/*
146 * Compute the hash for the msdos name corresponding to the dentry.
147 * Note: if the name is invalid, we leave the hash code unchanged so
148 * that the existing dentry can be used. The msdos fs routines will
149 * return ENOENT or EINVAL as appropriate.
150 */
151static int msdos_hash(const struct dentry *dentry, struct qstr *qstr)
152{
153	struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
154	unsigned char msdos_name[MSDOS_NAME];
155	int error;
156
157	error = msdos_format_name(qstr->name, qstr->len, msdos_name, options);
158	if (!error)
159		qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
160	return 0;
161}
162
163/*
164 * Compare two msdos names. If either of the names are invalid,
165 * we fall back to doing the standard name comparison.
166 */
167static int msdos_cmp(const struct dentry *parent, const struct dentry *dentry,
168		unsigned int len, const char *str, const struct qstr *name)
169{
170	struct fat_mount_options *options = &MSDOS_SB(parent->d_sb)->options;
171	unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
172	int error;
173
174	error = msdos_format_name(name->name, name->len, a_msdos_name, options);
175	if (error)
176		goto old_compare;
177	error = msdos_format_name(str, len, b_msdos_name, options);
178	if (error)
179		goto old_compare;
180	error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
181out:
182	return error;
183
184old_compare:
185	error = 1;
186	if (name->len == len)
187		error = memcmp(name->name, str, len);
188	goto out;
189}
190
191static const struct dentry_operations msdos_dentry_operations = {
192	.d_hash		= msdos_hash,
193	.d_compare	= msdos_cmp,
194};
195
196/*
197 * AV. Wrappers for FAT sb operations. Is it wise?
198 */
199
200/***** Get inode using directory and name */
201static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
202				   unsigned int flags)
203{
204	struct super_block *sb = dir->i_sb;
205	struct fat_slot_info sinfo;
206	struct inode *inode;
207	int err;
208
209	mutex_lock(&MSDOS_SB(sb)->s_lock);
210	err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
211	switch (err) {
212	case -ENOENT:
213		inode = NULL;
214		break;
215	case 0:
216		inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
217		brelse(sinfo.bh);
218		break;
219	default:
220		inode = ERR_PTR(err);
221	}
222	mutex_unlock(&MSDOS_SB(sb)->s_lock);
223	return d_splice_alias(inode, dentry);
224}
225
226/***** Creates a directory entry (name is already formatted). */
227static int msdos_add_entry(struct inode *dir, const unsigned char *name,
228			   int is_dir, int is_hid, int cluster,
229			   struct timespec *ts, struct fat_slot_info *sinfo)
230{
231	struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
232	struct msdos_dir_entry de;
233	__le16 time, date;
234	int err;
235
236	memcpy(de.name, name, MSDOS_NAME);
237	de.attr = is_dir ? ATTR_DIR : ATTR_ARCH;
238	if (is_hid)
239		de.attr |= ATTR_HIDDEN;
240	de.lcase = 0;
241	fat_time_unix2fat(sbi, ts, &time, &date, NULL);
242	de.cdate = de.adate = 0;
243	de.ctime = 0;
244	de.ctime_cs = 0;
245	de.time = time;
246	de.date = date;
247	fat_set_start(&de, cluster);
248	de.size = 0;
249
250	err = fat_add_entries(dir, &de, 1, sinfo);
251	if (err)
252		return err;
253
254	dir->i_ctime = dir->i_mtime = *ts;
255	if (IS_DIRSYNC(dir))
256		(void)fat_sync_inode(dir);
257	else
258		mark_inode_dirty(dir);
259
260	return 0;
261}
262
263/***** Create a file */
264static int msdos_create(struct inode *dir, struct dentry *dentry, umode_t mode,
265			bool excl)
266{
267	struct super_block *sb = dir->i_sb;
268	struct inode *inode = NULL;
269	struct fat_slot_info sinfo;
270	struct timespec ts;
271	unsigned char msdos_name[MSDOS_NAME];
272	int err, is_hid;
273
274	mutex_lock(&MSDOS_SB(sb)->s_lock);
275
276	err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
277				msdos_name, &MSDOS_SB(sb)->options);
278	if (err)
279		goto out;
280	is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
281	/* Have to do it due to foo vs. .foo conflicts */
282	if (!fat_scan(dir, msdos_name, &sinfo)) {
283		brelse(sinfo.bh);
284		err = -EINVAL;
285		goto out;
286	}
287
288	ts = CURRENT_TIME_SEC;
289	err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo);
290	if (err)
291		goto out;
292	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
293	brelse(sinfo.bh);
294	if (IS_ERR(inode)) {
295		err = PTR_ERR(inode);
296		goto out;
297	}
298	inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
299	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
300
301	d_instantiate(dentry, inode);
302out:
303	mutex_unlock(&MSDOS_SB(sb)->s_lock);
304	if (!err)
305		err = fat_flush_inodes(sb, dir, inode);
306	return err;
307}
308
309/***** Remove a directory */
310static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
311{
312	struct super_block *sb = dir->i_sb;
313	struct inode *inode = dentry->d_inode;
314	struct fat_slot_info sinfo;
315	int err;
316
317	mutex_lock(&MSDOS_SB(sb)->s_lock);
318	/*
319	 * Check whether the directory is not in use, then check
320	 * whether it is empty.
321	 */
322	err = fat_dir_empty(inode);
323	if (err)
324		goto out;
325	err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
326	if (err)
327		goto out;
328
329	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
330	if (err)
331		goto out;
332	drop_nlink(dir);
333
334	clear_nlink(inode);
335	inode->i_ctime = CURRENT_TIME_SEC;
336	fat_detach(inode);
337out:
338	mutex_unlock(&MSDOS_SB(sb)->s_lock);
339	if (!err)
340		err = fat_flush_inodes(sb, dir, inode);
341
342	return err;
343}
344
345/***** Make a directory */
346static int msdos_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
347{
348	struct super_block *sb = dir->i_sb;
349	struct fat_slot_info sinfo;
350	struct inode *inode;
351	unsigned char msdos_name[MSDOS_NAME];
352	struct timespec ts;
353	int err, is_hid, cluster;
354
355	mutex_lock(&MSDOS_SB(sb)->s_lock);
356
357	err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
358				msdos_name, &MSDOS_SB(sb)->options);
359	if (err)
360		goto out;
361	is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
362	/* foo vs .foo situation */
363	if (!fat_scan(dir, msdos_name, &sinfo)) {
364		brelse(sinfo.bh);
365		err = -EINVAL;
366		goto out;
367	}
368
369	ts = CURRENT_TIME_SEC;
370	cluster = fat_alloc_new_dir(dir, &ts);
371	if (cluster < 0) {
372		err = cluster;
373		goto out;
374	}
375	err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo);
376	if (err)
377		goto out_free;
378	inc_nlink(dir);
379
380	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
381	brelse(sinfo.bh);
382	if (IS_ERR(inode)) {
383		err = PTR_ERR(inode);
384		/* the directory was completed, just return a error */
385		goto out;
386	}
387	set_nlink(inode, 2);
388	inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
389	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
390
391	d_instantiate(dentry, inode);
392
393	mutex_unlock(&MSDOS_SB(sb)->s_lock);
394	fat_flush_inodes(sb, dir, inode);
395	return 0;
396
397out_free:
398	fat_free_clusters(dir, cluster);
399out:
400	mutex_unlock(&MSDOS_SB(sb)->s_lock);
401	return err;
402}
403
404/***** Unlink a file */
405static int msdos_unlink(struct inode *dir, struct dentry *dentry)
406{
407	struct inode *inode = dentry->d_inode;
408	struct super_block *sb = inode->i_sb;
409	struct fat_slot_info sinfo;
410	int err;
411
412	mutex_lock(&MSDOS_SB(sb)->s_lock);
413	err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
414	if (err)
415		goto out;
416
417	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
418	if (err)
419		goto out;
420	clear_nlink(inode);
421	inode->i_ctime = CURRENT_TIME_SEC;
422	fat_detach(inode);
423out:
424	mutex_unlock(&MSDOS_SB(sb)->s_lock);
425	if (!err)
426		err = fat_flush_inodes(sb, dir, inode);
427
428	return err;
429}
430
431static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
432			   struct dentry *old_dentry,
433			   struct inode *new_dir, unsigned char *new_name,
434			   struct dentry *new_dentry, int is_hid)
435{
436	struct buffer_head *dotdot_bh;
437	struct msdos_dir_entry *dotdot_de;
438	struct inode *old_inode, *new_inode;
439	struct fat_slot_info old_sinfo, sinfo;
440	struct timespec ts;
441	loff_t new_i_pos;
442	int err, old_attrs, is_dir, update_dotdot, corrupt = 0;
443
444	old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
445	old_inode = old_dentry->d_inode;
446	new_inode = new_dentry->d_inode;
447
448	err = fat_scan(old_dir, old_name, &old_sinfo);
449	if (err) {
450		err = -EIO;
451		goto out;
452	}
453
454	is_dir = S_ISDIR(old_inode->i_mode);
455	update_dotdot = (is_dir && old_dir != new_dir);
456	if (update_dotdot) {
457		if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de)) {
458			err = -EIO;
459			goto out;
460		}
461	}
462
463	old_attrs = MSDOS_I(old_inode)->i_attrs;
464	err = fat_scan(new_dir, new_name, &sinfo);
465	if (!err) {
466		if (!new_inode) {
467			/* "foo" -> ".foo" case. just change the ATTR_HIDDEN */
468			if (sinfo.de != old_sinfo.de) {
469				err = -EINVAL;
470				goto out;
471			}
472			if (is_hid)
473				MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
474			else
475				MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
476			if (IS_DIRSYNC(old_dir)) {
477				err = fat_sync_inode(old_inode);
478				if (err) {
479					MSDOS_I(old_inode)->i_attrs = old_attrs;
480					goto out;
481				}
482			} else
483				mark_inode_dirty(old_inode);
484
485			old_dir->i_version++;
486			old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
487			if (IS_DIRSYNC(old_dir))
488				(void)fat_sync_inode(old_dir);
489			else
490				mark_inode_dirty(old_dir);
491			goto out;
492		}
493	}
494
495	ts = CURRENT_TIME_SEC;
496	if (new_inode) {
497		if (err)
498			goto out;
499		if (is_dir) {
500			err = fat_dir_empty(new_inode);
501			if (err)
502				goto out;
503		}
504		new_i_pos = MSDOS_I(new_inode)->i_pos;
505		fat_detach(new_inode);
506	} else {
507		err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0,
508				      &ts, &sinfo);
509		if (err)
510			goto out;
511		new_i_pos = sinfo.i_pos;
512	}
513	new_dir->i_version++;
514
515	fat_detach(old_inode);
516	fat_attach(old_inode, new_i_pos);
517	if (is_hid)
518		MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
519	else
520		MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
521	if (IS_DIRSYNC(new_dir)) {
522		err = fat_sync_inode(old_inode);
523		if (err)
524			goto error_inode;
525	} else
526		mark_inode_dirty(old_inode);
527
528	if (update_dotdot) {
529		fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart);
530		mark_buffer_dirty_inode(dotdot_bh, old_inode);
531		if (IS_DIRSYNC(new_dir)) {
532			err = sync_dirty_buffer(dotdot_bh);
533			if (err)
534				goto error_dotdot;
535		}
536		drop_nlink(old_dir);
537		if (!new_inode)
538			inc_nlink(new_dir);
539	}
540
541	err = fat_remove_entries(old_dir, &old_sinfo);	/* and releases bh */
542	old_sinfo.bh = NULL;
543	if (err)
544		goto error_dotdot;
545	old_dir->i_version++;
546	old_dir->i_ctime = old_dir->i_mtime = ts;
547	if (IS_DIRSYNC(old_dir))
548		(void)fat_sync_inode(old_dir);
549	else
550		mark_inode_dirty(old_dir);
551
552	if (new_inode) {
553		drop_nlink(new_inode);
554		if (is_dir)
555			drop_nlink(new_inode);
556		new_inode->i_ctime = ts;
557	}
558out:
559	brelse(sinfo.bh);
560	brelse(dotdot_bh);
561	brelse(old_sinfo.bh);
562	return err;
563
564error_dotdot:
565	/* data cluster is shared, serious corruption */
566	corrupt = 1;
567
568	if (update_dotdot) {
569		fat_set_start(dotdot_de, MSDOS_I(old_dir)->i_logstart);
570		mark_buffer_dirty_inode(dotdot_bh, old_inode);
571		corrupt |= sync_dirty_buffer(dotdot_bh);
572	}
573error_inode:
574	fat_detach(old_inode);
575	fat_attach(old_inode, old_sinfo.i_pos);
576	MSDOS_I(old_inode)->i_attrs = old_attrs;
577	if (new_inode) {
578		fat_attach(new_inode, new_i_pos);
579		if (corrupt)
580			corrupt |= fat_sync_inode(new_inode);
581	} else {
582		/*
583		 * If new entry was not sharing the data cluster, it
584		 * shouldn't be serious corruption.
585		 */
586		int err2 = fat_remove_entries(new_dir, &sinfo);
587		if (corrupt)
588			corrupt |= err2;
589		sinfo.bh = NULL;
590	}
591	if (corrupt < 0) {
592		fat_fs_error(new_dir->i_sb,
593			     "%s: Filesystem corrupted (i_pos %lld)",
594			     __func__, sinfo.i_pos);
595	}
596	goto out;
597}
598
599/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
600static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry,
601			struct inode *new_dir, struct dentry *new_dentry)
 
602{
603	struct super_block *sb = old_dir->i_sb;
604	unsigned char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
605	int err, is_hid;
606
 
 
 
607	mutex_lock(&MSDOS_SB(sb)->s_lock);
608
609	err = msdos_format_name(old_dentry->d_name.name,
610				old_dentry->d_name.len, old_msdos_name,
611				&MSDOS_SB(old_dir->i_sb)->options);
612	if (err)
613		goto out;
614	err = msdos_format_name(new_dentry->d_name.name,
615				new_dentry->d_name.len, new_msdos_name,
616				&MSDOS_SB(new_dir->i_sb)->options);
617	if (err)
618		goto out;
619
620	is_hid =
621	     (new_dentry->d_name.name[0] == '.') && (new_msdos_name[0] != '.');
622
623	err = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
624			      new_dir, new_msdos_name, new_dentry, is_hid);
625out:
626	mutex_unlock(&MSDOS_SB(sb)->s_lock);
627	if (!err)
628		err = fat_flush_inodes(sb, old_dir, new_dir);
629	return err;
630}
631
632static const struct inode_operations msdos_dir_inode_operations = {
633	.create		= msdos_create,
634	.lookup		= msdos_lookup,
635	.unlink		= msdos_unlink,
636	.mkdir		= msdos_mkdir,
637	.rmdir		= msdos_rmdir,
638	.rename		= msdos_rename,
639	.setattr	= fat_setattr,
640	.getattr	= fat_getattr,
641};
642
643static void setup(struct super_block *sb)
644{
645	MSDOS_SB(sb)->dir_ops = &msdos_dir_inode_operations;
646	sb->s_d_op = &msdos_dentry_operations;
647	sb->s_flags |= MS_NOATIME;
648}
649
650static int msdos_fill_super(struct super_block *sb, void *data, int silent)
651{
652	return fat_fill_super(sb, data, silent, 0, setup);
653}
654
655static struct dentry *msdos_mount(struct file_system_type *fs_type,
656			int flags, const char *dev_name,
657			void *data)
658{
659	return mount_bdev(fs_type, flags, dev_name, data, msdos_fill_super);
660}
661
662static struct file_system_type msdos_fs_type = {
663	.owner		= THIS_MODULE,
664	.name		= "msdos",
665	.mount		= msdos_mount,
666	.kill_sb	= kill_block_super,
667	.fs_flags	= FS_REQUIRES_DEV,
668};
669MODULE_ALIAS_FS("msdos");
670
671static int __init init_msdos_fs(void)
672{
673	return register_filesystem(&msdos_fs_type);
674}
675
676static void __exit exit_msdos_fs(void)
677{
678	unregister_filesystem(&msdos_fs_type);
679}
680
681MODULE_LICENSE("GPL");
682MODULE_AUTHOR("Werner Almesberger");
683MODULE_DESCRIPTION("MS-DOS filesystem support");
684
685module_init(init_msdos_fs)
686module_exit(exit_msdos_fs)
v4.17
  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#include <linux/module.h>
 10#include <linux/iversion.h>
 
 11#include "fat.h"
 12
 13/* Characters that are undesirable in an MS-DOS file name */
 14static unsigned char bad_chars[] = "*?<>|\"";
 15static unsigned char bad_if_strict[] = "+=,; ";
 16
 17/***** Formats an MS-DOS file name. Rejects invalid names. */
 18static int msdos_format_name(const unsigned char *name, int len,
 19			     unsigned char *res, struct fat_mount_options *opts)
 20	/*
 21	 * name is the proposed name, len is its length, res is
 22	 * the resulting name, opts->name_check is either (r)elaxed,
 23	 * (n)ormal or (s)trict, opts->dotsOK allows dots at the
 24	 * beginning of name (for hidden files)
 25	 */
 26{
 27	unsigned char *walk;
 28	unsigned char c;
 29	int space;
 30
 31	if (name[0] == '.') {	/* dotfile because . and .. already done */
 32		if (opts->dotsOK) {
 33			/* Get rid of dot - test for it elsewhere */
 34			name++;
 35			len--;
 36		} else
 37			return -EINVAL;
 38	}
 39	/*
 40	 * disallow names that _really_ start with a dot
 41	 */
 42	space = 1;
 43	c = 0;
 44	for (walk = res; len && walk - res < 8; walk++) {
 45		c = *name++;
 46		len--;
 47		if (opts->name_check != 'r' && strchr(bad_chars, c))
 48			return -EINVAL;
 49		if (opts->name_check == 's' && strchr(bad_if_strict, c))
 50			return -EINVAL;
 51		if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
 52			return -EINVAL;
 53		if (c < ' ' || c == ':' || c == '\\')
 54			return -EINVAL;
 55	/*
 56	 * 0xE5 is legal as a first character, but we must substitute
 57	 * 0x05 because 0xE5 marks deleted files.  Yes, DOS really
 58	 * does this.
 59	 * It seems that Microsoft hacked DOS to support non-US
 60	 * characters after the 0xE5 character was already in use to
 61	 * mark deleted files.
 62	 */
 63		if ((res == walk) && (c == 0xE5))
 64			c = 0x05;
 65		if (c == '.')
 66			break;
 67		space = (c == ' ');
 68		*walk = (!opts->nocase && c >= 'a' && c <= 'z') ? c - 32 : c;
 69	}
 70	if (space)
 71		return -EINVAL;
 72	if (opts->name_check == 's' && len && c != '.') {
 73		c = *name++;
 74		len--;
 75		if (c != '.')
 76			return -EINVAL;
 77	}
 78	while (c != '.' && len--)
 79		c = *name++;
 80	if (c == '.') {
 81		while (walk - res < 8)
 82			*walk++ = ' ';
 83		while (len > 0 && walk - res < MSDOS_NAME) {
 84			c = *name++;
 85			len--;
 86			if (opts->name_check != 'r' && strchr(bad_chars, c))
 87				return -EINVAL;
 88			if (opts->name_check == 's' &&
 89			    strchr(bad_if_strict, c))
 90				return -EINVAL;
 91			if (c < ' ' || c == ':' || c == '\\')
 92				return -EINVAL;
 93			if (c == '.') {
 94				if (opts->name_check == 's')
 95					return -EINVAL;
 96				break;
 97			}
 98			if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
 99				return -EINVAL;
100			space = c == ' ';
101			if (!opts->nocase && c >= 'a' && c <= 'z')
102				*walk++ = c - 32;
103			else
104				*walk++ = c;
105		}
106		if (space)
107			return -EINVAL;
108		if (opts->name_check == 's' && len)
109			return -EINVAL;
110	}
111	while (walk - res < MSDOS_NAME)
112		*walk++ = ' ';
113
114	return 0;
115}
116
117/***** Locates a directory entry.  Uses unformatted name. */
118static int msdos_find(struct inode *dir, const unsigned char *name, int len,
119		      struct fat_slot_info *sinfo)
120{
121	struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
122	unsigned char msdos_name[MSDOS_NAME];
123	int err;
124
125	err = msdos_format_name(name, len, msdos_name, &sbi->options);
126	if (err)
127		return -ENOENT;
128
129	err = fat_scan(dir, msdos_name, sinfo);
130	if (!err && sbi->options.dotsOK) {
131		if (name[0] == '.') {
132			if (!(sinfo->de->attr & ATTR_HIDDEN))
133				err = -ENOENT;
134		} else {
135			if (sinfo->de->attr & ATTR_HIDDEN)
136				err = -ENOENT;
137		}
138		if (err)
139			brelse(sinfo->bh);
140	}
141	return err;
142}
143
144/*
145 * Compute the hash for the msdos name corresponding to the dentry.
146 * Note: if the name is invalid, we leave the hash code unchanged so
147 * that the existing dentry can be used. The msdos fs routines will
148 * return ENOENT or EINVAL as appropriate.
149 */
150static int msdos_hash(const struct dentry *dentry, struct qstr *qstr)
151{
152	struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
153	unsigned char msdos_name[MSDOS_NAME];
154	int error;
155
156	error = msdos_format_name(qstr->name, qstr->len, msdos_name, options);
157	if (!error)
158		qstr->hash = full_name_hash(dentry, msdos_name, MSDOS_NAME);
159	return 0;
160}
161
162/*
163 * Compare two msdos names. If either of the names are invalid,
164 * we fall back to doing the standard name comparison.
165 */
166static int msdos_cmp(const struct dentry *dentry,
167		unsigned int len, const char *str, const struct qstr *name)
168{
169	struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
170	unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
171	int error;
172
173	error = msdos_format_name(name->name, name->len, a_msdos_name, options);
174	if (error)
175		goto old_compare;
176	error = msdos_format_name(str, len, b_msdos_name, options);
177	if (error)
178		goto old_compare;
179	error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
180out:
181	return error;
182
183old_compare:
184	error = 1;
185	if (name->len == len)
186		error = memcmp(name->name, str, len);
187	goto out;
188}
189
190static const struct dentry_operations msdos_dentry_operations = {
191	.d_hash		= msdos_hash,
192	.d_compare	= msdos_cmp,
193};
194
195/*
196 * AV. Wrappers for FAT sb operations. Is it wise?
197 */
198
199/***** Get inode using directory and name */
200static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
201				   unsigned int flags)
202{
203	struct super_block *sb = dir->i_sb;
204	struct fat_slot_info sinfo;
205	struct inode *inode;
206	int err;
207
208	mutex_lock(&MSDOS_SB(sb)->s_lock);
209	err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
210	switch (err) {
211	case -ENOENT:
212		inode = NULL;
213		break;
214	case 0:
215		inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
216		brelse(sinfo.bh);
217		break;
218	default:
219		inode = ERR_PTR(err);
220	}
221	mutex_unlock(&MSDOS_SB(sb)->s_lock);
222	return d_splice_alias(inode, dentry);
223}
224
225/***** Creates a directory entry (name is already formatted). */
226static int msdos_add_entry(struct inode *dir, const unsigned char *name,
227			   int is_dir, int is_hid, int cluster,
228			   struct timespec *ts, struct fat_slot_info *sinfo)
229{
230	struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
231	struct msdos_dir_entry de;
232	__le16 time, date;
233	int err;
234
235	memcpy(de.name, name, MSDOS_NAME);
236	de.attr = is_dir ? ATTR_DIR : ATTR_ARCH;
237	if (is_hid)
238		de.attr |= ATTR_HIDDEN;
239	de.lcase = 0;
240	fat_time_unix2fat(sbi, ts, &time, &date, NULL);
241	de.cdate = de.adate = 0;
242	de.ctime = 0;
243	de.ctime_cs = 0;
244	de.time = time;
245	de.date = date;
246	fat_set_start(&de, cluster);
247	de.size = 0;
248
249	err = fat_add_entries(dir, &de, 1, sinfo);
250	if (err)
251		return err;
252
253	dir->i_ctime = dir->i_mtime = *ts;
254	if (IS_DIRSYNC(dir))
255		(void)fat_sync_inode(dir);
256	else
257		mark_inode_dirty(dir);
258
259	return 0;
260}
261
262/***** Create a file */
263static int msdos_create(struct inode *dir, struct dentry *dentry, umode_t mode,
264			bool excl)
265{
266	struct super_block *sb = dir->i_sb;
267	struct inode *inode = NULL;
268	struct fat_slot_info sinfo;
269	struct timespec ts;
270	unsigned char msdos_name[MSDOS_NAME];
271	int err, is_hid;
272
273	mutex_lock(&MSDOS_SB(sb)->s_lock);
274
275	err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
276				msdos_name, &MSDOS_SB(sb)->options);
277	if (err)
278		goto out;
279	is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
280	/* Have to do it due to foo vs. .foo conflicts */
281	if (!fat_scan(dir, msdos_name, &sinfo)) {
282		brelse(sinfo.bh);
283		err = -EINVAL;
284		goto out;
285	}
286
287	ts = current_time(dir);
288	err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo);
289	if (err)
290		goto out;
291	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
292	brelse(sinfo.bh);
293	if (IS_ERR(inode)) {
294		err = PTR_ERR(inode);
295		goto out;
296	}
297	inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
298	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
299
300	d_instantiate(dentry, inode);
301out:
302	mutex_unlock(&MSDOS_SB(sb)->s_lock);
303	if (!err)
304		err = fat_flush_inodes(sb, dir, inode);
305	return err;
306}
307
308/***** Remove a directory */
309static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
310{
311	struct super_block *sb = dir->i_sb;
312	struct inode *inode = d_inode(dentry);
313	struct fat_slot_info sinfo;
314	int err;
315
316	mutex_lock(&MSDOS_SB(sb)->s_lock);
317	/*
318	 * Check whether the directory is not in use, then check
319	 * whether it is empty.
320	 */
321	err = fat_dir_empty(inode);
322	if (err)
323		goto out;
324	err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
325	if (err)
326		goto out;
327
328	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
329	if (err)
330		goto out;
331	drop_nlink(dir);
332
333	clear_nlink(inode);
334	inode->i_ctime = current_time(inode);
335	fat_detach(inode);
336out:
337	mutex_unlock(&MSDOS_SB(sb)->s_lock);
338	if (!err)
339		err = fat_flush_inodes(sb, dir, inode);
340
341	return err;
342}
343
344/***** Make a directory */
345static int msdos_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
346{
347	struct super_block *sb = dir->i_sb;
348	struct fat_slot_info sinfo;
349	struct inode *inode;
350	unsigned char msdos_name[MSDOS_NAME];
351	struct timespec ts;
352	int err, is_hid, cluster;
353
354	mutex_lock(&MSDOS_SB(sb)->s_lock);
355
356	err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
357				msdos_name, &MSDOS_SB(sb)->options);
358	if (err)
359		goto out;
360	is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
361	/* foo vs .foo situation */
362	if (!fat_scan(dir, msdos_name, &sinfo)) {
363		brelse(sinfo.bh);
364		err = -EINVAL;
365		goto out;
366	}
367
368	ts = current_time(dir);
369	cluster = fat_alloc_new_dir(dir, &ts);
370	if (cluster < 0) {
371		err = cluster;
372		goto out;
373	}
374	err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo);
375	if (err)
376		goto out_free;
377	inc_nlink(dir);
378
379	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
380	brelse(sinfo.bh);
381	if (IS_ERR(inode)) {
382		err = PTR_ERR(inode);
383		/* the directory was completed, just return a error */
384		goto out;
385	}
386	set_nlink(inode, 2);
387	inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
388	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
389
390	d_instantiate(dentry, inode);
391
392	mutex_unlock(&MSDOS_SB(sb)->s_lock);
393	fat_flush_inodes(sb, dir, inode);
394	return 0;
395
396out_free:
397	fat_free_clusters(dir, cluster);
398out:
399	mutex_unlock(&MSDOS_SB(sb)->s_lock);
400	return err;
401}
402
403/***** Unlink a file */
404static int msdos_unlink(struct inode *dir, struct dentry *dentry)
405{
406	struct inode *inode = d_inode(dentry);
407	struct super_block *sb = inode->i_sb;
408	struct fat_slot_info sinfo;
409	int err;
410
411	mutex_lock(&MSDOS_SB(sb)->s_lock);
412	err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
413	if (err)
414		goto out;
415
416	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
417	if (err)
418		goto out;
419	clear_nlink(inode);
420	inode->i_ctime = current_time(inode);
421	fat_detach(inode);
422out:
423	mutex_unlock(&MSDOS_SB(sb)->s_lock);
424	if (!err)
425		err = fat_flush_inodes(sb, dir, inode);
426
427	return err;
428}
429
430static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
431			   struct dentry *old_dentry,
432			   struct inode *new_dir, unsigned char *new_name,
433			   struct dentry *new_dentry, int is_hid)
434{
435	struct buffer_head *dotdot_bh;
436	struct msdos_dir_entry *dotdot_de;
437	struct inode *old_inode, *new_inode;
438	struct fat_slot_info old_sinfo, sinfo;
439	struct timespec ts;
440	loff_t new_i_pos;
441	int err, old_attrs, is_dir, update_dotdot, corrupt = 0;
442
443	old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
444	old_inode = d_inode(old_dentry);
445	new_inode = d_inode(new_dentry);
446
447	err = fat_scan(old_dir, old_name, &old_sinfo);
448	if (err) {
449		err = -EIO;
450		goto out;
451	}
452
453	is_dir = S_ISDIR(old_inode->i_mode);
454	update_dotdot = (is_dir && old_dir != new_dir);
455	if (update_dotdot) {
456		if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de)) {
457			err = -EIO;
458			goto out;
459		}
460	}
461
462	old_attrs = MSDOS_I(old_inode)->i_attrs;
463	err = fat_scan(new_dir, new_name, &sinfo);
464	if (!err) {
465		if (!new_inode) {
466			/* "foo" -> ".foo" case. just change the ATTR_HIDDEN */
467			if (sinfo.de != old_sinfo.de) {
468				err = -EINVAL;
469				goto out;
470			}
471			if (is_hid)
472				MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
473			else
474				MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
475			if (IS_DIRSYNC(old_dir)) {
476				err = fat_sync_inode(old_inode);
477				if (err) {
478					MSDOS_I(old_inode)->i_attrs = old_attrs;
479					goto out;
480				}
481			} else
482				mark_inode_dirty(old_inode);
483
484			inode_inc_iversion(old_dir);
485			old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir);
486			if (IS_DIRSYNC(old_dir))
487				(void)fat_sync_inode(old_dir);
488			else
489				mark_inode_dirty(old_dir);
490			goto out;
491		}
492	}
493
494	ts = current_time(old_inode);
495	if (new_inode) {
496		if (err)
497			goto out;
498		if (is_dir) {
499			err = fat_dir_empty(new_inode);
500			if (err)
501				goto out;
502		}
503		new_i_pos = MSDOS_I(new_inode)->i_pos;
504		fat_detach(new_inode);
505	} else {
506		err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0,
507				      &ts, &sinfo);
508		if (err)
509			goto out;
510		new_i_pos = sinfo.i_pos;
511	}
512	inode_inc_iversion(new_dir);
513
514	fat_detach(old_inode);
515	fat_attach(old_inode, new_i_pos);
516	if (is_hid)
517		MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
518	else
519		MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
520	if (IS_DIRSYNC(new_dir)) {
521		err = fat_sync_inode(old_inode);
522		if (err)
523			goto error_inode;
524	} else
525		mark_inode_dirty(old_inode);
526
527	if (update_dotdot) {
528		fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart);
529		mark_buffer_dirty_inode(dotdot_bh, old_inode);
530		if (IS_DIRSYNC(new_dir)) {
531			err = sync_dirty_buffer(dotdot_bh);
532			if (err)
533				goto error_dotdot;
534		}
535		drop_nlink(old_dir);
536		if (!new_inode)
537			inc_nlink(new_dir);
538	}
539
540	err = fat_remove_entries(old_dir, &old_sinfo);	/* and releases bh */
541	old_sinfo.bh = NULL;
542	if (err)
543		goto error_dotdot;
544	inode_inc_iversion(old_dir);
545	old_dir->i_ctime = old_dir->i_mtime = ts;
546	if (IS_DIRSYNC(old_dir))
547		(void)fat_sync_inode(old_dir);
548	else
549		mark_inode_dirty(old_dir);
550
551	if (new_inode) {
552		drop_nlink(new_inode);
553		if (is_dir)
554			drop_nlink(new_inode);
555		new_inode->i_ctime = ts;
556	}
557out:
558	brelse(sinfo.bh);
559	brelse(dotdot_bh);
560	brelse(old_sinfo.bh);
561	return err;
562
563error_dotdot:
564	/* data cluster is shared, serious corruption */
565	corrupt = 1;
566
567	if (update_dotdot) {
568		fat_set_start(dotdot_de, MSDOS_I(old_dir)->i_logstart);
569		mark_buffer_dirty_inode(dotdot_bh, old_inode);
570		corrupt |= sync_dirty_buffer(dotdot_bh);
571	}
572error_inode:
573	fat_detach(old_inode);
574	fat_attach(old_inode, old_sinfo.i_pos);
575	MSDOS_I(old_inode)->i_attrs = old_attrs;
576	if (new_inode) {
577		fat_attach(new_inode, new_i_pos);
578		if (corrupt)
579			corrupt |= fat_sync_inode(new_inode);
580	} else {
581		/*
582		 * If new entry was not sharing the data cluster, it
583		 * shouldn't be serious corruption.
584		 */
585		int err2 = fat_remove_entries(new_dir, &sinfo);
586		if (corrupt)
587			corrupt |= err2;
588		sinfo.bh = NULL;
589	}
590	if (corrupt < 0) {
591		fat_fs_error(new_dir->i_sb,
592			     "%s: Filesystem corrupted (i_pos %lld)",
593			     __func__, sinfo.i_pos);
594	}
595	goto out;
596}
597
598/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
599static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry,
600			struct inode *new_dir, struct dentry *new_dentry,
601			unsigned int flags)
602{
603	struct super_block *sb = old_dir->i_sb;
604	unsigned char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
605	int err, is_hid;
606
607	if (flags & ~RENAME_NOREPLACE)
608		return -EINVAL;
609
610	mutex_lock(&MSDOS_SB(sb)->s_lock);
611
612	err = msdos_format_name(old_dentry->d_name.name,
613				old_dentry->d_name.len, old_msdos_name,
614				&MSDOS_SB(old_dir->i_sb)->options);
615	if (err)
616		goto out;
617	err = msdos_format_name(new_dentry->d_name.name,
618				new_dentry->d_name.len, new_msdos_name,
619				&MSDOS_SB(new_dir->i_sb)->options);
620	if (err)
621		goto out;
622
623	is_hid =
624	     (new_dentry->d_name.name[0] == '.') && (new_msdos_name[0] != '.');
625
626	err = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
627			      new_dir, new_msdos_name, new_dentry, is_hid);
628out:
629	mutex_unlock(&MSDOS_SB(sb)->s_lock);
630	if (!err)
631		err = fat_flush_inodes(sb, old_dir, new_dir);
632	return err;
633}
634
635static const struct inode_operations msdos_dir_inode_operations = {
636	.create		= msdos_create,
637	.lookup		= msdos_lookup,
638	.unlink		= msdos_unlink,
639	.mkdir		= msdos_mkdir,
640	.rmdir		= msdos_rmdir,
641	.rename		= msdos_rename,
642	.setattr	= fat_setattr,
643	.getattr	= fat_getattr,
644};
645
646static void setup(struct super_block *sb)
647{
648	MSDOS_SB(sb)->dir_ops = &msdos_dir_inode_operations;
649	sb->s_d_op = &msdos_dentry_operations;
650	sb->s_flags |= SB_NOATIME;
651}
652
653static int msdos_fill_super(struct super_block *sb, void *data, int silent)
654{
655	return fat_fill_super(sb, data, silent, 0, setup);
656}
657
658static struct dentry *msdos_mount(struct file_system_type *fs_type,
659			int flags, const char *dev_name,
660			void *data)
661{
662	return mount_bdev(fs_type, flags, dev_name, data, msdos_fill_super);
663}
664
665static struct file_system_type msdos_fs_type = {
666	.owner		= THIS_MODULE,
667	.name		= "msdos",
668	.mount		= msdos_mount,
669	.kill_sb	= kill_block_super,
670	.fs_flags	= FS_REQUIRES_DEV,
671};
672MODULE_ALIAS_FS("msdos");
673
674static int __init init_msdos_fs(void)
675{
676	return register_filesystem(&msdos_fs_type);
677}
678
679static void __exit exit_msdos_fs(void)
680{
681	unregister_filesystem(&msdos_fs_type);
682}
683
684MODULE_LICENSE("GPL");
685MODULE_AUTHOR("Werner Almesberger");
686MODULE_DESCRIPTION("MS-DOS filesystem support");
687
688module_init(init_msdos_fs)
689module_exit(exit_msdos_fs)