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)
v3.1
  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, const struct inode *inode,
152	       struct qstr *qstr)
153{
154	struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
155	unsigned char msdos_name[MSDOS_NAME];
156	int error;
157
158	error = msdos_format_name(qstr->name, qstr->len, msdos_name, options);
159	if (!error)
160		qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
161	return 0;
162}
163
164/*
165 * Compare two msdos names. If either of the names are invalid,
166 * we fall back to doing the standard name comparison.
167 */
168static int msdos_cmp(const struct dentry *parent, const struct inode *pinode,
169		const struct dentry *dentry, const struct inode *inode,
170		unsigned int len, const char *str, const struct qstr *name)
171{
172	struct fat_mount_options *options = &MSDOS_SB(parent->d_sb)->options;
173	unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
174	int error;
175
176	error = msdos_format_name(name->name, name->len, a_msdos_name, options);
177	if (error)
178		goto old_compare;
179	error = msdos_format_name(str, len, b_msdos_name, options);
180	if (error)
181		goto old_compare;
182	error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
183out:
184	return error;
185
186old_compare:
187	error = 1;
188	if (name->len == len)
189		error = memcmp(name->name, str, len);
190	goto out;
191}
192
193static const struct dentry_operations msdos_dentry_operations = {
194	.d_hash		= msdos_hash,
195	.d_compare	= msdos_cmp,
196};
197
198/*
199 * AV. Wrappers for FAT sb operations. Is it wise?
200 */
201
202/***** Get inode using directory and name */
203static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
204				   struct nameidata *nd)
205{
206	struct super_block *sb = dir->i_sb;
207	struct fat_slot_info sinfo;
208	struct inode *inode;
209	int err;
210
211	lock_super(sb);
212	err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
213	switch (err) {
214	case -ENOENT:
215		inode = NULL;
216		break;
217	case 0:
218		inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
219		brelse(sinfo.bh);
220		break;
221	default:
222		inode = ERR_PTR(err);
223	}
224	unlock_super(sb);
225	return d_splice_alias(inode, dentry);
226}
227
228/***** Creates a directory entry (name is already formatted). */
229static int msdos_add_entry(struct inode *dir, const unsigned char *name,
230			   int is_dir, int is_hid, int cluster,
231			   struct timespec *ts, struct fat_slot_info *sinfo)
232{
233	struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
234	struct msdos_dir_entry de;
235	__le16 time, date;
236	int err;
237
238	memcpy(de.name, name, MSDOS_NAME);
239	de.attr = is_dir ? ATTR_DIR : ATTR_ARCH;
240	if (is_hid)
241		de.attr |= ATTR_HIDDEN;
242	de.lcase = 0;
243	fat_time_unix2fat(sbi, ts, &time, &date, NULL);
244	de.cdate = de.adate = 0;
245	de.ctime = 0;
246	de.ctime_cs = 0;
247	de.time = time;
248	de.date = date;
249	de.start = cpu_to_le16(cluster);
250	de.starthi = cpu_to_le16(cluster >> 16);
251	de.size = 0;
252
253	err = fat_add_entries(dir, &de, 1, sinfo);
254	if (err)
255		return err;
256
257	dir->i_ctime = dir->i_mtime = *ts;
258	if (IS_DIRSYNC(dir))
259		(void)fat_sync_inode(dir);
260	else
261		mark_inode_dirty(dir);
262
263	return 0;
264}
265
266/***** Create a file */
267static int msdos_create(struct inode *dir, struct dentry *dentry, int mode,
268			struct nameidata *nd)
269{
270	struct super_block *sb = dir->i_sb;
271	struct inode *inode = NULL;
272	struct fat_slot_info sinfo;
273	struct timespec ts;
274	unsigned char msdos_name[MSDOS_NAME];
275	int err, is_hid;
276
277	lock_super(sb);
278
279	err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
280				msdos_name, &MSDOS_SB(sb)->options);
281	if (err)
282		goto out;
283	is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
284	/* Have to do it due to foo vs. .foo conflicts */
285	if (!fat_scan(dir, msdos_name, &sinfo)) {
286		brelse(sinfo.bh);
287		err = -EINVAL;
288		goto out;
289	}
290
291	ts = CURRENT_TIME_SEC;
292	err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo);
293	if (err)
294		goto out;
295	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
296	brelse(sinfo.bh);
297	if (IS_ERR(inode)) {
298		err = PTR_ERR(inode);
299		goto out;
300	}
301	inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
302	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
303
304	d_instantiate(dentry, inode);
305out:
306	unlock_super(sb);
307	if (!err)
308		err = fat_flush_inodes(sb, dir, inode);
309	return err;
310}
311
312/***** Remove a directory */
313static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
314{
315	struct super_block *sb = dir->i_sb;
316	struct inode *inode = dentry->d_inode;
317	struct fat_slot_info sinfo;
318	int err;
319
320	lock_super(sb);
321	/*
322	 * Check whether the directory is not in use, then check
323	 * whether it is empty.
324	 */
325	err = fat_dir_empty(inode);
326	if (err)
327		goto out;
328	err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
329	if (err)
330		goto out;
331
332	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
333	if (err)
334		goto out;
335	drop_nlink(dir);
336
337	clear_nlink(inode);
338	inode->i_ctime = CURRENT_TIME_SEC;
339	fat_detach(inode);
340out:
341	unlock_super(sb);
342	if (!err)
343		err = fat_flush_inodes(sb, dir, inode);
344
345	return err;
346}
347
348/***** Make a directory */
349static int msdos_mkdir(struct inode *dir, struct dentry *dentry, int mode)
350{
351	struct super_block *sb = dir->i_sb;
352	struct fat_slot_info sinfo;
353	struct inode *inode;
354	unsigned char msdos_name[MSDOS_NAME];
355	struct timespec ts;
356	int err, is_hid, cluster;
357
358	lock_super(sb);
359
360	err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
361				msdos_name, &MSDOS_SB(sb)->options);
362	if (err)
363		goto out;
364	is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
365	/* foo vs .foo situation */
366	if (!fat_scan(dir, msdos_name, &sinfo)) {
367		brelse(sinfo.bh);
368		err = -EINVAL;
369		goto out;
370	}
371
372	ts = CURRENT_TIME_SEC;
373	cluster = fat_alloc_new_dir(dir, &ts);
374	if (cluster < 0) {
375		err = cluster;
376		goto out;
377	}
378	err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo);
379	if (err)
380		goto out_free;
381	inc_nlink(dir);
382
383	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
384	brelse(sinfo.bh);
385	if (IS_ERR(inode)) {
386		err = PTR_ERR(inode);
387		/* the directory was completed, just return a error */
388		goto out;
389	}
390	inode->i_nlink = 2;
391	inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
392	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
393
394	d_instantiate(dentry, inode);
395
396	unlock_super(sb);
397	fat_flush_inodes(sb, dir, inode);
398	return 0;
399
400out_free:
401	fat_free_clusters(dir, cluster);
402out:
403	unlock_super(sb);
404	return err;
405}
406
407/***** Unlink a file */
408static int msdos_unlink(struct inode *dir, struct dentry *dentry)
409{
410	struct inode *inode = dentry->d_inode;
411	struct super_block *sb= inode->i_sb;
412	struct fat_slot_info sinfo;
413	int err;
414
415	lock_super(sb);
416	err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
417	if (err)
418		goto out;
419
420	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
421	if (err)
422		goto out;
423	clear_nlink(inode);
424	inode->i_ctime = CURRENT_TIME_SEC;
425	fat_detach(inode);
426out:
427	unlock_super(sb);
428	if (!err)
429		err = fat_flush_inodes(sb, dir, inode);
430
431	return err;
432}
433
434static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
435			   struct dentry *old_dentry,
436			   struct inode *new_dir, unsigned char *new_name,
437			   struct dentry *new_dentry, int is_hid)
438{
439	struct buffer_head *dotdot_bh;
440	struct msdos_dir_entry *dotdot_de;
441	struct inode *old_inode, *new_inode;
442	struct fat_slot_info old_sinfo, sinfo;
443	struct timespec ts;
444	loff_t dotdot_i_pos, new_i_pos;
445	int err, old_attrs, is_dir, update_dotdot, corrupt = 0;
446
447	old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
448	old_inode = old_dentry->d_inode;
449	new_inode = new_dentry->d_inode;
450
451	err = fat_scan(old_dir, old_name, &old_sinfo);
452	if (err) {
453		err = -EIO;
454		goto out;
455	}
456
457	is_dir = S_ISDIR(old_inode->i_mode);
458	update_dotdot = (is_dir && old_dir != new_dir);
459	if (update_dotdot) {
460		if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de,
461					 &dotdot_i_pos) < 0) {
462			err = -EIO;
463			goto out;
464		}
465	}
466
467	old_attrs = MSDOS_I(old_inode)->i_attrs;
468	err = fat_scan(new_dir, new_name, &sinfo);
469	if (!err) {
470		if (!new_inode) {
471			/* "foo" -> ".foo" case. just change the ATTR_HIDDEN */
472			if (sinfo.de != old_sinfo.de) {
473				err = -EINVAL;
474				goto out;
475			}
476			if (is_hid)
477				MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
478			else
479				MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
480			if (IS_DIRSYNC(old_dir)) {
481				err = fat_sync_inode(old_inode);
482				if (err) {
483					MSDOS_I(old_inode)->i_attrs = old_attrs;
484					goto out;
485				}
486			} else
487				mark_inode_dirty(old_inode);
488
489			old_dir->i_version++;
490			old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
491			if (IS_DIRSYNC(old_dir))
492				(void)fat_sync_inode(old_dir);
493			else
494				mark_inode_dirty(old_dir);
495			goto out;
496		}
497	}
498
499	ts = CURRENT_TIME_SEC;
500	if (new_inode) {
501		if (err)
502			goto out;
503		if (is_dir) {
504			err = fat_dir_empty(new_inode);
505			if (err)
506				goto out;
507		}
508		new_i_pos = MSDOS_I(new_inode)->i_pos;
509		fat_detach(new_inode);
510	} else {
511		err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0,
512				      &ts, &sinfo);
513		if (err)
514			goto out;
515		new_i_pos = sinfo.i_pos;
516	}
517	new_dir->i_version++;
518
519	fat_detach(old_inode);
520	fat_attach(old_inode, new_i_pos);
521	if (is_hid)
522		MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
523	else
524		MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
525	if (IS_DIRSYNC(new_dir)) {
526		err = fat_sync_inode(old_inode);
527		if (err)
528			goto error_inode;
529	} else
530		mark_inode_dirty(old_inode);
531
532	if (update_dotdot) {
533		int start = MSDOS_I(new_dir)->i_logstart;
534		dotdot_de->start = cpu_to_le16(start);
535		dotdot_de->starthi = cpu_to_le16(start >> 16);
536		mark_buffer_dirty_inode(dotdot_bh, old_inode);
537		if (IS_DIRSYNC(new_dir)) {
538			err = sync_dirty_buffer(dotdot_bh);
539			if (err)
540				goto error_dotdot;
541		}
542		drop_nlink(old_dir);
543		if (!new_inode)
544			inc_nlink(new_dir);
545	}
546
547	err = fat_remove_entries(old_dir, &old_sinfo);	/* and releases bh */
548	old_sinfo.bh = NULL;
549	if (err)
550		goto error_dotdot;
551	old_dir->i_version++;
552	old_dir->i_ctime = old_dir->i_mtime = ts;
553	if (IS_DIRSYNC(old_dir))
554		(void)fat_sync_inode(old_dir);
555	else
556		mark_inode_dirty(old_dir);
557
558	if (new_inode) {
559		drop_nlink(new_inode);
560		if (is_dir)
561			drop_nlink(new_inode);
562		new_inode->i_ctime = ts;
563	}
564out:
565	brelse(sinfo.bh);
566	brelse(dotdot_bh);
567	brelse(old_sinfo.bh);
568	return err;
569
570error_dotdot:
571	/* data cluster is shared, serious corruption */
572	corrupt = 1;
573
574	if (update_dotdot) {
575		int start = MSDOS_I(old_dir)->i_logstart;
576		dotdot_de->start = cpu_to_le16(start);
577		dotdot_de->starthi = cpu_to_le16(start >> 16);
578		mark_buffer_dirty_inode(dotdot_bh, old_inode);
579		corrupt |= sync_dirty_buffer(dotdot_bh);
580	}
581error_inode:
582	fat_detach(old_inode);
583	fat_attach(old_inode, old_sinfo.i_pos);
584	MSDOS_I(old_inode)->i_attrs = old_attrs;
585	if (new_inode) {
586		fat_attach(new_inode, new_i_pos);
587		if (corrupt)
588			corrupt |= fat_sync_inode(new_inode);
589	} else {
590		/*
591		 * If new entry was not sharing the data cluster, it
592		 * shouldn't be serious corruption.
593		 */
594		int err2 = fat_remove_entries(new_dir, &sinfo);
595		if (corrupt)
596			corrupt |= err2;
597		sinfo.bh = NULL;
598	}
599	if (corrupt < 0) {
600		fat_fs_error(new_dir->i_sb,
601			     "%s: Filesystem corrupted (i_pos %lld)",
602			     __func__, sinfo.i_pos);
603	}
604	goto out;
605}
606
607/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
608static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry,
609			struct inode *new_dir, struct dentry *new_dentry)
610{
611	struct super_block *sb = old_dir->i_sb;
612	unsigned char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
613	int err, is_hid;
614
615	lock_super(sb);
616
617	err = msdos_format_name(old_dentry->d_name.name,
618				old_dentry->d_name.len, old_msdos_name,
619				&MSDOS_SB(old_dir->i_sb)->options);
620	if (err)
621		goto out;
622	err = msdos_format_name(new_dentry->d_name.name,
623				new_dentry->d_name.len, new_msdos_name,
624				&MSDOS_SB(new_dir->i_sb)->options);
625	if (err)
626		goto out;
627
628	is_hid =
629	     (new_dentry->d_name.name[0] == '.') && (new_msdos_name[0] != '.');
630
631	err = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
632			      new_dir, new_msdos_name, new_dentry, is_hid);
633out:
634	unlock_super(sb);
635	if (!err)
636		err = fat_flush_inodes(sb, old_dir, new_dir);
637	return err;
638}
639
640static const struct inode_operations msdos_dir_inode_operations = {
641	.create		= msdos_create,
642	.lookup		= msdos_lookup,
643	.unlink		= msdos_unlink,
644	.mkdir		= msdos_mkdir,
645	.rmdir		= msdos_rmdir,
646	.rename		= msdos_rename,
647	.setattr	= fat_setattr,
648	.getattr	= fat_getattr,
649};
650
651static void setup(struct super_block *sb)
652{
653	MSDOS_SB(sb)->dir_ops = &msdos_dir_inode_operations;
654	sb->s_d_op = &msdos_dentry_operations;
655	sb->s_flags |= MS_NOATIME;
656}
657
658static int msdos_fill_super(struct super_block *sb, void *data, int silent)
659{
660	return fat_fill_super(sb, data, silent, 0, setup);
661}
662
663static struct dentry *msdos_mount(struct file_system_type *fs_type,
664			int flags, const char *dev_name,
665			void *data)
666{
667	return mount_bdev(fs_type, flags, dev_name, data, msdos_fill_super);
668}
669
670static struct file_system_type msdos_fs_type = {
671	.owner		= THIS_MODULE,
672	.name		= "msdos",
673	.mount		= msdos_mount,
674	.kill_sb	= kill_block_super,
675	.fs_flags	= FS_REQUIRES_DEV,
676};
 
677
678static int __init init_msdos_fs(void)
679{
680	return register_filesystem(&msdos_fs_type);
681}
682
683static void __exit exit_msdos_fs(void)
684{
685	unregister_filesystem(&msdos_fs_type);
686}
687
688MODULE_LICENSE("GPL");
689MODULE_AUTHOR("Werner Almesberger");
690MODULE_DESCRIPTION("MS-DOS filesystem support");
691
692module_init(init_msdos_fs)
693module_exit(exit_msdos_fs)