File: /usr/src/linux/fs/umsdos/rdir.c

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     
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     
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      */
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     
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      */
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