| /* |
| * 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, |
| loff_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)); |
| /* only patch if needed (because we get called even for lookup |
| (not only rlookup) stuff sometimes, like in umsdos_covered() */ |
| if (UMSDOS_I(dentry->d_inode)->i_patched == 0) |
| 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, |
| }; |