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