namei.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:29k
- /*
- * 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_LOCK
- static 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=%un", 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);
- }
- #else
- static 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)
- {
- }
- #endif
- static 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/%sn",
- 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=%dn",
- 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=%dn", 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=%dn", 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, unlinkingn");
- 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_VERBOSE
- printk("umsdos_link: new %s/%s -> %s/%sn",
- 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) {
- if (atomic_read(&olddir->i_sem.count) < 1)
- goto out_free;
- down(&olddir->i_sem);
- }
- /*
- * Parse the name and get the visible directory entry.
- */
- ret = umsdos_parse (olddentry->d_name.name, olddentry->d_name.len,
- &old_info);
- if (ret)
- goto out_unlock;
- ret = umsdos_findentry (olddentry->d_parent, &old_info, 1);
- if (ret) {
- printk("UMSDOS_link: %s/%s not in EMD, ret=%dn",
- olddentry->d_parent->d_name.name, olddentry->d_name.name, ret);
- goto out_unlock;
- }
- /*
- * If the visible dentry is a pseudo-hardlink, the original
- * file must be already hidden.
- */
- if (!(old_info.entry.flags & UMSDOS_HLINK)) {
- int err;
- /* create a hidden link name */
- ret = umsdos_newhidden (olddentry->d_parent, &hid_info);
- if (ret) {
- printk("umsdos_link: can't make hidden %s/%s, ret=%dn",
- olddentry->d_parent->d_name.name, hid_info.entry.name, ret);
- goto out_unlock;
- }
- /*
- * Make a dentry and rename the original file ...
- */
- temp = umsdos_lookup_dentry(olddentry->d_parent,
- hid_info.entry.name,
- hid_info.entry.name_len, 0);
- ret = PTR_ERR(temp);
- if (IS_ERR(temp)) {
- printk("umsdos_link: lookup %s/%s failed, ret=%dn",
- dentry->d_parent->d_name.name, hid_info.entry.name, ret);
- goto cleanup;
- }
- /* rename the link to the hidden location ... */
- ret = umsdos_rename_f(olddir, olddentry, olddir, temp,
- UMSDOS_HIDDEN);
- d_move(olddentry, temp);
- dput(temp);
- if (ret) {
- printk("umsdos_link: rename to %s/%s failed, ret=%dn",
- temp->d_parent->d_name.name, temp->d_name.name, ret);
- goto cleanup;
- }
- /* mark the inode as a hardlink */
- oldinode->u.umsdos_i.i_is_hlink = 1;
- /*
- * Capture the path to the hidden link.
- */
- path = umsdos_d_path(olddentry, (char *) buffer, PAGE_SIZE);
- Printk(("umsdos_link: hidden link path=%sn", path));
- /*
- * Recreate a dentry for the original name and symlink it,
- * then symlink the new dentry. Don't give up if one fails,
- * or we'll lose the file completely!
- *
- * Note: this counts as the "original" reference, so we
- * don't increment i_nlink for this one.
- */
- temp = umsdos_lookup_dentry(olddentry->d_parent,
- old_info.entry.name,
- old_info.entry.name_len, 0);
- ret = PTR_ERR(temp);
- if (!IS_ERR(temp)) {
- ret = umsdos_symlink_x (olddir, temp, path,
- S_IFREG | 0777, UMSDOS_HLINK);
- dput(temp);
- }
- /* This symlink increments i_nlink (see below.) */
- err = umsdos_symlink_x (dir, dentry, path,
- S_IFREG | 0777, UMSDOS_HLINK);
- /* fold the two errors */
- if (!ret)
- ret = err;
- goto out_unlock;
- /* creation failed ... remove the link entry */
- cleanup:
- printk("umsdos_link: link failed, ret=%d, removing %s/%sn",
- ret, olddentry->d_parent->d_name.name, hid_info.entry.name);
- err = umsdos_delentry(olddentry->d_parent, &hid_info, 0);
- goto out_unlock;
- }
- Printk(("UMSDOS_link: %s/%s already hiddenn",
- olddentry->d_parent->d_name.name, olddentry->d_name.name));
- /*
- * The original file is already hidden, and we need to get
- * the dentry for its real name, not the visible name.
- * N.B. make sure it's the hidden inode ...
- */
- if (!oldinode->u.umsdos_i.i_is_hlink)
- printk("UMSDOS_link: %s/%s hidden, ino=%ld not hlink??n",
- olddentry->d_parent->d_name.name,
- olddentry->d_name.name, oldinode->i_ino);
- /*
- * In order to get the correct (real) inode, we just drop
- * the original dentry.
- */
- d_drop(olddentry);
- Printk(("UMSDOS_link: hard link %s/%s, fake=%sn",
- olddentry->d_parent->d_name.name, olddentry->d_name.name, old_info.fake.fname));
- /* Do a real lookup to get the short name dentry */
- temp = umsdos_covered(olddentry->d_parent, old_info.fake.fname,
- old_info.fake.len);
- ret = PTR_ERR(temp);
- if (IS_ERR(temp))
- goto out_unlock;
- /* now resolve the link ... */
- temp = umsdos_solve_hlink(temp);
- ret = PTR_ERR(temp);
- if (IS_ERR(temp))
- goto out_unlock;
- path = umsdos_d_path(temp, (char *) buffer, PAGE_SIZE);
- dput(temp);
- Printk(("umsdos_link: %s/%s already hidden, path=%sn",
- olddentry->d_parent->d_name.name, olddentry->d_name.name, path));
- /* finally we can symlink it ... */
- ret = umsdos_symlink_x (dir, dentry, path, S_IFREG | 0777,UMSDOS_HLINK);
- out_unlock:
- /* remain locked for the call to notify_change ... */
- if (ret == 0) {
- struct iattr newattrs;
- /* Do a real lookup to get the short name dentry */
- temp = umsdos_covered(olddentry->d_parent,
- old_info.fake.fname,
- old_info.fake.len);
- ret = PTR_ERR(temp);
- if (IS_ERR(temp))
- goto out_unlock2;
- /* now resolve the link ... */
- temp = umsdos_solve_hlink(temp);
- ret = PTR_ERR(temp);
- if (IS_ERR(temp))
- goto out_unlock2;
- #ifdef UMSDOS_PARANOIA
- if (!oldinode->u.umsdos_i.i_is_hlink)
- printk("UMSDOS_link: %s/%s, ino=%ld, not marked as hlink!n",
- olddentry->d_parent->d_name.name, olddentry->d_name.name, oldinode->i_ino);
- #endif
- temp->d_inode->i_nlink++;
- Printk(("UMSDOS_link: linked %s/%s, ino=%ld, nlink=%dn",
- olddentry->d_parent->d_name.name, olddentry->d_name.name,
- oldinode->i_ino, oldinode->i_nlink));
- newattrs.ia_valid = 0;
- ret = umsdos_notify_change_locked(temp, &newattrs);
- if (ret == 0)
- mark_inode_dirty(temp->d_inode);
- dput(temp);
- out_unlock2:
- if (ret == 0)
- mark_inode_dirty(olddentry->d_inode);
- }
- if (olddir != dir)
- up(&olddir->i_sem);
- out_free:
- free_page(buffer);
- out:
- Printk (("umsdos_link %dn", ret));
- return ret;
- }
- /*
- * Add a sub-directory in a directory
- */
- /* #Specification: mkdir / Directory already exist in DOS
- * We do the same thing as for file creation.
- * For all user it is an error.
- */
- /* #Specification: mkdir / umsdos directory / create EMD
- * When we created a new sub-directory in a UMSDOS
- * directory (one with full UMSDOS semantics), we
- * create immediately an EMD file in the new
- * sub-directory so it inherits UMSDOS semantics.
- */
- int UMSDOS_mkdir (struct inode *dir, struct dentry *dentry, int mode)
- {
- struct dentry *temp;
- struct inode *inode;
- int ret, err;
- 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 | S_IFDIR;
- info.entry.rdev = 0;
- 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.flags = 0;
- info.entry.nlink = 1;
- ret = umsdos_newentry (dentry->d_parent, &info);
- if (ret)
- goto out;
- /* lookup the short name dentry */
- temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
- ret = PTR_ERR(temp);
- if (IS_ERR(temp))
- goto out_remove;
- /* Make sure the short name doesn't exist */
- ret = -EEXIST;
- if (temp->d_inode) {
- printk("umsdos_mkdir: short name %s/%s existsn",
- dentry->d_parent->d_name.name, info.fake.fname);
- goto out_remove_dput;
- }
- ret = msdos_mkdir (dir, temp, mode);
- if (ret)
- goto out_remove_dput;
- /*
- * Lock the inode to protect the EMD creation ...
- */
- inode = temp->d_inode;
- down(&inode->i_sem);
- atomic_inc(&inode->i_count);
- d_instantiate(dentry, inode);
- /* N.B. this should have an option to create the EMD ... */
- umsdos_lookup_patch_new(dentry, &info);
- /*
- * Create the EMD file, and set up the dir so it is
- * promoted to EMD with the EMD file invisible.
- *
- * N.B. error return if EMD fails?
- */
- err = umsdos_make_emd(dentry);
- umsdos_setup_dir(dentry);
- up(&inode->i_sem);
- dput(temp);
- out:
- Printk(("umsdos_mkdir: %s/%s, ret=%dn",
- dentry->d_parent->d_name.name, dentry->d_name.name, ret));
- return ret;
- /* an error occurred ... remove EMD entry. */
- out_remove_dput:
- dput(temp);
- out_remove:
- umsdos_delentry (dentry->d_parent, &info, 1);
- goto out;
- }
- /*
- * Add a new device special file into a directory.
- *
- * #Specification: Special files / strategy
- * Device special file, pipes, etc ... are created like normal
- * file in the msdos file system. Of course they remain empty.
- *
- * One strategy was to create those files only in the EMD file
- * since they were not important for MSDOS. The problem with
- * that, is that there were not getting inode number allocated.
- * The MSDOS filesystems is playing a nice game to fake inode
- * number, so why not use it.
- *
- * The absence of inode number compatible with those allocated
- * for ordinary files was causing major trouble with hard link
- * in particular and other parts of the kernel I guess.
- */
- int UMSDOS_mknod (struct inode *dir, struct dentry *dentry,
- int mode, int rdev)
- {
- return umsdos_create_any (dir, dentry, mode, rdev, 0);
- }
- /*
- * Remove a sub-directory.
- */
- int UMSDOS_rmdir (struct inode *dir, struct dentry *dentry)
- {
- struct dentry *temp;
- int ret, err, empty;
- struct umsdos_info info;
- ret = umsdos_nevercreat (dir, dentry, -EPERM);
- if (ret)
- goto out;
- ret = -EBUSY;
- if (!d_unhashed(dentry))
- goto out;
- /* check whether the EMD is empty */
- ret = -ENOTEMPTY;
- empty = umsdos_isempty (dentry);
- /* Have to remove the EMD file? */
- if (empty == 1) {
- struct dentry *demd;
- demd = umsdos_get_emd_dentry(dentry);
- if (!IS_ERR(demd)) {
- err = -ENOENT;
- if (demd->d_inode)
- err = msdos_unlink (dentry->d_inode, demd);
- Printk (("UMSDOS_rmdir: unlinking empty EMD err=%d", err));
- #ifdef UMSDOS_PARANOIA
- if (err)
- printk("umsdos_rmdir: EMD %s/%s unlink failed, err=%dn",
- demd->d_parent->d_name.name, demd->d_name.name, err);
- #endif
- if (!err) {
- d_delete(demd);
- ret = 0;
- }
- dput(demd);
- }
- } else if (empty == 2)
- ret = 0;
- if (ret)
- goto out;
- umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
- /* Call findentry to complete the mangling */
- umsdos_findentry (dentry->d_parent, &info, 2);
- temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
- ret = PTR_ERR(temp);
- if (IS_ERR(temp))
- goto out;
- /*
- * Attempt to remove the msdos name.
- */
- ret = msdos_rmdir (dir, temp);
- if (ret && ret != -ENOENT)
- goto out_dput;
- d_delete(temp);
- /* OK so far ... remove the name from the EMD */
- ret = umsdos_delentry (dentry->d_parent, &info, 1);
- #ifdef UMSDOS_PARANOIA
- if (ret)
- printk("umsdos_rmdir: delentry %s failed, ret=%dn", info.entry.name, ret);
- #endif
- /* dput() temp if we didn't do it above */
- out_dput:
- dput(temp);
- out:
- Printk (("umsdos_rmdir %dn", ret));
- return ret;
- }
- /*
- * Remove a file from the directory.
- *
- * #Specification: hard link / deleting a link
- * When we delete a file and this file is a link,
- * we must subtract 1 from the nlink field of the
- * hidden link.
- *
- * If the count goes to 0, we delete this hidden
- * link too.
- */
- int UMSDOS_unlink (struct inode *dir, struct dentry *dentry)
- {
- struct dentry *temp, *link = NULL;
- struct inode *inode;
- int ret;
- struct umsdos_info info;
- Printk(("UMSDOS_unlink: entering %s/%sn",
- dentry->d_parent->d_name.name, dentry->d_name.name));
- ret = umsdos_nevercreat (dir, dentry, -EPERM);
- if (ret)
- goto out;
- ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
- if (ret)
- goto out;
- umsdos_lockcreate (dir);
- ret = umsdos_findentry (dentry->d_parent, &info, 1);
- if (ret) {
- printk("UMSDOS_unlink: %s/%s not in EMD, ret=%dn",
- dentry->d_parent->d_name.name, dentry->d_name.name, ret);
- goto out_unlock;
- }
- Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname));
- /*
- * Note! If this is a hardlink and the names are aliased,
- * the short-name lookup will return the hardlink dentry.
- * In order to get the correct (real) inode, we just drop
- * the original dentry.
- */
- if (info.entry.flags & UMSDOS_HLINK) {
- d_drop(dentry);
- }
- /* Do a real lookup to get the short name dentry */
- temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
- ret = PTR_ERR(temp);
- if (IS_ERR(temp))
- goto out_unlock;
- /*
- * Resolve hardlinks now, but defer processing until later.
- */
- if (info.entry.flags & UMSDOS_HLINK) {
- link = umsdos_solve_hlink(dget(temp));
- }
- /* Delete the EMD entry */
- ret = umsdos_delentry (dentry->d_parent, &info, 0);
- if (ret && ret != -ENOENT) {
- printk(KERN_WARNING "UMSDOS_unlink: delentry %s, error=%dn",
- info.entry.name, ret);
- goto out_dput;
- }
- ret = msdos_unlink(dir, temp);
- if (!ret)
- d_delete(temp);
- #ifdef UMSDOS_PARANOIA
- if (ret)
- printk("umsdos_unlink: %s/%s unlink failed, ret=%dn",
- temp->d_parent->d_name.name, temp->d_name.name, ret);
- #endif
- /* dput() temp if we didn't do it above */
- out_dput:
- dput(temp);
- out_unlock:
- umsdos_unlockcreate (dir);
- /*
- * Now check for deferred handling of a hardlink.
- */
- if (!link)
- goto out;
- if (IS_ERR(link)) {
- printk("umsdos_unlink: failed to resolve %s/%sn",
- dentry->d_parent->d_name.name, dentry->d_name.name);
- if (!ret)
- ret = PTR_ERR(link);
- goto out;
- }
- Printk(("umsdos_unlink: link %s/%s deferred, pending ret=%dn",
- link->d_parent->d_name.name, link->d_name.name, ret));
- /* already have an error? */
- if (ret)
- goto out_cleanup;
- /* make sure the link exists ... */
- inode = link->d_inode;
- if (!inode) {
- printk(KERN_WARNING "umsdos_unlink: hard link not foundn");
- goto out_cleanup;
- }
- /*
- * If this was the last linked reference, delete it now.
- *
- * N.B. Deadlock problem? We should be holding the lock
- * for the hardlink's parent, but another process might
- * be holding that lock waiting for us to finish ...
- */
- if (inode->i_nlink <= 1) {
- ret = UMSDOS_unlink (link->d_parent->d_inode, link);
- if (ret) {
- printk(KERN_WARNING
- "umsdos_unlink: link removal failed, ret=%dn",
- ret);
- } else
- d_delete(link);
- } else {
- struct iattr newattrs;
- inode->i_nlink--;
- newattrs.ia_valid = 0;
- ret = umsdos_notify_change_locked(link, &newattrs);
- if (!ret)
- mark_inode_dirty(link->d_inode);
- }
- out_cleanup:
- d_drop(link);
- dput(link);
- out:
- Printk (("umsdos_unlink %dn", ret));
- return ret;
- }
- /*
- * Rename (move) a file.
- */
- int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
- {
- int ret;
- ret = umsdos_nevercreat (new_dir, new_dentry, -EEXIST);
- if (ret)
- return ret;
- /*
- * If the target already exists, delete it first.
- */
- if (new_dentry->d_inode) {
- dget(new_dentry);
- if (S_ISDIR(old_dentry->d_inode->i_mode))
- ret = UMSDOS_rmdir (new_dir, new_dentry);
- else
- ret = UMSDOS_unlink (new_dir, new_dentry);
- if (!ret)
- d_drop(new_dentry);
- dput(new_dentry);
- if (ret)
- return ret;
- }
- ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0);
- return ret;
- }