1 /*
2  *  linux/fs/umsdos/rdir.c
3  *
4  *  Written 1994 by Jacques Gelinas
5  *
6  *  Extended MS-DOS directory pure MS-DOS handling functions
7  *  (For directory without EMD file).
8  */
9 
10 #include <linux/sched.h>
11 #include <linux/fs.h>
12 #include <linux/msdos_fs.h>
13 #include <linux/errno.h>
14 #include <linux/stat.h>
15 #include <linux/limits.h>
16 #include <linux/umsdos_fs.h>
17 #include <linux/slab.h>
18 
19 #include <asm/uaccess.h>
20 
21 
22 extern struct dentry *saved_root;
23 extern struct inode *pseudo_root;
24 extern struct dentry_operations umsdos_dentry_operations;
25 
26 struct RDIR_FILLDIR {
27 	void *dirbuf;
28 	filldir_t filldir;
29 	int real_root;
30 };
31 
rdir_filldir(void * buf,const char * name,int name_len,loff_t offset,ino_t ino,unsigned int d_type)32 static int rdir_filldir (	void *buf,
33 				const char *name,
34 				int name_len,
35 				loff_t offset,
36 				ino_t ino,
37 				unsigned int d_type)
38 {
39 	int ret = 0;
40 	struct RDIR_FILLDIR *d = (struct RDIR_FILLDIR *) buf;
41 
42 	if (d->real_root) {
43 		PRINTK ((KERN_DEBUG "rdir_filldir /mn/: real root!\n"));
44 		/* real root of a pseudo_rooted partition */
45 		if (name_len != UMSDOS_PSDROOT_LEN
46 		    || memcmp (name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN) != 0) {
47 			/* So it is not the /linux directory */
48 			if (name_len == 2 && name[0] == '.' && name[1] == '.') {
49 				/* Make sure the .. entry points back to the pseudo_root */
50 				ino = pseudo_root->i_ino;
51 			}
52 			ret = d->filldir (d->dirbuf, name, name_len, offset, ino, DT_UNKNOWN);
53 		}
54 	} else {
55 		/* Any DOS directory */
56 		ret = d->filldir (d->dirbuf, name, name_len, offset, ino, DT_UNKNOWN);
57 	}
58 	return ret;
59 }
60 
61 
UMSDOS_rreaddir(struct file * filp,void * dirbuf,filldir_t filldir)62 static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir)
63 {
64 	struct inode *dir = filp->f_dentry->d_inode;
65 	struct RDIR_FILLDIR bufk;
66 
67 	bufk.filldir = filldir;
68 	bufk.dirbuf = dirbuf;
69 	bufk.real_root = pseudo_root && (dir == saved_root->d_inode);
70 	return fat_readdir (filp, &bufk, rdir_filldir);
71 }
72 
73 
74 /*
75  * Lookup into a non promoted directory.
76  * If the result is a directory, make sure we find out if it is
77  * a promoted one or not (calling umsdos_setup_dir_inode(inode)).
78  */
79 /* #Specification: pseudo root / DOS/..
80  * In the real root directory (c:\), the directory ..
81  * is the pseudo root (c:\linux).
82  */
umsdos_rlookup_x(struct inode * dir,struct dentry * dentry,int nopseudo)83 struct dentry *umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo)
84 {
85 	struct dentry *ret;
86 
87 	if (saved_root && dir == saved_root->d_inode && !nopseudo &&
88 	    dentry->d_name.len == UMSDOS_PSDROOT_LEN &&
89 	    memcmp (dentry->d_name.name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN) == 0) {
90 		/* #Specification: pseudo root / DOS/linux
91 		 * Even in the real root directory (c:\), the directory
92 		 * /linux won't show
93 		 */
94 
95 		ret = ERR_PTR(-ENOENT);
96 		goto out;
97 	}
98 
99 	ret = msdos_lookup (dir, dentry);
100 	if (ret) {
101 		printk(KERN_WARNING
102 			"umsdos_rlookup_x: %s/%s failed, ret=%ld\n",
103 			dentry->d_parent->d_name.name, dentry->d_name.name,
104 			PTR_ERR(ret));
105 		goto out;
106 	}
107 	if (dentry->d_inode) {
108 		/* We must install the proper function table
109 		 * depending on whether this is an MS-DOS or
110 		 * a UMSDOS directory
111 		 */
112 Printk ((KERN_DEBUG "umsdos_rlookup_x: patch_dentry_inode %s/%s\n",
113 dentry->d_parent->d_name.name, dentry->d_name.name));
114 /* only patch if needed (because we get called even for lookup
115    (not only rlookup) stuff sometimes, like in umsdos_covered() */
116 		if (dentry->d_inode->u.umsdos_i.i_patched == 0)
117 		umsdos_patch_dentry_inode(dentry, 0);
118 
119 	}
120 out:
121 	/* always install our dentry ops ... */
122 	dentry->d_op = &umsdos_dentry_operations;
123 	return ret;
124 }
125 
126 
UMSDOS_rlookup(struct inode * dir,struct dentry * dentry)127 struct dentry *UMSDOS_rlookup ( struct inode *dir, struct dentry *dentry)
128 {
129 	return umsdos_rlookup_x (dir, dentry, 0);
130 }
131 
132 
133 /* #Specification: dual mode / rmdir in a DOS directory
134  * In a DOS (not EMD in it) directory, we use a reverse strategy
135  * compared with a UMSDOS directory. We assume that a subdirectory
136  * of a DOS directory is also a DOS directory. This is not always
137  * true (umssync may be used anywhere), but makes sense.
138  *
139  * So we call msdos_rmdir() directly. If it failed with a -ENOTEMPTY
140  * then we check if it is a Umsdos directory. We check if it is
141  * really empty (only . .. and --linux-.--- in it). If it is true
142  * we remove the EMD and do a msdos_rmdir() again.
143  *
144  * In a Umsdos directory, we assume all subdirectories are also
145  * Umsdos directories, so we check the EMD file first.
146  */
147 /* #Specification: pseudo root / rmdir /DOS
148  * The pseudo sub-directory /DOS can't be removed!
149  * This is done even if the pseudo root is not a Umsdos
150  * directory anymore (very unlikely), but an accident (under
151  * MS-DOS) is always possible.
152  *
153  * EPERM is returned.
154  */
UMSDOS_rrmdir(struct inode * dir,struct dentry * dentry)155 static int UMSDOS_rrmdir ( struct inode *dir, struct dentry *dentry)
156 {
157 	int ret, empty;
158 
159 	ret = -EPERM;
160 	if (umsdos_is_pseudodos (dir, dentry))
161 		goto out;
162 
163 	ret = -EBUSY;
164 	if (!d_unhashed(dentry))
165 		goto out;
166 
167 	ret = msdos_rmdir (dir, dentry);
168 	if (ret != -ENOTEMPTY)
169 		goto out;
170 
171 	empty = umsdos_isempty (dentry);
172 	if (empty == 1) {
173 		struct dentry *demd;
174 		/* We have to remove the EMD file. */
175 		demd = umsdos_get_emd_dentry(dentry);
176 		ret = PTR_ERR(demd);
177 		if (!IS_ERR(demd)) {
178 			ret = 0;
179 			if (demd->d_inode)
180 				ret = msdos_unlink (dentry->d_inode, demd);
181 			if (!ret)
182 				d_delete(demd);
183 			dput(demd);
184 		}
185 	}
186 	if (ret)
187 		goto out;
188 
189 	/* now retry the original ... */
190 	ret = msdos_rmdir (dir, dentry);
191 
192 out:
193 	return ret;
194 }
195 
196 /* #Specification: dual mode / introduction
197  * One goal of UMSDOS is to allow a practical and simple coexistence
198  * between MS-DOS and Linux in a single partition. Using the EMD file
199  * in each directory, UMSDOS adds Unix semantics and capabilities to
200  * a normal DOS filesystem. To help and simplify coexistence, here is
201  * the logic related to the EMD file.
202  *
203  * If it is missing, then the directory is managed by the MS-DOS driver.
204  * The names are limited to DOS limits (8.3). No links, no device special
205  * and pipe and so on.
206  *
207  * If it is there, it is the directory. If it is there but empty, then
208  * the directory looks empty. The utility umssync allows synchronisation
209  * of the real DOS directory and the EMD.
210  *
211  * Whenever umssync is applied to a directory without EMD, one is
212  * created on the fly.  The directory is promoted to full Unix semantics.
213  * Of course, the ls command will show exactly the same content as before
214  * the umssync session.
215  *
216  * It is believed that the user/admin will promote directories to Unix
217  * semantics as needed.
218  *
219  * The strategy to implement this is to use two function table (struct
220  * inode_operations). One for true UMSDOS directory and one for directory
221  * with missing EMD.
222  *
223  * Functions related to the DOS semantic (but aware of UMSDOS) generally
224  * have a "r" prefix (r for real) such as UMSDOS_rlookup, to differentiate
225  * from the one with full UMSDOS semantics.
226  */
227 struct file_operations umsdos_rdir_operations =
228 {
229 	read:		generic_read_dir,
230 	readdir:	UMSDOS_rreaddir,
231 	ioctl:		UMSDOS_ioctl_dir,
232 };
233 
234 struct inode_operations umsdos_rdir_inode_operations =
235 {
236 	create:		msdos_create,
237 	lookup:		UMSDOS_rlookup,
238 	unlink:		msdos_unlink,
239 	mkdir:		msdos_mkdir,
240 	rmdir:		UMSDOS_rrmdir,
241 	rename:		msdos_rename,
242 	setattr:	UMSDOS_notify_change,
243 };
244