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