📄 namei.c
字号:
/* * linux/fs/umsdos/namei.c * * Written 1993 by Jacques Gelinas * Inspired from linux/fs/msdos/... by Werner Almesberger * * Maintain and access the --linux alternate directory file. */ /* * You are in the maze of twisted functions - half of them shouldn't * be here... */#include <linux/errno.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/stat.h>#include <linux/string.h>#include <linux/msdos_fs.h>#include <linux/umsdos_fs.h>#include <linux/slab.h>#define UMSDOS_DIR_LOCK#ifdef UMSDOS_DIR_LOCKstatic inline void u_sleep_on (struct inode *dir){ sleep_on (&dir->u.umsdos_i.dir_info.p);}static inline void u_wake_up (struct inode *dir){ wake_up (&dir->u.umsdos_i.dir_info.p);}/* * Wait for creation exclusivity. * Return 0 if the dir was already available. * Return 1 if a wait was necessary. * When 1 is return, it means a wait was done. It does not * mean the directory is available. */static int umsdos_waitcreate (struct inode *dir){ int ret = 0; if (dir->u.umsdos_i.dir_info.creating && dir->u.umsdos_i.dir_info.pid != current->pid) { PRINTK (("creating && dir_info.pid=%lu, current->pid=%u\n", dir->u.umsdos_i.dir_info.pid, current->pid)); u_sleep_on (dir); ret = 1; } return ret;}/* * Wait for any lookup process to finish */static void umsdos_waitlookup (struct inode *dir){ while (dir->u.umsdos_i.dir_info.looking) { u_sleep_on (dir); }}/* * Lock all other process out of this directory. *//* #Specification: file creation / not atomic * File creation is a two step process. First we create (allocate) * an entry in the EMD file and then (using the entry offset) we * build a unique name for MSDOS. We create this name in the msdos * space. * * We have to use semaphore (sleep_on/wake_up) to prevent lookup * into a directory when we create a file or directory and to * prevent creation while a lookup is going on. Since many lookup * may happen at the same time, the semaphore is a counter. * * Only one creation is allowed at the same time. This protection * may not be necessary. The problem arise mainly when a lookup * or a readdir is done while a file is partially created. The * lookup process see that as a "normal" problem and silently * erase the file from the EMD file. Normal because a file * may be erased during a MSDOS session, but not removed from * the EMD file. * * The locking is done on a directory per directory basis. Each * directory inode has its wait_queue. * * For some operation like hard link, things even get worse. Many * creation must occur at once (atomic). To simplify the design * a process is allowed to recursively lock the directory for * creation. The pid of the locking process is kept along with * a counter so a second level of locking is granted or not. */void umsdos_lockcreate (struct inode *dir){ /* * Wait for any creation process to finish except * if we (the process) own the lock */ while (umsdos_waitcreate (dir) != 0); dir->u.umsdos_i.dir_info.creating++; dir->u.umsdos_i.dir_info.pid = current->pid; umsdos_waitlookup (dir);}/* * Lock all other process out of those two directories. */static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2){ /* * We must check that both directory are available before * locking anyone of them. This is to avoid some deadlock. * Thanks to dglaude@is1.vub.ac.be (GLAUDE DAVID) for pointing * this to me. */ while (1) { if (umsdos_waitcreate (dir1) == 0 && umsdos_waitcreate (dir2) == 0) { /* We own both now */ dir1->u.umsdos_i.dir_info.creating++; dir1->u.umsdos_i.dir_info.pid = current->pid; dir2->u.umsdos_i.dir_info.creating++; dir2->u.umsdos_i.dir_info.pid = current->pid; break; } } umsdos_waitlookup (dir1); umsdos_waitlookup (dir2);}/* * Wait until creation is finish in this directory. */void umsdos_startlookup (struct inode *dir){ while (umsdos_waitcreate (dir) != 0); dir->u.umsdos_i.dir_info.looking++;}/* * Unlock the directory. */void umsdos_unlockcreate (struct inode *dir){ dir->u.umsdos_i.dir_info.creating--; if (dir->u.umsdos_i.dir_info.creating < 0) { printk ("UMSDOS: dir->u.umsdos_i.dir_info.creating < 0: %d" ,dir->u.umsdos_i.dir_info.creating); } u_wake_up (dir);}/* * Tell directory lookup is over. */void umsdos_endlookup (struct inode *dir){ dir->u.umsdos_i.dir_info.looking--; if (dir->u.umsdos_i.dir_info.looking < 0) { printk ("UMSDOS: dir->u.umsdos_i.dir_info.looking < 0: %d" ,dir->u.umsdos_i.dir_info.looking); } u_wake_up (dir);}#elsestatic void umsdos_lockcreate (struct inode *dir){}static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2){}void umsdos_startlookup (struct inode *dir){}static void umsdos_unlockcreate (struct inode *dir){}void umsdos_endlookup (struct inode *dir){}#endifstatic int umsdos_nevercreat (struct inode *dir, struct dentry *dentry, int errcod){ int ret = 0; if (umsdos_is_pseudodos (dir, dentry)) { /* #Specification: pseudo root / any file creation /DOS * The pseudo sub-directory /DOS can't be created! * EEXIST is returned. * * The pseudo sub-directory /DOS can't be removed! * EPERM is returned. */ ret = errcod; } return ret;}/* * Add a new file (ordinary or special) into the alternate directory. * The file is added to the real MSDOS directory. If successful, it * is then added to the EMD file. * * Return the status of the operation. 0 mean success. * * #Specification: create / file exists in DOS * Here is a situation: we are trying to create a file with * UMSDOS. The file is unknown to UMSDOS but already * exists in the DOS directory. * * Here is what we are NOT doing: * * We could silently assume that everything is fine * and allows the creation to succeed. * * It is possible not all files in the partition * are meant to be visible from linux. By trying to create * those file in some directory, one user may get access * to those file without proper permissions. Looks like * a security hole to me. Off course sharing a file system * with DOS is some kind of security hole :-) * * So ? * * We return EEXIST in this case. * The same is true for directory creation. */static int umsdos_create_any (struct inode *dir, struct dentry *dentry, int mode, int rdev, char flags){ struct dentry *fake; struct inode *inode; int ret; struct umsdos_info info; ret = umsdos_nevercreat (dir, dentry, -EEXIST); if (ret) goto out; ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); if (ret) goto out; info.entry.mode = mode; info.entry.rdev = rdev; info.entry.flags = flags; info.entry.uid = current->fsuid; info.entry.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; info.entry.ctime = info.entry.atime = info.entry.mtime = CURRENT_TIME; info.entry.nlink = 1; ret = umsdos_newentry (dentry->d_parent, &info); if (ret) goto out; /* do a real lookup to get the short name dentry */ fake = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len); ret = PTR_ERR(fake); if (IS_ERR(fake)) goto out_remove; /* should not exist yet ... */ ret = -EEXIST; if (fake->d_inode) goto out_remove_dput; ret = msdos_create (dir, fake, S_IFREG | 0777); if (ret) goto out_remove_dput; inode = fake->d_inode; atomic_inc(&inode->i_count); d_instantiate (dentry, inode); dput(fake); if (atomic_read(&inode->i_count) > 1) { printk(KERN_WARNING "umsdos_create_any: %s/%s, ino=%ld, icount=%d??\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, atomic_read(&inode->i_count)); } umsdos_lookup_patch_new(dentry, &info);out: return ret; /* Creation failed ... remove the EMD entry */out_remove_dput: dput(fake);out_remove: if (ret == -EEXIST) printk(KERN_WARNING "UMSDOS: out of sync, deleting %s/%s\n", dentry->d_parent->d_name.name, info.fake.fname); umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode)); goto out;}/* * Add a new file into the alternate directory. * The file is added to the real MSDOS directory. If successful, it * is then added to the EMD file. * * Return the status of the operation. 0 mean success. */int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode){ return umsdos_create_any (dir, dentry, mode, 0, 0);}/* * Initialise the new_entry from the old for a rename operation. * (Only useful for umsdos_rename_f() below). */static void umsdos_ren_init (struct umsdos_info *new_info, struct umsdos_info *old_info){ new_info->entry.mode = old_info->entry.mode; new_info->entry.rdev = old_info->entry.rdev; new_info->entry.uid = old_info->entry.uid; new_info->entry.gid = old_info->entry.gid; new_info->entry.ctime = old_info->entry.ctime; new_info->entry.atime = old_info->entry.atime; new_info->entry.mtime = old_info->entry.mtime; new_info->entry.flags = old_info->entry.flags; new_info->entry.nlink = old_info->entry.nlink;}/* * Rename a file (move) in the file system. */ static int umsdos_rename_f (struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, int flags){ struct inode *old_inode = old_dentry->d_inode; struct dentry *old, *new, *old_emd; int err, ret; struct umsdos_info old_info; struct umsdos_info new_info; ret = -EPERM; err = umsdos_parse (old_dentry->d_name.name, old_dentry->d_name.len, &old_info); if (err) goto out; err = umsdos_parse (new_dentry->d_name.name, new_dentry->d_name.len, &new_info); if (err) goto out; /* Get the EMD dentry for the old parent */ old_emd = umsdos_get_emd_dentry(old_dentry->d_parent); ret = PTR_ERR(old_emd); if (IS_ERR(old_emd)) goto out; umsdos_lockcreate2 (old_dir, new_dir); ret = umsdos_findentry(old_emd->d_parent, &old_info, 0); if (ret) goto out_unlock; err = umsdos_findentry(new_dentry->d_parent, &new_info, 0); if (err == 0) { /* check whether it _really_ exists ... */ ret = -EEXIST; if (new_dentry->d_inode) goto out_unlock; /* bogus lookup? complain and fix up the EMD ... */ printk(KERN_WARNING "umsdos_rename_f: entry %s/%s exists, inode NULL??\n", new_dentry->d_parent->d_name.name, new_info.entry.name); err = umsdos_delentry(new_dentry->d_parent, &new_info, S_ISDIR(new_info.entry.mode)); } umsdos_ren_init (&new_info, &old_info); if (flags) new_info.entry.flags = flags; ret = umsdos_newentry (new_dentry->d_parent, &new_info); if (ret) goto out_unlock; /* If we're moving a hardlink, drop it first */ if (old_info.entry.flags & UMSDOS_HLINK) { d_drop(old_dentry); } old = umsdos_covered(old_dentry->d_parent, old_info.fake.fname, old_info.fake.len); ret = PTR_ERR(old); if (IS_ERR(old)) goto out_unlock; /* make sure it's the same inode! */ ret = -ENOENT; /* * note: for hardlinks they will be different! * old_inode will contain inode of .LINKxxx file containing data, and * old->d_inode will contain inode of file containing path to .LINKxxx file */ if (!(old_info.entry.flags & UMSDOS_HLINK)) { if (old->d_inode != old_inode) goto out_dput; } new = umsdos_covered(new_dentry->d_parent, new_info.fake.fname, new_info.fake.len); ret = PTR_ERR(new); if (IS_ERR(new)) goto out_dput; /* Do the msdos-level rename */ ret = msdos_rename (old_dir, old, new_dir, new); dput(new); /* If the rename failed, remove the new EMD entry */ if (ret != 0) { umsdos_delentry (new_dentry->d_parent, &new_info, S_ISDIR (new_info.entry.mode)); goto out_dput; } /* * Rename successful ... remove the old name from the EMD. * Note that we use the EMD parent here, as the old dentry * may have moved to a new parent ... */ err = umsdos_delentry (old_emd->d_parent, &old_info, S_ISDIR (old_info.entry.mode)); if (err) { /* Failed? Complain a bit, but don't fail the operation */ printk(KERN_WARNING "umsdos_rename_f: delentry %s/%s failed, error=%d\n", old_emd->d_parent->d_name.name, old_info.entry.name, err); } /* * Update f_pos so notify_change will succeed * if the file was already in use. */ umsdos_set_dirinfo_new(old_dentry, new_info.f_pos); /* dput() the dentry if we haven't already */out_dput: dput(old);out_unlock: dput(old_emd); umsdos_unlockcreate (old_dir); umsdos_unlockcreate (new_dir);out: Printk ((" _ret=%d\n", ret)); return ret;}/* * Setup a Symbolic link or a (pseudo) hard link * Return a negative error code or 0 if OK. *//* #Specification: symbolic links / strategy * A symbolic link is simply a file which holds a path. It is * implemented as a normal MSDOS file (not very space efficient :-() * * I see two different ways to do this: One is to place the link data * in unused entries of the EMD file; the other is to have a separate * file dedicated to hold all symbolic links data. * * Let's go for simplicity... *//* * AV. Should be called with dir->i_sem down. */static int umsdos_symlink_x (struct inode *dir, struct dentry *dentry, const char *symname, int mode, char flags){ int ret, len; ret = umsdos_create_any (dir, dentry, mode, 0, flags); if (ret) { printk(KERN_WARNING "umsdos_symlink: create failed, ret=%d\n", ret); goto out; } len = strlen (symname) + 1; ret = block_symlink(dentry->d_inode, symname, len); if (ret < 0) goto out_unlink;out: return ret;out_unlink: printk(KERN_WARNING "umsdos_symlink: write failed, unlinking\n"); UMSDOS_unlink (dir, dentry); d_drop(dentry); goto out;}/* * Setup a Symbolic link. * Return a negative error code or 0 if OK. */int UMSDOS_symlink ( struct inode *dir, struct dentry *dentry, const char *symname){ return umsdos_symlink_x (dir, dentry, symname, S_IFLNK | 0777, 0);}/* * Add a link to an inode in a directory */int UMSDOS_link (struct dentry *olddentry, struct inode *dir, struct dentry *dentry){ struct inode *oldinode = olddentry->d_inode; struct inode *olddir = olddentry->d_parent->d_inode; struct dentry *temp; char *path; unsigned long buffer; int ret; struct umsdos_info old_info; struct umsdos_info hid_info;#ifdef UMSDOS_DEBUG_VERBOSEprintk("umsdos_link: new %s/%s -> %s/%s\n",dentry->d_parent->d_name.name, dentry->d_name.name, olddentry->d_parent->d_name.name, olddentry->d_name.name);#endif ret = -EPERM; if (S_ISDIR (oldinode->i_mode)) goto out; ret = umsdos_nevercreat (dir, dentry, -EPERM); if (ret) goto out; ret = -ENOMEM; buffer = get_free_page(GFP_KERNEL); if (!buffer) goto out; /* * Lock the link parent if it's not the same directory. */ ret = -EDEADLOCK; if (olddir != dir) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -