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