1 |
1275 |
phoenix |
/*
|
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 |
|
|
};
|