#include <linux/fcntl.h>
#include <linux/namei.h>
#include <linux/proc_fs.h>
-#include <linux/vs_base.h>
#include <linux/vserver/inode.h>
-#include <linux/vserver/debug.h>
+#include <linux/vs_base.h>
+#include <linux/vs_tag.h>
+#include <linux/vs_cowbl.h>
#include <asm/namei.h>
#include <asm/uaccess.h>
#ifdef CONFIG_AUDITSYSCALL
void putname(const char *name)
{
- if (unlikely(current->audit_context))
+ if (unlikely(!audit_dummy_context()))
audit_putname(name);
else
__putname(name);
return -EACCES;
}
-static inline int vx_barrier(struct inode *inode)
+static inline int dx_barrier(struct inode *inode)
{
- if (IS_BARRIER(inode) && !vx_check(0, VX_ADMIN)) {
+ if (IS_BARRIER(inode) && !vx_check(0, VS_ADMIN)) {
vxwprintk(1, "xid=%d did hit the barrier.",
vx_current_xid());
return 1;
return 0;
}
-static inline int xid_permission(struct inode *inode, int mask, struct nameidata *nd)
+static inline int dx_permission(struct inode *inode, int mask, struct nameidata *nd)
{
- if (vx_barrier(inode))
+ if (dx_barrier(inode))
return -EACCES;
- if (inode->i_xid == 0)
+ if (inode->i_tag == 0)
return 0;
#ifdef CONFIG_VSERVER_FILESHARING
/* MEF: PlanetLab FS module assumes that any file that can be
* named (e.g., via a cross mount) is not hidden from another
* context or the admin context.
*/
- if (vx_check(inode->i_xid,VX_STATIC|VX_DYNAMIC))
+ if (!dx_check(inode->i_tag, DX_HOSTID))
return 0;
#endif
- if (vx_check(inode->i_xid, VX_ADMIN|VX_WATCH|VX_IDENT))
+ if (dx_check(inode->i_tag, DX_ADMIN|DX_WATCH|DX_IDENT))
return 0;
vxwprintk(1, "xid=%d denied access to %p[#%d,%lu] »%s«.",
- vx_current_xid(), inode, inode->i_xid, inode->i_ino,
+ vx_current_xid(), inode, inode->i_tag, inode->i_ino,
vxd_cond_path(nd));
return -EACCES;
}
int permission(struct inode *inode, int mask, struct nameidata *nd)
{
+ umode_t mode = inode->i_mode;
int retval, submask;
if (mask & MAY_WRITE) {
- umode_t mode = inode->i_mode;
/*
* Nobody gets write access to a read-only fs.
/*
* Nobody gets write access to an immutable file.
*/
- if (IS_IMMUTABLE(inode))
+ if (IS_IMMUTABLE(inode) && !IS_COW(inode))
return -EACCES;
}
+ /*
+ * MAY_EXEC on regular files requires special handling: We override
+ * filesystem execute permissions if the mode bits aren't set or
+ * the fs is mounted with the "noexec" flag.
+ */
+ if ((mask & MAY_EXEC) && S_ISREG(mode) && (!(mode & S_IXUGO) ||
+ (nd && nd->mnt && (nd->mnt->mnt_flags & MNT_NOEXEC))))
+ return -EACCES;
+
/* Ordinary permission routines do not understand MAY_APPEND. */
submask = mask & ~MAY_APPEND;
- if ((retval = xid_permission(inode, mask, nd)))
+ if ((retval = dx_permission(inode, mask, nd)))
return retval;
if (inode->i_op && inode->i_op->permission)
retval = inode->i_op->permission(inode, submask, nd);
*/
int file_permission(struct file *file, int mask)
{
- return permission(file->f_dentry->d_inode, mask, NULL);
+ return permission(file->f_path.dentry->d_inode, mask, NULL);
}
/*
int deny_write_access(struct file * file)
{
- struct inode *inode = file->f_dentry->d_inode;
+ struct inode *inode = file->f_path.dentry->d_inode;
spin_lock(&inode->i_lock);
if (atomic_read(&inode->i_writecount) > 0) {
*/
void release_open_intent(struct nameidata *nd)
{
- if (nd->intent.open.file->f_dentry == NULL)
+ if (nd->intent.open.file->f_path.dentry == NULL)
put_filp(nd->intent.open.file);
else
fput(nd->intent.open.file);
}
+static inline struct dentry *
+do_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+ int status = dentry->d_op->d_revalidate(dentry, nd);
+ if (unlikely(status <= 0)) {
+ /*
+ * The dentry failed validation.
+ * If d_revalidate returned 0 attempt to invalidate
+ * the dentry otherwise d_revalidate is asking us
+ * to return a fail status.
+ */
+ if (!status) {
+ if (!d_invalidate(dentry)) {
+ dput(dentry);
+ dentry = NULL;
+ }
+ } else {
+ dput(dentry);
+ dentry = ERR_PTR(status);
+ }
+ }
+ return dentry;
+}
+
/*
* Internal lookup() using the new generic dcache.
* SMP-safe
if (!dentry)
dentry = d_lookup(parent, name);
- if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
- if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) {
- dput(dentry);
- dentry = NULL;
- }
- }
+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate)
+ dentry = do_revalidate(dentry, nd);
+
return dentry;
}
{
umode_t mode = inode->i_mode;
- if (vx_barrier(inode))
+ if (dx_barrier(inode))
return -EACCES;
if (inode->i_op && inode->i_op->permission)
return -EAGAIN;
*/
mutex_unlock(&dir->i_mutex);
if (result->d_op && result->d_op->d_revalidate) {
- if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) {
- dput(result);
+ result = do_revalidate(result, nd);
+ if (!result)
result = ERR_PTR(-ENOENT);
- }
}
return result;
}
static __always_inline int
walk_init_root(const char *name, struct nameidata *nd)
{
- read_lock(¤t->fs->lock);
- if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
- nd->mnt = mntget(current->fs->altrootmnt);
- nd->dentry = dget(current->fs->altroot);
- read_unlock(¤t->fs->lock);
+ struct fs_struct *fs = current->fs;
+
+ read_lock(&fs->lock);
+ if (fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
+ nd->mnt = mntget(fs->altrootmnt);
+ nd->dentry = dget(fs->altroot);
+ read_unlock(&fs->lock);
if (__emul_lookup_dentry(name,nd))
return 0;
- read_lock(¤t->fs->lock);
+ read_lock(&fs->lock);
}
- nd->mnt = mntget(current->fs->rootmnt);
- nd->dentry = dget(current->fs->root);
- read_unlock(¤t->fs->lock);
+ nd->mnt = mntget(fs->rootmnt);
+ nd->dentry = dget(fs->root);
+ read_unlock(&fs->lock);
return 1;
}
return PTR_ERR(link);
}
-struct path {
- struct vfsmount *mnt;
- struct dentry *dentry;
-};
-
static inline void dput_path(struct path *path, struct nameidata *nd)
{
dput(path->dentry);
static __always_inline void follow_dotdot(struct nameidata *nd)
{
+ struct fs_struct *fs = current->fs;
+
while(1) {
struct vfsmount *parent;
struct dentry *old = nd->dentry;
- read_lock(¤t->fs->lock);
- if (nd->dentry == current->fs->root &&
- nd->mnt == current->fs->rootmnt) {
- read_unlock(¤t->fs->lock);
+ read_lock(&fs->lock);
+ if (nd->dentry == fs->root &&
+ nd->mnt == fs->rootmnt) {
+ read_unlock(&fs->lock);
/* for sane '/' avoid follow_mount() */
return;
}
- read_unlock(¤t->fs->lock);
+ read_unlock(&fs->lock);
spin_lock(&dcache_lock);
if (nd->dentry != nd->mnt->mnt_root) {
nd->dentry = dget(nd->dentry->d_parent);
* It _is_ time-critical.
*/
static int do_lookup(struct nameidata *nd, struct qstr *name,
- struct path *path, int atomic)
+ struct path *path)
{
struct vfsmount *mnt = nd->mnt;
struct dentry *dentry = __d_lookup(nd->dentry, name);
inode = dentry->d_inode;
if (!inode)
goto done;
+ if (inode->i_sb->s_magic == PROC_SUPER_MAGIC) {
+ struct proc_dir_entry *de = PDE(inode);
+
+ if (de && !vx_hide_check(0, de->vx_flags))
+ goto hidden;
+ }
#ifdef CONFIG_VSERVER_FILESHARING
/* MEF: PlanetLab FS module assumes that any file that can be
* named (e.g., via a cross mount) is not hidden from another
* context or the admin context.
*/
- if (vx_check(inode->i_xid,VX_STATIC|VX_DYNAMIC|VX_ADMIN)) {
+ if (!dx_check(inode->i_tag, DX_HOSTID)) {
/* do nothing */
}
else /* do the following check */
#endif
- if (!vx_check(inode->i_xid, VX_WATCH|VX_ADMIN|VX_HOSTID|VX_IDENT))
+ if (!dx_check(inode->i_tag, DX_WATCH|DX_ADMIN|DX_HOSTID|DX_IDENT))
goto hidden;
- if (inode->i_sb->s_magic == PROC_SUPER_MAGIC) {
- struct proc_dir_entry *de = PDE(inode);
-
- if (de && !vx_hide_check(0, de->vx_flags))
- goto hidden;
- }
done:
path->mnt = mnt;
path->dentry = dentry;
return 0;
hidden:
vxwprintk(1, "xid=%d did lookup hidden %p[#%d,%lu] »%s«.",
- vx_current_xid(), inode, inode->i_xid, inode->i_ino,
+ vx_current_xid(), inode, inode->i_tag, inode->i_ino,
vxd_path(dentry, mnt));
dput(dentry);
return -ENOENT;
need_lookup:
- if (atomic)
- return -EWOULDBLOCKIO;
dentry = real_lookup(nd->dentry, name, nd);
if (IS_ERR(dentry))
goto fail;
goto done;
need_revalidate:
- if (atomic)
- return -EWOULDBLOCKIO;
- if (dentry->d_op->d_revalidate(dentry, nd))
- goto done;
- if (d_invalidate(dentry))
- goto done;
- dput(dentry);
- goto need_lookup;
+ dentry = do_revalidate(dentry, nd);
+ if (!dentry)
+ goto need_lookup;
+ if (IS_ERR(dentry))
+ goto fail;
+ goto done;
fail:
return PTR_ERR(dentry);
{
struct path next;
struct inode *inode;
- int err, atomic;
+ int err;
unsigned int lookup_flags = nd->flags;
-
- atomic = (lookup_flags & LOOKUP_ATOMIC);
-
+
while (*name=='/')
name++;
if (!*name)
break;
}
/* This does the actual lookups.. */
- err = do_lookup(nd, &this, &next, atomic);
+ err = do_lookup(nd, &this, &next);
if (err)
break;
if (err < 0)
break;
}
- err = do_lookup(nd, &this, &next, atomic);
+ err = do_lookup(nd, &this, &next);
if (err)
break;
inode = next.dentry->d_inode;
struct vfsmount *old_mnt = nd->mnt;
struct qstr last = nd->last;
int last_type = nd->last_type;
+ struct fs_struct *fs = current->fs;
+
/*
- * NAME was not found in alternate root or it's a directory. Try to find
- * it in the normal root:
+ * NAME was not found in alternate root or it's a directory.
+ * Try to find it in the normal root:
*/
nd->last_type = LAST_ROOT;
- read_lock(¤t->fs->lock);
- nd->mnt = mntget(current->fs->rootmnt);
- nd->dentry = dget(current->fs->root);
- read_unlock(¤t->fs->lock);
+ read_lock(&fs->lock);
+ nd->mnt = mntget(fs->rootmnt);
+ nd->dentry = dget(fs->root);
+ read_unlock(&fs->lock);
if (path_walk(name, nd) == 0) {
if (nd->dentry->d_inode) {
dput(old_dentry);
struct vfsmount *mnt = NULL, *oldmnt;
struct dentry *dentry = NULL, *olddentry;
int err;
+ struct fs_struct *fs = current->fs;
if (!emul)
goto set_it;
dentry = nd.dentry;
}
set_it:
- write_lock(¤t->fs->lock);
- oldmnt = current->fs->altrootmnt;
- olddentry = current->fs->altroot;
- current->fs->altrootmnt = mnt;
- current->fs->altroot = dentry;
- write_unlock(¤t->fs->lock);
+ write_lock(&fs->lock);
+ oldmnt = fs->altrootmnt;
+ olddentry = fs->altroot;
+ fs->altrootmnt = mnt;
+ fs->altroot = dentry;
+ write_unlock(&fs->lock);
if (olddentry) {
dput(olddentry);
mntput(oldmnt);
int retval = 0;
int fput_needed;
struct file *file;
+ struct fs_struct *fs = current->fs;
nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags = flags;
nd->depth = 0;
if (*name=='/') {
- read_lock(¤t->fs->lock);
- if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
- nd->mnt = mntget(current->fs->altrootmnt);
- nd->dentry = dget(current->fs->altroot);
- read_unlock(¤t->fs->lock);
+ read_lock(&fs->lock);
+ if (fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
+ nd->mnt = mntget(fs->altrootmnt);
+ nd->dentry = dget(fs->altroot);
+ read_unlock(&fs->lock);
if (__emul_lookup_dentry(name,nd))
goto out; /* found in altroot */
- read_lock(¤t->fs->lock);
+ read_lock(&fs->lock);
}
- nd->mnt = mntget(current->fs->rootmnt);
- nd->dentry = dget(current->fs->root);
- read_unlock(¤t->fs->lock);
+ nd->mnt = mntget(fs->rootmnt);
+ nd->dentry = dget(fs->root);
+ read_unlock(&fs->lock);
} else if (dfd == AT_FDCWD) {
- read_lock(¤t->fs->lock);
- nd->mnt = mntget(current->fs->pwdmnt);
- nd->dentry = dget(current->fs->pwd);
- read_unlock(¤t->fs->lock);
+ read_lock(&fs->lock);
+ nd->mnt = mntget(fs->pwdmnt);
+ nd->dentry = dget(fs->pwd);
+ read_unlock(&fs->lock);
} else {
struct dentry *dentry;
if (!file)
goto out_fail;
- dentry = file->f_dentry;
+ dentry = file->f_path.dentry;
retval = -ENOTDIR;
if (!S_ISDIR(dentry->d_inode->i_mode))
if (retval)
goto fput_fail;
- nd->mnt = mntget(file->f_vfsmnt);
+ nd->mnt = mntget(file->f_path.mnt);
nd->dentry = dget(dentry);
fput_light(file, fput_needed);
retval = link_path_walk(name, nd);
out:
if (likely(retval == 0)) {
- if (unlikely(current->audit_context && nd && nd->dentry &&
+ if (unlikely(!audit_dummy_context() && nd && nd->dentry &&
nd->dentry->d_inode))
- audit_inode(name, nd->dentry->d_inode, flags);
+ audit_inode(name, nd->dentry->d_inode);
}
out_fail:
return retval;
return -ENOENT;
BUG_ON(victim->d_parent->d_inode != dir);
- audit_inode_child(victim->d_name.name, victim->d_inode, dir->i_ino);
+ audit_inode_child(victim->d_name.name, victim->d_inode, dir);
error = permission(dir,MAY_WRITE | MAY_EXEC, nd);
if (error)
if (f & O_DIRECTORY)
retval |= LOOKUP_DIRECTORY;
- if (f & O_ATOMICLOOKUP)
- retval |= LOOKUP_ATOMIC;
return retval;
}
struct dentry *p;
if (p1 == p2) {
- mutex_lock(&p1->d_inode->i_mutex);
+ mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
return NULL;
}
for (p = p1; p->d_parent != p; p = p->d_parent) {
if (p->d_parent == p2) {
- mutex_lock(&p2->d_inode->i_mutex);
- mutex_lock(&p1->d_inode->i_mutex);
+ mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT);
+ mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD);
return p;
}
}
for (p = p2; p->d_parent != p; p = p->d_parent) {
if (p->d_parent == p1) {
- mutex_lock(&p1->d_inode->i_mutex);
- mutex_lock(&p2->d_inode->i_mutex);
+ mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
+ mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD);
return p;
}
}
- mutex_lock(&p1->d_inode->i_mutex);
- mutex_lock(&p2->d_inode->i_mutex);
+ mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
+ mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD);
return NULL;
}
if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE))
return -EISDIR;
+#ifdef CONFIG_VSERVER_COWBL
+ if (IS_COW(inode) && (flag & FMODE_WRITE)) {
+ if (IS_COW_LINK(inode))
+ return -EMLINK;
+ inode->i_flags &= ~(S_IUNLINK|S_IMMUTABLE);
+ mark_inode_dirty(inode);
+ }
+#endif
error = vfs_permission(nd, acc_mode);
if (error)
return error;
return 0;
}
+static int open_namei_create(struct nameidata *nd, struct path *path,
+ int flag, int mode)
+{
+ int error;
+ struct dentry *dir = nd->dentry;
+
+ if (!IS_POSIXACL(dir->d_inode))
+ mode &= ~current->fs->umask;
+ error = vfs_create(dir->d_inode, path->dentry, mode, nd);
+ mutex_unlock(&dir->d_inode->i_mutex);
+ dput(nd->dentry);
+ nd->dentry = path->dentry;
+ if (error)
+ return error;
+ /* Don't check for write permission, don't truncate */
+ return may_open(nd, 0, flag & ~O_TRUNC);
+}
+
/*
* open_namei()
*
struct dentry *dir;
int count = 0;
+#ifdef CONFIG_VSERVER_COWBL
+ int rflag = flag;
+ int rmode = mode;
+restart:
+#endif
acc_mode = ACC_MODE(flag);
/* O_TRUNC implies we need access checks for write permissions */
/* Negative dentry, just create the file */
if (!path.dentry->d_inode) {
- if (!IS_POSIXACL(dir->d_inode))
- mode &= ~current->fs->umask;
- error = vfs_create(dir->d_inode, path.dentry, mode, nd);
- mutex_unlock(&dir->d_inode->i_mutex);
- dput(nd->dentry);
- nd->dentry = path.dentry;
+ error = open_namei_create(nd, &path, flag, mode);
if (error)
goto exit;
- /* Don't check for write permission, don't truncate */
- acc_mode = 0;
- flag &= ~O_TRUNC;
- goto ok;
+ return 0;
}
/*
* It already exists.
*/
mutex_unlock(&dir->d_inode->i_mutex);
+ audit_inode_update(path.dentry->d_inode);
error = -EEXIST;
if (flag & O_EXCL)
if (flag & O_NOFOLLOW)
goto exit_dput;
}
+
error = -ENOENT;
if (!path.dentry->d_inode)
goto exit_dput;
goto exit;
ok:
error = may_open(nd, acc_mode, flag);
+#ifdef CONFIG_VSERVER_COWBL
+ if (error == -EMLINK) {
+ struct dentry *dentry;
+ dentry = cow_break_link(pathname);
+ if (IS_ERR(dentry)) {
+ error = PTR_ERR(dentry);
+ goto exit;
+ }
+ dput(dentry);
+ release_open_intent(nd);
+ path_release(nd);
+ flag = rflag;
+ mode = rmode;
+ goto restart;
+ }
+#endif
if (error)
goto exit;
return 0;
{
struct dentry *dentry = ERR_PTR(-EEXIST);
- mutex_lock(&nd->dentry->d_inode->i_mutex);
+ mutex_lock_nested(&nd->dentry->d_inode->i_mutex, I_MUTEX_PARENT);
/*
* Yucky last component or no last component at all?
* (foo/., foo/.., /////)
if (nd->last_type != LAST_NORM)
goto fail;
nd->flags &= ~LOOKUP_PARENT;
+ nd->flags |= LOOKUP_CREATE;
+ nd->intent.open.flags = O_EXCL;
/*
* Do the final lookup.
{
int error = 0;
char * tmp;
+ struct dentry *dentry;
+ struct nameidata nd;
tmp = getname(pathname);
error = PTR_ERR(tmp);
- if (!IS_ERR(tmp)) {
- struct dentry *dentry;
- struct nameidata nd;
+ if (IS_ERR(tmp))
+ goto out_err;
- error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
- if (error)
- goto out;
- dentry = lookup_create(&nd, 1);
- error = PTR_ERR(dentry);
- if (!IS_ERR(dentry)) {
- if (!IS_POSIXACL(nd.dentry->d_inode))
- mode &= ~current->fs->umask;
- error = vfs_mkdir(nd.dentry->d_inode, dentry,
- mode, &nd);
- dput(dentry);
- }
- mutex_unlock(&nd.dentry->d_inode->i_mutex);
- path_release(&nd);
-out:
- putname(tmp);
- }
+ error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
+ if (error)
+ goto out;
+ dentry = lookup_create(&nd, 1);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ goto out_unlock;
+ if (!IS_POSIXACL(nd.dentry->d_inode))
+ mode &= ~current->fs->umask;
+ error = vfs_mkdir(nd.dentry->d_inode, dentry, mode, &nd);
+ dput(dentry);
+out_unlock:
+ mutex_unlock(&nd.dentry->d_inode->i_mutex);
+ path_release(&nd);
+out:
+ putname(tmp);
+out_err:
return error;
}
void dentry_unhash(struct dentry *dentry)
{
dget(dentry);
- if (atomic_read(&dentry->d_count))
- shrink_dcache_parent(dentry);
+ shrink_dcache_parent(dentry);
spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
if (atomic_read(&dentry->d_count) == 2)
error = -EBUSY;
goto exit1;
}
- mutex_lock(&nd.dentry->d_inode->i_mutex);
+ mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
dentry = lookup_hash(&nd);
error = PTR_ERR(dentry);
- if (!IS_ERR(dentry)) {
- error = vfs_rmdir(nd.dentry->d_inode, dentry, &nd);
- dput(dentry);
- }
+ if (IS_ERR(dentry))
+ goto exit2;
+ error = vfs_rmdir(nd.dentry->d_inode, dentry, &nd);
+ dput(dentry);
+exit2:
mutex_unlock(&nd.dentry->d_inode->i_mutex);
exit1:
path_release(&nd);
error = -EISDIR;
if (nd.last_type != LAST_NORM)
goto exit1;
- mutex_lock(&nd.dentry->d_inode->i_mutex);
+ mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
dentry = lookup_hash(&nd);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
int error = 0;
char * from;
char * to;
+ struct dentry *dentry;
+ struct nameidata nd;
from = getname(oldname);
if(IS_ERR(from))
return PTR_ERR(from);
to = getname(newname);
error = PTR_ERR(to);
- if (!IS_ERR(to)) {
- struct dentry *dentry;
- struct nameidata nd;
+ if (IS_ERR(to))
+ goto out_putname;
- error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
- if (error)
- goto out;
- dentry = lookup_create(&nd, 0);
- error = PTR_ERR(dentry);
- if (!IS_ERR(dentry)) {
- error = vfs_symlink(nd.dentry->d_inode, dentry,
- from, S_IALLUGO, &nd);
- dput(dentry);
- }
- mutex_unlock(&nd.dentry->d_inode->i_mutex);
- path_release(&nd);
+ error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
+ if (error)
+ goto out;
+ dentry = lookup_create(&nd, 0);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ goto out_unlock;
+
+ error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO, &nd);
+ dput(dentry);
+out_unlock:
+ mutex_unlock(&nd.dentry->d_inode->i_mutex);
+ path_release(&nd);
out:
- putname(to);
- }
+ putname(to);
+out_putname:
putname(from);
return error;
}
int error;
char * to;
- if (flags != 0)
+ if ((flags & ~AT_SYMLINK_FOLLOW) != 0)
return -EINVAL;
to = getname(newname);
if (IS_ERR(to))
return PTR_ERR(to);
- error = __user_walk_fd(olddfd, oldname, 0, &old_nd);
+ error = __user_walk_fd(olddfd, oldname,
+ flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
+ &old_nd);
if (error)
goto exit;
error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
goto out_release;
new_dentry = lookup_create(&nd, 0);
error = PTR_ERR(new_dentry);
- if (!IS_ERR(new_dentry)) {
- error = vfs_link(old_nd.dentry, nd.dentry->d_inode,
- new_dentry, &nd);
- dput(new_dentry);
- }
+ if (IS_ERR(new_dentry))
+ goto out_unlock;
+ error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry, &nd);
+ dput(new_dentry);
+out_unlock:
mutex_unlock(&nd.dentry->d_inode->i_mutex);
out_release:
path_release(&nd);
dput(new_dentry);
}
if (!error)
- d_move(old_dentry,new_dentry);
+ if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
+ d_move(old_dentry,new_dentry);
return error;
}
else
error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
if (!error) {
- /* The following d_move() should become unconditional */
- if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME))
+ if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
d_move(old_dentry, new_dentry);
}
if (target)
return __vfs_follow_link(nd, link);
}
+
+#ifdef CONFIG_VSERVER_COWBL
+
+#include <linux/file.h>
+
+struct dentry *cow_break_link(const char *pathname)
+{
+ int ret, mode, pathlen;
+ struct nameidata old_nd, dir_nd;
+ struct dentry *old_dentry, *new_dentry;
+ struct dentry *res = ERR_PTR(-EMLINK);
+ struct vfsmount *old_mnt, *new_mnt;
+ struct file *old_file;
+ struct file *new_file;
+ char *to, *path, pad='\251';
+ loff_t size;
+
+ vxdprintk(VXD_CBIT(misc, 1), "cow_break_link(»%s«)", pathname);
+ path = kmalloc(PATH_MAX, GFP_KERNEL);
+
+ ret = path_lookup(pathname, LOOKUP_FOLLOW, &old_nd);
+ vxdprintk(VXD_CBIT(misc, 2), "path_lookup(old): %d", ret);
+ old_dentry = old_nd.dentry;
+ old_mnt = old_nd.mnt;
+ mode = old_dentry->d_inode->i_mode;
+
+ to = d_path(old_dentry, old_mnt, path, PATH_MAX-2);
+ pathlen = strlen(to);
+ vxdprintk(VXD_CBIT(misc, 2), "old path »%s«", to);
+
+ to[pathlen+1] = 0;
+retry:
+ to[pathlen] = pad--;
+ if (pad <= '\240')
+ goto out_rel_old;
+
+ vxdprintk(VXD_CBIT(misc, 1), "temp copy »%s«", to);
+ ret = path_lookup(to,
+ LOOKUP_PARENT|LOOKUP_OPEN|LOOKUP_CREATE, &dir_nd);
+
+ /* this puppy downs the inode sem */
+ new_dentry = lookup_create(&dir_nd, 0);
+ vxdprintk(VXD_CBIT(misc, 2),
+ "lookup_create(new): %p", new_dentry);
+ if (!new_dentry) {
+ path_release(&dir_nd);
+ goto retry;
+ }
+
+ ret = vfs_create(dir_nd.dentry->d_inode, new_dentry, mode, &dir_nd);
+ vxdprintk(VXD_CBIT(misc, 2),
+ "vfs_create(new): %d", ret);
+ if (ret == -EEXIST) {
+ mutex_unlock(&dir_nd.dentry->d_inode->i_mutex);
+ dput(new_dentry);
+ path_release(&dir_nd);
+ goto retry;
+ }
+ else if (ret < 0) {
+ res = ERR_PTR(ret);
+ goto out_rel_both;
+ }
+
+ new_mnt = dir_nd.mnt;
+
+ dget(old_dentry);
+ mntget(old_mnt);
+ /* this one cleans up the dentry in case of failure */
+ old_file = dentry_open(old_dentry, old_mnt, O_RDONLY);
+ vxdprintk(VXD_CBIT(misc, 2),
+ "dentry_open(old): %p", old_file);
+ if (!old_file)
+ goto out_rel_both;
+
+ dget(new_dentry);
+ mntget(new_mnt);
+ /* this one cleans up the dentry in case of failure */
+ new_file = dentry_open(new_dentry, new_mnt, O_WRONLY);
+ vxdprintk(VXD_CBIT(misc, 2),
+ "dentry_open(new): %p", new_file);
+ if (!new_file)
+ goto out_fput_old;
+
+ size = i_size_read(old_file->f_dentry->d_inode);
+ ret = vfs_sendfile(new_file, old_file, NULL, size, 0);
+ vxdprintk(VXD_CBIT(misc, 2), "vfs_sendfile: %d", ret);
+
+ if (ret < 0)
+ goto out_fput_both;
+
+ ret = vfs_rename(dir_nd.dentry->d_inode, new_dentry,
+ old_nd.dentry->d_parent->d_inode, old_dentry);
+ vxdprintk(VXD_CBIT(misc, 2), "vfs_rename: %d", ret);
+ if (!ret) {
+ res = new_dentry;
+ dget(new_dentry);
+ }
+
+out_fput_both:
+ vxdprintk(VXD_CBIT(misc, 3),
+ "fput(new_file=%p[#%d])", new_file,
+ atomic_read(&new_file->f_count));
+ fput(new_file);
+
+out_fput_old:
+ vxdprintk(VXD_CBIT(misc, 3),
+ "fput(old_file=%p[#%d])", old_file,
+ atomic_read(&old_file->f_count));
+ fput(old_file);
+
+out_rel_both:
+ mutex_unlock(&dir_nd.dentry->d_inode->i_mutex);
+ dput(new_dentry);
+
+ path_release(&dir_nd);
+out_rel_old:
+ path_release(&old_nd);
+ kfree(path);
+ return res;
+}
+
+#endif
+
/* get the link contents into pagecache */
static char *page_getlink(struct dentry * dentry, struct page **ppage)
{
struct page * page;
struct address_space *mapping = dentry->d_inode->i_mapping;
- page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage,
- NULL);
+ page = read_mapping_page(mapping, 0, NULL);
if (IS_ERR(page))
goto sync_fail;
wait_on_page_locked(page);