2019-05-24 18:04:05 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2021-05-07 09:06:44 +08:00
|
|
|
/*
|
2005-12-16 06:31:24 +08:00
|
|
|
* namei.c
|
|
|
|
*
|
|
|
|
* Create and rename file, directory, symlinks
|
|
|
|
*
|
|
|
|
* Copyright (C) 2002, 2004 Oracle. All rights reserved.
|
|
|
|
*
|
|
|
|
* Portions of this code from linux/fs/ext3/dir.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1992, 1993, 1994, 1995
|
|
|
|
* Remy Card (card@masi.ibp.fr)
|
|
|
|
* Laboratoire MASI - Institut Blaise pascal
|
|
|
|
* Universite Pierre et Marie Curie (Paris VI)
|
|
|
|
*
|
|
|
|
* from
|
|
|
|
*
|
|
|
|
* linux/fs/minix/dir.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1991, 1992 Linux Torvalds
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/highmem.h>
|
2008-10-10 01:38:40 +08:00
|
|
|
#include <linux/quotaops.h>
|
2017-12-11 19:35:17 +08:00
|
|
|
#include <linux/iversion.h>
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
#include <cluster/masklog.h>
|
|
|
|
|
|
|
|
#include "ocfs2.h"
|
|
|
|
|
|
|
|
#include "alloc.h"
|
|
|
|
#include "dcache.h"
|
|
|
|
#include "dir.h"
|
|
|
|
#include "dlmglue.h"
|
|
|
|
#include "extent_map.h"
|
|
|
|
#include "file.h"
|
|
|
|
#include "inode.h"
|
|
|
|
#include "journal.h"
|
|
|
|
#include "namei.h"
|
|
|
|
#include "suballoc.h"
|
2006-04-22 04:49:02 +08:00
|
|
|
#include "super.h"
|
2005-12-16 06:31:24 +08:00
|
|
|
#include "symlink.h"
|
|
|
|
#include "sysfile.h"
|
|
|
|
#include "uptodate.h"
|
2008-08-18 17:11:00 +08:00
|
|
|
#include "xattr.h"
|
2008-11-14 11:17:41 +08:00
|
|
|
#include "acl.h"
|
2011-02-23 22:45:26 +08:00
|
|
|
#include "ocfs2_trace.h"
|
2021-04-07 20:36:44 +08:00
|
|
|
#include "ioctl.h"
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
#include "buffer_head_io.h"
|
|
|
|
|
|
|
|
static int ocfs2_mknod_locked(struct ocfs2_super *osb,
|
|
|
|
struct inode *dir,
|
2008-11-14 11:15:44 +08:00
|
|
|
struct inode *inode,
|
2005-12-16 06:31:24 +08:00
|
|
|
dev_t dev,
|
|
|
|
struct buffer_head **new_fe_bh,
|
|
|
|
struct buffer_head *parent_fe_bh,
|
2006-10-10 09:11:45 +08:00
|
|
|
handle_t *handle,
|
2005-12-16 06:31:24 +08:00
|
|
|
struct ocfs2_alloc_context *inode_ac);
|
|
|
|
|
|
|
|
static int ocfs2_prepare_orphan_dir(struct ocfs2_super *osb,
|
2006-10-06 09:12:57 +08:00
|
|
|
struct inode **ret_orphan_dir,
|
2009-08-18 11:44:10 +08:00
|
|
|
u64 blkno,
|
2005-12-16 06:31:24 +08:00
|
|
|
char *name,
|
2015-02-17 07:59:54 +08:00
|
|
|
struct ocfs2_dir_lookup_result *lookup,
|
|
|
|
bool dio);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
static int ocfs2_orphan_add(struct ocfs2_super *osb,
|
2006-10-10 09:11:45 +08:00
|
|
|
handle_t *handle,
|
2005-12-16 06:31:24 +08:00
|
|
|
struct inode *inode,
|
2010-03-19 09:21:09 +08:00
|
|
|
struct buffer_head *fe_bh,
|
2005-12-16 06:31:24 +08:00
|
|
|
char *name,
|
2008-11-13 07:43:34 +08:00
|
|
|
struct ocfs2_dir_lookup_result *lookup,
|
2015-02-17 07:59:54 +08:00
|
|
|
struct inode *orphan_dir_inode,
|
|
|
|
bool dio);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
static int ocfs2_create_symlink_data(struct ocfs2_super *osb,
|
2006-10-10 09:11:45 +08:00
|
|
|
handle_t *handle,
|
2005-12-16 06:31:24 +08:00
|
|
|
struct inode *inode,
|
|
|
|
const char *symname);
|
|
|
|
|
2015-01-09 06:32:23 +08:00
|
|
|
static int ocfs2_double_lock(struct ocfs2_super *osb,
|
|
|
|
struct buffer_head **bh1,
|
|
|
|
struct inode *inode1,
|
|
|
|
struct buffer_head **bh2,
|
|
|
|
struct inode *inode2,
|
|
|
|
int rename);
|
|
|
|
|
|
|
|
static void ocfs2_double_unlock(struct inode *inode1, struct inode *inode2);
|
2005-12-16 06:31:24 +08:00
|
|
|
/* An orphan dir name is an 8 byte value, printed as a hex string */
|
|
|
|
#define OCFS2_ORPHAN_NAMELEN ((int)(2 * sizeof(u64)))
|
|
|
|
|
|
|
|
static struct dentry *ocfs2_lookup(struct inode *dir, struct dentry *dentry,
|
2012-06-11 05:13:09 +08:00
|
|
|
unsigned int flags)
|
2005-12-16 06:31:24 +08:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
u64 blkno;
|
|
|
|
struct inode *inode = NULL;
|
|
|
|
struct dentry *ret;
|
|
|
|
struct ocfs2_inode_info *oi;
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_lookup(dir, dentry, dentry->d_name.len,
|
|
|
|
dentry->d_name.name,
|
|
|
|
(unsigned long long)OCFS2_I(dir)->ip_blkno, 0);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
if (dentry->d_name.len > OCFS2_MAX_FILENAME_LEN) {
|
|
|
|
ret = ERR_PTR(-ENAMETOOLONG);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2009-06-04 21:26:50 +08:00
|
|
|
status = ocfs2_inode_lock_nested(dir, NULL, 0, OI_LS_PARENT);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOENT)
|
|
|
|
mlog_errno(status);
|
|
|
|
ret = ERR_PTR(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2007-09-12 06:22:06 +08:00
|
|
|
status = ocfs2_lookup_ino_from_name(dir, dentry->d_name.name,
|
|
|
|
dentry->d_name.len, &blkno);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0)
|
|
|
|
goto bail_add;
|
|
|
|
|
2008-01-11 07:11:45 +08:00
|
|
|
inode = ocfs2_iget(OCFS2_SB(dir->i_sb), blkno, 0, 0);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (IS_ERR(inode)) {
|
|
|
|
ret = ERR_PTR(-EACCES);
|
|
|
|
goto bail_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
oi = OCFS2_I(inode);
|
|
|
|
/* Clear any orphaned state... If we were able to look up the
|
|
|
|
* inode from a directory, it certainly can't be orphaned. We
|
|
|
|
* might have the bad state from a node which intended to
|
|
|
|
* orphan this inode but crashed before it could commit the
|
|
|
|
* unlink. */
|
|
|
|
spin_lock(&oi->ip_lock);
|
|
|
|
oi->ip_flags &= ~OCFS2_INODE_MAYBE_ORPHANED;
|
|
|
|
spin_unlock(&oi->ip_lock);
|
|
|
|
|
|
|
|
bail_add:
|
|
|
|
ret = d_splice_alias(inode, dentry);
|
|
|
|
|
2006-09-09 05:21:03 +08:00
|
|
|
if (inode) {
|
|
|
|
/*
|
|
|
|
* If d_splice_alias() finds a DCACHE_DISCONNECTED
|
|
|
|
* dentry, it will d_move() it on top of ourse. The
|
|
|
|
* return value will indicate this however, so in
|
|
|
|
* those cases, we switch them around for the locking
|
|
|
|
* code.
|
|
|
|
*
|
|
|
|
* NOTE: This dentry already has ->d_op set from
|
|
|
|
* ocfs2_get_parent() and ocfs2_get_dentry()
|
|
|
|
*/
|
2014-10-30 05:50:53 +08:00
|
|
|
if (!IS_ERR_OR_NULL(ret))
|
2006-09-09 05:21:03 +08:00
|
|
|
dentry = ret;
|
|
|
|
|
|
|
|
status = ocfs2_dentry_attach_lock(dentry, inode,
|
2006-09-22 07:51:28 +08:00
|
|
|
OCFS2_I(dir)->ip_blkno);
|
2006-09-09 05:21:03 +08:00
|
|
|
if (status) {
|
|
|
|
mlog_errno(status);
|
|
|
|
ret = ERR_PTR(status);
|
|
|
|
goto bail_unlock;
|
|
|
|
}
|
2010-06-28 23:04:32 +08:00
|
|
|
} else
|
|
|
|
ocfs2_dentry_attach_gen(dentry);
|
2006-09-09 05:21:03 +08:00
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
bail_unlock:
|
|
|
|
/* Don't drop the cluster lock until *after* the d_add --
|
|
|
|
* unlink on another node will message us to remove that
|
|
|
|
* dentry under this lock so otherwise we can race this with
|
2007-09-25 06:56:19 +08:00
|
|
|
* the downconvert thread and have a stale dentry. */
|
2007-10-19 06:30:42 +08:00
|
|
|
ocfs2_inode_unlock(dir, 0);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
bail:
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_lookup_ret(ret);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-07-26 14:55:32 +08:00
|
|
|
static struct inode *ocfs2_get_init_inode(struct inode *dir, umode_t mode)
|
2008-11-14 11:15:44 +08:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
2015-07-14 19:36:02 +08:00
|
|
|
int status;
|
2008-11-14 11:15:44 +08:00
|
|
|
|
|
|
|
inode = new_inode(dir->i_sb);
|
|
|
|
if (!inode) {
|
|
|
|
mlog(ML_ERROR, "new_inode failed!\n");
|
2015-07-14 19:36:02 +08:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2008-11-14 11:15:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* populate as many fields early on as possible - many of
|
|
|
|
* these are used by the support functions here and in
|
|
|
|
* callers. */
|
|
|
|
if (S_ISDIR(mode))
|
2011-10-28 20:13:29 +08:00
|
|
|
set_nlink(inode, 2);
|
fs: move S_ISGID stripping into the vfs_*() helpers
Move setgid handling out of individual filesystems and into the VFS
itself to stop the proliferation of setgid inheritance bugs.
Creating files that have both the S_IXGRP and S_ISGID bit raised in
directories that themselves have the S_ISGID bit set requires additional
privileges to avoid security issues.
When a filesystem creates a new inode it needs to take care that the
caller is either in the group of the newly created inode or they have
CAP_FSETID in their current user namespace and are privileged over the
parent directory of the new inode. If any of these two conditions is
true then the S_ISGID bit can be raised for an S_IXGRP file and if not
it needs to be stripped.
However, there are several key issues with the current implementation:
* S_ISGID stripping logic is entangled with umask stripping.
If a filesystem doesn't support or enable POSIX ACLs then umask
stripping is done directly in the vfs before calling into the
filesystem.
If the filesystem does support POSIX ACLs then unmask stripping may be
done in the filesystem itself when calling posix_acl_create().
Since umask stripping has an effect on S_ISGID inheritance, e.g., by
stripping the S_IXGRP bit from the file to be created and all relevant
filesystems have to call posix_acl_create() before inode_init_owner()
where we currently take care of S_ISGID handling S_ISGID handling is
order dependent. IOW, whether or not you get a setgid bit depends on
POSIX ACLs and umask and in what order they are called.
Note that technically filesystems are free to impose their own
ordering between posix_acl_create() and inode_init_owner() meaning
that there's additional ordering issues that influence S_SIGID
inheritance.
* Filesystems that don't rely on inode_init_owner() don't get S_ISGID
stripping logic.
While that may be intentional (e.g. network filesystems might just
defer setgid stripping to a server) it is often just a security issue.
This is not just ugly it's unsustainably messy especially since we do
still have bugs in this area years after the initial round of setgid
bugfixes.
So the current state is quite messy and while we won't be able to make
it completely clean as posix_acl_create() is still a filesystem specific
call we can improve the S_SIGD stripping situation quite a bit by
hoisting it out of inode_init_owner() and into the vfs creation
operations. This means we alleviate the burden for filesystems to handle
S_ISGID stripping correctly and can standardize the ordering between
S_ISGID and umask stripping in the vfs.
We add a new helper vfs_prepare_mode() so S_ISGID handling is now done
in the VFS before umask handling. This has S_ISGID handling is
unaffected unaffected by whether umask stripping is done by the VFS
itself (if no POSIX ACLs are supported or enabled) or in the filesystem
in posix_acl_create() (if POSIX ACLs are supported).
The vfs_prepare_mode() helper is called directly in vfs_*() helpers that
create new filesystem objects. We need to move them into there to make
sure that filesystems like overlayfs hat have callchains like:
sys_mknod()
-> do_mknodat(mode)
-> .mknod = ovl_mknod(mode)
-> ovl_create(mode)
-> vfs_mknod(mode)
get S_ISGID stripping done when calling into lower filesystems via
vfs_*() creation helpers. Moving vfs_prepare_mode() into e.g.
vfs_mknod() takes care of that. This is in any case semantically cleaner
because S_ISGID stripping is VFS security requirement.
Security hooks so far have seen the mode with the umask applied but
without S_ISGID handling done. The relevant hooks are called outside of
vfs_*() creation helpers so by calling vfs_prepare_mode() from vfs_*()
helpers the security hooks would now see the mode without umask
stripping applied. For now we fix this by passing the mode with umask
settings applied to not risk any regressions for LSM hooks. IOW, nothing
changes for LSM hooks. It is worth pointing out that security hooks
never saw the mode that is seen by the filesystem when actually creating
the file. They have always been completely misplaced for that to work.
The following filesystems use inode_init_owner() and thus relied on
S_ISGID stripping: spufs, 9p, bfs, btrfs, ext2, ext4, f2fs, hfsplus,
hugetlbfs, jfs, minix, nilfs2, ntfs3, ocfs2, omfs, overlayfs, ramfs,
reiserfs, sysv, ubifs, udf, ufs, xfs, zonefs, bpf, tmpfs.
All of the above filesystems end up calling inode_init_owner() when new
filesystem objects are created through the ->mkdir(), ->mknod(),
->create(), ->tmpfile(), ->rename() inode operations.
Since directories always inherit the S_ISGID bit with the exception of
xfs when irix_sgid_inherit mode is turned on S_ISGID stripping doesn't
apply. The ->symlink() and ->link() inode operations trivially inherit
the mode from the target and the ->rename() inode operation inherits the
mode from the source inode. All other creation inode operations will get
S_ISGID handling via vfs_prepare_mode() when called from their relevant
vfs_*() helpers.
In addition to this there are filesystems which allow the creation of
filesystem objects through ioctl()s or - in the case of spufs -
circumventing the vfs in other ways. If filesystem objects are created
through ioctl()s the vfs doesn't know about it and can't apply regular
permission checking including S_ISGID logic. Therfore, a filesystem
relying on S_ISGID stripping in inode_init_owner() in their ioctl()
callpath will be affected by moving this logic into the vfs. We audited
those filesystems:
* btrfs allows the creation of filesystem objects through various
ioctls(). Snapshot creation literally takes a snapshot and so the mode
is fully preserved and S_ISGID stripping doesn't apply.
Creating a new subvolum relies on inode_init_owner() in
btrfs_new_subvol_inode() but only creates directories and doesn't
raise S_ISGID.
* ocfs2 has a peculiar implementation of reflinks. In contrast to e.g.
xfs and btrfs FICLONE/FICLONERANGE ioctl() that is only concerned with
the actual extents ocfs2 uses a separate ioctl() that also creates the
target file.
Iow, ocfs2 circumvents the vfs entirely here and did indeed rely on
inode_init_owner() to strip the S_ISGID bit. This is the only place
where a filesystem needs to call mode_strip_sgid() directly but this
is self-inflicted pain.
* spufs doesn't go through the vfs at all and doesn't use ioctl()s
either. Instead it has a dedicated system call spufs_create() which
allows the creation of filesystem objects. But spufs only creates
directories and doesn't allo S_SIGID bits, i.e. it specifically only
allows 0777 bits.
* bpf uses vfs_mkobj() but also doesn't allow S_ISGID bits to be created.
The patch will have an effect on ext2 when the EXT2_MOUNT_GRPID mount
option is used, on ext4 when the EXT4_MOUNT_GRPID mount option is used,
and on xfs when the XFS_FEAT_GRPID mount option is used. When any of
these filesystems are mounted with their respective GRPID option then
newly created files inherit the parent directories group
unconditionally. In these cases non of the filesystems call
inode_init_owner() and thus did never strip the S_ISGID bit for newly
created files. Moving this logic into the VFS means that they now get
the S_ISGID bit stripped. This is a user visible change. If this leads
to regressions we will either need to figure out a better way or we need
to revert. However, given the various setgid bugs that we found just in
the last two years this is a regression risk we should take.
Associated with this change is a new set of fstests to enforce the
semantics for all new filesystems.
Link: https://lore.kernel.org/ceph-devel/20220427092201.wvsdjbnc7b4dttaw@wittgenstein [1]
Link: e014f37db1a2 ("xfs: use setattr_copy to set vfs inode attributes") [2]
Link: 01ea173e103e ("xfs: fix up non-directory creation in SGID directories") [3]
Link: fd84bfdddd16 ("ceph: fix up non-directory creation in SGID directories") [4]
Link: https://lore.kernel.org/r/1657779088-2242-3-git-send-email-xuyang2018.jy@fujitsu.com
Suggested-by: Dave Chinner <david@fromorbit.com>
Suggested-by: Christian Brauner (Microsoft) <brauner@kernel.org>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-and-Tested-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Yang Xu <xuyang2018.jy@fujitsu.com>
[<brauner@kernel.org>: rewrote commit message]
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
2022-07-14 14:11:27 +08:00
|
|
|
mode = mode_strip_sgid(&init_user_ns, dir, mode);
|
2021-01-21 21:19:25 +08:00
|
|
|
inode_init_owner(&init_user_ns, inode, dir, mode);
|
2015-07-14 19:36:02 +08:00
|
|
|
status = dquot_initialize(inode);
|
|
|
|
if (status)
|
|
|
|
return ERR_PTR(status);
|
|
|
|
|
2008-11-14 11:15:44 +08:00
|
|
|
return inode;
|
|
|
|
}
|
|
|
|
|
2014-06-24 04:22:09 +08:00
|
|
|
static void ocfs2_cleanup_add_entry_failure(struct ocfs2_super *osb,
|
|
|
|
struct dentry *dentry, struct inode *inode)
|
|
|
|
{
|
|
|
|
struct ocfs2_dentry_lock *dl = dentry->d_fsdata;
|
|
|
|
|
|
|
|
ocfs2_simple_drop_lockres(osb, &dl->dl_lockres);
|
|
|
|
ocfs2_lock_res_free(&dl->dl_lockres);
|
|
|
|
BUG_ON(dl->dl_count != 1);
|
|
|
|
spin_lock(&dentry_attach_lock);
|
|
|
|
dentry->d_fsdata = NULL;
|
|
|
|
spin_unlock(&dentry_attach_lock);
|
|
|
|
kfree(dl);
|
|
|
|
iput(inode);
|
|
|
|
}
|
|
|
|
|
2021-01-21 21:19:43 +08:00
|
|
|
static int ocfs2_mknod(struct user_namespace *mnt_userns,
|
|
|
|
struct inode *dir,
|
2005-12-16 06:31:24 +08:00
|
|
|
struct dentry *dentry,
|
2011-07-26 13:52:52 +08:00
|
|
|
umode_t mode,
|
2005-12-16 06:31:24 +08:00
|
|
|
dev_t dev)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
struct buffer_head *parent_fe_bh = NULL;
|
2006-10-10 09:11:45 +08:00
|
|
|
handle_t *handle = NULL;
|
2005-12-16 06:31:24 +08:00
|
|
|
struct ocfs2_super *osb;
|
|
|
|
struct ocfs2_dinode *dirfe;
|
2022-10-17 21:02:27 +08:00
|
|
|
struct ocfs2_dinode *fe = NULL;
|
2005-12-16 06:31:24 +08:00
|
|
|
struct buffer_head *new_fe_bh = NULL;
|
|
|
|
struct inode *inode = NULL;
|
|
|
|
struct ocfs2_alloc_context *inode_ac = NULL;
|
|
|
|
struct ocfs2_alloc_context *data_ac = NULL;
|
2008-11-13 08:27:44 +08:00
|
|
|
struct ocfs2_alloc_context *meta_ac = NULL;
|
2008-11-14 11:16:41 +08:00
|
|
|
int want_clusters = 0;
|
2008-11-13 08:27:44 +08:00
|
|
|
int want_meta = 0;
|
2008-11-14 11:16:41 +08:00
|
|
|
int xattr_credits = 0;
|
|
|
|
struct ocfs2_security_xattr_info si = {
|
|
|
|
.enable = 1,
|
|
|
|
};
|
2008-10-10 01:38:40 +08:00
|
|
|
int did_quota_inode = 0;
|
2008-11-13 07:43:34 +08:00
|
|
|
struct ocfs2_dir_lookup_result lookup = { NULL, };
|
2010-05-11 02:56:52 +08:00
|
|
|
sigset_t oldset;
|
|
|
|
int did_block_signals = 0;
|
2014-06-24 04:22:09 +08:00
|
|
|
struct ocfs2_dentry_lock *dl = NULL;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_mknod(dir, dentry, dentry->d_name.len, dentry->d_name.name,
|
|
|
|
(unsigned long long)OCFS2_I(dir)->ip_blkno,
|
|
|
|
(unsigned long)dev, mode);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2015-07-14 19:36:02 +08:00
|
|
|
status = dquot_initialize(dir);
|
|
|
|
if (status) {
|
|
|
|
mlog_errno(status);
|
|
|
|
return status;
|
|
|
|
}
|
2010-03-03 22:05:06 +08:00
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
/* get our super block */
|
|
|
|
osb = OCFS2_SB(dir->i_sb);
|
|
|
|
|
2007-10-19 06:30:42 +08:00
|
|
|
status = ocfs2_inode_lock(dir, &parent_fe_bh, 1);
|
2006-10-06 07:04:17 +08:00
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOENT)
|
|
|
|
mlog_errno(status);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2008-11-21 09:54:57 +08:00
|
|
|
if (S_ISDIR(mode) && (dir->i_nlink >= ocfs2_link_max(osb))) {
|
2006-08-10 02:45:07 +08:00
|
|
|
status = -EMLINK;
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
dirfe = (struct ocfs2_dinode *) parent_fe_bh->b_data;
|
2008-11-21 09:54:57 +08:00
|
|
|
if (!ocfs2_read_links_count(dirfe)) {
|
2005-12-16 06:31:24 +08:00
|
|
|
/* can't make a file in a deleted directory. */
|
|
|
|
status = -ENOENT;
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_check_dir_for_entry(dir, dentry->d_name.name,
|
|
|
|
dentry->d_name.len);
|
|
|
|
if (status)
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
/* get a spot inside the dir. */
|
|
|
|
status = ocfs2_prepare_dir_for_insert(osb, dir, parent_fe_bh,
|
|
|
|
dentry->d_name.name,
|
2008-11-13 07:43:34 +08:00
|
|
|
dentry->d_name.len, &lookup);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reserve an inode spot */
|
2006-10-07 09:34:35 +08:00
|
|
|
status = ocfs2_reserve_new_inode(osb, &inode_ac);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOSPC)
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2008-11-14 11:15:44 +08:00
|
|
|
inode = ocfs2_get_init_inode(dir, mode);
|
2015-07-14 19:36:02 +08:00
|
|
|
if (IS_ERR(inode)) {
|
|
|
|
status = PTR_ERR(inode);
|
|
|
|
inode = NULL;
|
2008-11-14 11:15:44 +08:00
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2008-11-14 11:16:41 +08:00
|
|
|
/* get security xattr */
|
2011-02-02 00:05:39 +08:00
|
|
|
status = ocfs2_init_security_get(inode, dir, &dentry->d_name, &si);
|
2008-11-14 11:16:41 +08:00
|
|
|
if (status) {
|
|
|
|
if (status == -EOPNOTSUPP)
|
|
|
|
si.enable = 0;
|
|
|
|
else {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-14 11:17:41 +08:00
|
|
|
/* calculate meta data/clusters for setting security and acl xattr */
|
|
|
|
status = ocfs2_calc_xattr_init(dir, parent_fe_bh, mode,
|
2008-11-13 08:27:44 +08:00
|
|
|
&si, &want_clusters,
|
|
|
|
&xattr_credits, &want_meta);
|
2008-11-14 11:17:41 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2008-11-14 11:16:41 +08:00
|
|
|
/* Reserve a cluster if creating an extent based directory. */
|
2008-11-13 08:27:44 +08:00
|
|
|
if (S_ISDIR(mode) && !ocfs2_supports_inline_data(osb)) {
|
2008-11-14 11:16:41 +08:00
|
|
|
want_clusters += 1;
|
|
|
|
|
2008-11-13 08:27:44 +08:00
|
|
|
/* Dir indexing requires extra space as well */
|
2008-11-25 09:02:08 +08:00
|
|
|
if (ocfs2_supports_indexed_dirs(osb))
|
2008-11-13 08:27:44 +08:00
|
|
|
want_meta++;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_reserve_new_metadata_blocks(osb, want_meta, &meta_ac);
|
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOSPC)
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2008-11-14 11:16:41 +08:00
|
|
|
status = ocfs2_reserve_clusters(osb, want_clusters, &data_ac);
|
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOSPC)
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2008-11-13 08:27:44 +08:00
|
|
|
handle = ocfs2_start_trans(osb, ocfs2_mknod_credits(osb->sb,
|
|
|
|
S_ISDIR(mode),
|
|
|
|
xattr_credits));
|
2005-12-16 06:31:24 +08:00
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
status = PTR_ERR(handle);
|
|
|
|
handle = NULL;
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2010-05-11 02:56:52 +08:00
|
|
|
/* Starting to change things, restart is no longer possible. */
|
|
|
|
ocfs2_block_signals(&oldset);
|
|
|
|
did_block_signals = 1;
|
|
|
|
|
2010-03-03 22:05:01 +08:00
|
|
|
status = dquot_alloc_inode(inode);
|
|
|
|
if (status)
|
2008-10-10 01:38:40 +08:00
|
|
|
goto leave;
|
|
|
|
did_quota_inode = 1;
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
/* do the real work now. */
|
2009-08-18 11:44:10 +08:00
|
|
|
status = ocfs2_mknod_locked(osb, dir, inode, dev,
|
2005-12-16 06:31:24 +08:00
|
|
|
&new_fe_bh, parent_fe_bh, handle,
|
2008-11-14 11:15:44 +08:00
|
|
|
inode_ac);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2022-10-17 21:02:27 +08:00
|
|
|
fe = (struct ocfs2_dinode *) new_fe_bh->b_data;
|
2005-12-16 06:31:24 +08:00
|
|
|
if (S_ISDIR(mode)) {
|
|
|
|
status = ocfs2_fill_new_dir(osb, handle, dir, inode,
|
2008-11-13 08:27:44 +08:00
|
|
|
new_fe_bh, data_ac, meta_ac);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2009-02-13 08:41:25 +08:00
|
|
|
status = ocfs2_journal_access_di(handle, INODE_CACHE(dir),
|
|
|
|
parent_fe_bh,
|
2008-10-18 10:25:01 +08:00
|
|
|
OCFS2_JOURNAL_ACCESS_WRITE);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
2008-11-21 09:54:57 +08:00
|
|
|
ocfs2_add_links_count(dirfe, 1);
|
2010-03-20 05:13:52 +08:00
|
|
|
ocfs2_journal_dirty(handle, parent_fe_bh);
|
2006-10-01 14:29:04 +08:00
|
|
|
inc_nlink(dir);
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2016-05-13 06:42:18 +08:00
|
|
|
status = ocfs2_init_acl(handle, inode, dir, new_fe_bh, parent_fe_bh,
|
|
|
|
meta_ac, data_ac);
|
2013-12-20 21:16:48 +08:00
|
|
|
|
2008-11-14 11:17:41 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
ocfs2: roll back the reference count modification of the parent directory if an error occurs
Under some conditions, the directory cannot be deleted. The specific
scenarios are as follows: (for example, /mnt/ocfs2 is the mount point)
1. Create the /mnt/ocfs2/p_dir directory. At this time, the i_nlink
corresponding to the inode of the /mnt/ocfs2/p_dir directory is equal
to 2.
2. During the process of creating the /mnt/ocfs2/p_dir/s_dir
directory, if the call to the inc_nlink function in ocfs2_mknod
succeeds, the functions such as ocfs2_init_acl,
ocfs2_init_security_set, and ocfs2_dentry_attach_lock fail. At this
time, the i_nlink corresponding to the inode of the /mnt/ocfs2/p_dir
directory is equal to 3, but /mnt/ocfs2/p_dir/s_dir is not added to the
/mnt/ocfs2/p_dir directory entry.
3. Delete the /mnt/ocfs2/p_dir directory (rm -rf /mnt/ocfs2/p_dir).
At this time, it is found that the i_nlink corresponding to the inode
corresponding to the /mnt/ocfs2/p_dir directory is equal to 3.
Therefore, the /mnt/ocfs2/p_dir directory cannot be deleted.
Signed-off-by: Jian wang <wangjian161@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Jun Piao <piaojun@huawei.com>
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: Mark Fasheh <mark@fasheh.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Changwei Ge <gechangwei@live.cn>
Cc: Gang He <ghe@suse.com>
Cc: Jun Piao <piaojun@huawei.com>
Link: http://lkml.kernel.org/r/a44f6666-bbc4-405e-0e6c-0f4e922eeef6@huawei.com
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2020-04-02 12:04:02 +08:00
|
|
|
goto roll_back;
|
2008-11-14 11:17:41 +08:00
|
|
|
}
|
|
|
|
|
2008-11-14 11:16:41 +08:00
|
|
|
if (si.enable) {
|
|
|
|
status = ocfs2_init_security_set(handle, inode, new_fe_bh, &si,
|
2008-11-13 08:27:44 +08:00
|
|
|
meta_ac, data_ac);
|
2008-11-14 11:16:41 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
ocfs2: roll back the reference count modification of the parent directory if an error occurs
Under some conditions, the directory cannot be deleted. The specific
scenarios are as follows: (for example, /mnt/ocfs2 is the mount point)
1. Create the /mnt/ocfs2/p_dir directory. At this time, the i_nlink
corresponding to the inode of the /mnt/ocfs2/p_dir directory is equal
to 2.
2. During the process of creating the /mnt/ocfs2/p_dir/s_dir
directory, if the call to the inc_nlink function in ocfs2_mknod
succeeds, the functions such as ocfs2_init_acl,
ocfs2_init_security_set, and ocfs2_dentry_attach_lock fail. At this
time, the i_nlink corresponding to the inode of the /mnt/ocfs2/p_dir
directory is equal to 3, but /mnt/ocfs2/p_dir/s_dir is not added to the
/mnt/ocfs2/p_dir directory entry.
3. Delete the /mnt/ocfs2/p_dir directory (rm -rf /mnt/ocfs2/p_dir).
At this time, it is found that the i_nlink corresponding to the inode
corresponding to the /mnt/ocfs2/p_dir directory is equal to 3.
Therefore, the /mnt/ocfs2/p_dir directory cannot be deleted.
Signed-off-by: Jian wang <wangjian161@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Jun Piao <piaojun@huawei.com>
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: Mark Fasheh <mark@fasheh.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Changwei Ge <gechangwei@live.cn>
Cc: Gang He <ghe@suse.com>
Cc: Jun Piao <piaojun@huawei.com>
Link: http://lkml.kernel.org/r/a44f6666-bbc4-405e-0e6c-0f4e922eeef6@huawei.com
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2020-04-02 12:04:02 +08:00
|
|
|
goto roll_back;
|
2008-11-14 11:16:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-24 02:42:22 +08:00
|
|
|
/*
|
|
|
|
* Do this before adding the entry to the directory. We add
|
|
|
|
* also set d_op after success so that ->d_iput() will cleanup
|
|
|
|
* the dentry lock even if ocfs2_add_entry() fails below.
|
|
|
|
*/
|
|
|
|
status = ocfs2_dentry_attach_lock(dentry, inode,
|
|
|
|
OCFS2_I(dir)->ip_blkno);
|
|
|
|
if (status) {
|
2005-12-16 06:31:24 +08:00
|
|
|
mlog_errno(status);
|
ocfs2: roll back the reference count modification of the parent directory if an error occurs
Under some conditions, the directory cannot be deleted. The specific
scenarios are as follows: (for example, /mnt/ocfs2 is the mount point)
1. Create the /mnt/ocfs2/p_dir directory. At this time, the i_nlink
corresponding to the inode of the /mnt/ocfs2/p_dir directory is equal
to 2.
2. During the process of creating the /mnt/ocfs2/p_dir/s_dir
directory, if the call to the inc_nlink function in ocfs2_mknod
succeeds, the functions such as ocfs2_init_acl,
ocfs2_init_security_set, and ocfs2_dentry_attach_lock fail. At this
time, the i_nlink corresponding to the inode of the /mnt/ocfs2/p_dir
directory is equal to 3, but /mnt/ocfs2/p_dir/s_dir is not added to the
/mnt/ocfs2/p_dir directory entry.
3. Delete the /mnt/ocfs2/p_dir directory (rm -rf /mnt/ocfs2/p_dir).
At this time, it is found that the i_nlink corresponding to the inode
corresponding to the /mnt/ocfs2/p_dir directory is equal to 3.
Therefore, the /mnt/ocfs2/p_dir directory cannot be deleted.
Signed-off-by: Jian wang <wangjian161@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Jun Piao <piaojun@huawei.com>
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: Mark Fasheh <mark@fasheh.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Changwei Ge <gechangwei@live.cn>
Cc: Gang He <ghe@suse.com>
Cc: Jun Piao <piaojun@huawei.com>
Link: http://lkml.kernel.org/r/a44f6666-bbc4-405e-0e6c-0f4e922eeef6@huawei.com
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2020-04-02 12:04:02 +08:00
|
|
|
goto roll_back;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2014-06-24 04:22:09 +08:00
|
|
|
dl = dentry->d_fsdata;
|
|
|
|
|
2010-04-24 02:42:22 +08:00
|
|
|
status = ocfs2_add_entry(handle, dentry, inode,
|
|
|
|
OCFS2_I(inode)->ip_blkno, parent_fe_bh,
|
|
|
|
&lookup);
|
|
|
|
if (status < 0) {
|
2006-09-09 05:21:03 +08:00
|
|
|
mlog_errno(status);
|
ocfs2: roll back the reference count modification of the parent directory if an error occurs
Under some conditions, the directory cannot be deleted. The specific
scenarios are as follows: (for example, /mnt/ocfs2 is the mount point)
1. Create the /mnt/ocfs2/p_dir directory. At this time, the i_nlink
corresponding to the inode of the /mnt/ocfs2/p_dir directory is equal
to 2.
2. During the process of creating the /mnt/ocfs2/p_dir/s_dir
directory, if the call to the inc_nlink function in ocfs2_mknod
succeeds, the functions such as ocfs2_init_acl,
ocfs2_init_security_set, and ocfs2_dentry_attach_lock fail. At this
time, the i_nlink corresponding to the inode of the /mnt/ocfs2/p_dir
directory is equal to 3, but /mnt/ocfs2/p_dir/s_dir is not added to the
/mnt/ocfs2/p_dir directory entry.
3. Delete the /mnt/ocfs2/p_dir directory (rm -rf /mnt/ocfs2/p_dir).
At this time, it is found that the i_nlink corresponding to the inode
corresponding to the /mnt/ocfs2/p_dir directory is equal to 3.
Therefore, the /mnt/ocfs2/p_dir directory cannot be deleted.
Signed-off-by: Jian wang <wangjian161@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Jun Piao <piaojun@huawei.com>
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: Mark Fasheh <mark@fasheh.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Changwei Ge <gechangwei@live.cn>
Cc: Gang He <ghe@suse.com>
Cc: Jun Piao <piaojun@huawei.com>
Link: http://lkml.kernel.org/r/a44f6666-bbc4-405e-0e6c-0f4e922eeef6@huawei.com
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2020-04-02 12:04:02 +08:00
|
|
|
goto roll_back;
|
2006-09-09 05:21:03 +08:00
|
|
|
}
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
insert_inode_hash(inode);
|
|
|
|
d_instantiate(dentry, inode);
|
|
|
|
status = 0;
|
ocfs2: roll back the reference count modification of the parent directory if an error occurs
Under some conditions, the directory cannot be deleted. The specific
scenarios are as follows: (for example, /mnt/ocfs2 is the mount point)
1. Create the /mnt/ocfs2/p_dir directory. At this time, the i_nlink
corresponding to the inode of the /mnt/ocfs2/p_dir directory is equal
to 2.
2. During the process of creating the /mnt/ocfs2/p_dir/s_dir
directory, if the call to the inc_nlink function in ocfs2_mknod
succeeds, the functions such as ocfs2_init_acl,
ocfs2_init_security_set, and ocfs2_dentry_attach_lock fail. At this
time, the i_nlink corresponding to the inode of the /mnt/ocfs2/p_dir
directory is equal to 3, but /mnt/ocfs2/p_dir/s_dir is not added to the
/mnt/ocfs2/p_dir directory entry.
3. Delete the /mnt/ocfs2/p_dir directory (rm -rf /mnt/ocfs2/p_dir).
At this time, it is found that the i_nlink corresponding to the inode
corresponding to the /mnt/ocfs2/p_dir directory is equal to 3.
Therefore, the /mnt/ocfs2/p_dir directory cannot be deleted.
Signed-off-by: Jian wang <wangjian161@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Jun Piao <piaojun@huawei.com>
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: Mark Fasheh <mark@fasheh.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Changwei Ge <gechangwei@live.cn>
Cc: Gang He <ghe@suse.com>
Cc: Jun Piao <piaojun@huawei.com>
Link: http://lkml.kernel.org/r/a44f6666-bbc4-405e-0e6c-0f4e922eeef6@huawei.com
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2020-04-02 12:04:02 +08:00
|
|
|
|
|
|
|
roll_back:
|
|
|
|
if (status < 0 && S_ISDIR(mode)) {
|
|
|
|
ocfs2_add_links_count(dirfe, -1);
|
|
|
|
drop_nlink(dir);
|
|
|
|
}
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
leave:
|
2008-10-10 01:38:40 +08:00
|
|
|
if (status < 0 && did_quota_inode)
|
2010-03-03 22:05:01 +08:00
|
|
|
dquot_free_inode(inode);
|
2022-10-17 21:02:27 +08:00
|
|
|
if (handle) {
|
|
|
|
if (status < 0 && fe)
|
|
|
|
ocfs2_set_links_count(fe, 0);
|
2006-10-10 07:48:10 +08:00
|
|
|
ocfs2_commit_trans(osb, handle);
|
2022-10-17 21:02:27 +08:00
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2007-10-19 06:30:42 +08:00
|
|
|
ocfs2_inode_unlock(dir, 1);
|
2010-05-11 02:56:52 +08:00
|
|
|
if (did_block_signals)
|
|
|
|
ocfs2_unblock_signals(&oldset);
|
2006-10-06 07:04:17 +08:00
|
|
|
|
2008-10-08 05:25:16 +08:00
|
|
|
brelse(new_fe_bh);
|
|
|
|
brelse(parent_fe_bh);
|
2008-11-14 11:16:41 +08:00
|
|
|
kfree(si.value);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2008-11-13 07:43:34 +08:00
|
|
|
ocfs2_free_dir_lookup_result(&lookup);
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
if (inode_ac)
|
|
|
|
ocfs2_free_alloc_context(inode_ac);
|
|
|
|
|
|
|
|
if (data_ac)
|
|
|
|
ocfs2_free_alloc_context(data_ac);
|
|
|
|
|
2008-11-13 08:27:44 +08:00
|
|
|
if (meta_ac)
|
|
|
|
ocfs2_free_alloc_context(meta_ac);
|
2008-11-14 11:16:41 +08:00
|
|
|
|
2010-04-22 16:11:29 +08:00
|
|
|
/*
|
2022-03-23 05:38:45 +08:00
|
|
|
* We should call iput after the i_rwsem of the bitmap been
|
2010-04-22 16:11:29 +08:00
|
|
|
* unlocked in ocfs2_free_alloc_context, or the
|
|
|
|
* ocfs2_delete_inode will mutex_lock again.
|
|
|
|
*/
|
|
|
|
if ((status < 0) && inode) {
|
2014-06-24 04:22:09 +08:00
|
|
|
if (dl)
|
|
|
|
ocfs2_cleanup_add_entry_failure(osb, dentry, inode);
|
|
|
|
|
2010-04-22 16:11:29 +08:00
|
|
|
OCFS2_I(inode)->ip_flags |= OCFS2_INODE_SKIP_ORPHAN_DIR;
|
|
|
|
clear_nlink(inode);
|
|
|
|
iput(inode);
|
|
|
|
}
|
|
|
|
|
2011-03-07 16:43:21 +08:00
|
|
|
if (status)
|
|
|
|
mlog_errno(status);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2010-08-14 06:15:15 +08:00
|
|
|
static int __ocfs2_mknod_locked(struct inode *dir,
|
|
|
|
struct inode *inode,
|
|
|
|
dev_t dev,
|
|
|
|
struct buffer_head **new_fe_bh,
|
|
|
|
struct buffer_head *parent_fe_bh,
|
|
|
|
handle_t *handle,
|
|
|
|
struct ocfs2_alloc_context *inode_ac,
|
|
|
|
u64 fe_blkno, u64 suballoc_loc, u16 suballoc_bit)
|
2005-12-16 06:31:24 +08:00
|
|
|
{
|
|
|
|
int status = 0;
|
2010-08-14 06:15:15 +08:00
|
|
|
struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
|
2005-12-16 06:31:24 +08:00
|
|
|
struct ocfs2_dinode *fe = NULL;
|
|
|
|
struct ocfs2_extent_list *fel;
|
2008-11-13 08:27:44 +08:00
|
|
|
u16 feat;
|
2014-04-04 05:46:48 +08:00
|
|
|
struct ocfs2_inode_info *oi = OCFS2_I(inode);
|
2016-12-13 08:41:26 +08:00
|
|
|
struct timespec64 ts;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
*new_fe_bh = NULL;
|
|
|
|
|
|
|
|
/* populate as many fields early on as possible - many of
|
|
|
|
* these are used by the support functions here and in
|
|
|
|
* callers. */
|
|
|
|
inode->i_ino = ino_from_blkno(osb->sb, fe_blkno);
|
2018-04-06 07:18:37 +08:00
|
|
|
oi->ip_blkno = fe_blkno;
|
2005-12-16 06:31:24 +08:00
|
|
|
spin_lock(&osb->osb_lock);
|
|
|
|
inode->i_generation = osb->s_next_generation++;
|
|
|
|
spin_unlock(&osb->osb_lock);
|
|
|
|
|
|
|
|
*new_fe_bh = sb_getblk(osb->sb, fe_blkno);
|
|
|
|
if (!*new_fe_bh) {
|
2013-11-13 07:06:54 +08:00
|
|
|
status = -ENOMEM;
|
2005-12-16 06:31:24 +08:00
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
2009-02-11 12:00:41 +08:00
|
|
|
ocfs2_set_new_buffer_uptodate(INODE_CACHE(inode), *new_fe_bh);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2009-02-13 08:41:25 +08:00
|
|
|
status = ocfs2_journal_access_di(handle, INODE_CACHE(inode),
|
|
|
|
*new_fe_bh,
|
2008-10-18 10:25:01 +08:00
|
|
|
OCFS2_JOURNAL_ACCESS_CREATE);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
fe = (struct ocfs2_dinode *) (*new_fe_bh)->b_data;
|
|
|
|
memset(fe, 0, osb->sb->s_blocksize);
|
|
|
|
|
|
|
|
fe->i_generation = cpu_to_le32(inode->i_generation);
|
|
|
|
fe->i_fs_generation = cpu_to_le32(osb->fs_generation);
|
|
|
|
fe->i_blkno = cpu_to_le64(fe_blkno);
|
2010-03-26 10:09:15 +08:00
|
|
|
fe->i_suballoc_loc = cpu_to_le64(suballoc_loc);
|
2005-12-16 06:31:24 +08:00
|
|
|
fe->i_suballoc_bit = cpu_to_le16(suballoc_bit);
|
2008-03-05 16:11:46 +08:00
|
|
|
fe->i_suballoc_slot = cpu_to_le16(inode_ac->ac_alloc_slot);
|
2013-02-01 09:27:11 +08:00
|
|
|
fe->i_uid = cpu_to_le32(i_uid_read(inode));
|
|
|
|
fe->i_gid = cpu_to_le32(i_gid_read(inode));
|
2008-11-14 11:15:44 +08:00
|
|
|
fe->i_mode = cpu_to_le16(inode->i_mode);
|
|
|
|
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
|
2005-12-16 06:31:24 +08:00
|
|
|
fe->id1.dev1.i_rdev = cpu_to_le64(huge_encode_dev(dev));
|
2008-11-21 09:54:57 +08:00
|
|
|
|
|
|
|
ocfs2_set_links_count(fe, inode->i_nlink);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
fe->i_last_eb_blk = 0;
|
|
|
|
strcpy(fe->i_signature, OCFS2_INODE_SIGNATURE);
|
2013-07-04 06:00:48 +08:00
|
|
|
fe->i_flags |= cpu_to_le32(OCFS2_VALID_FL);
|
2016-12-13 08:41:26 +08:00
|
|
|
ktime_get_real_ts64(&ts);
|
2005-12-16 06:31:24 +08:00
|
|
|
fe->i_atime = fe->i_ctime = fe->i_mtime =
|
2016-12-13 08:41:26 +08:00
|
|
|
cpu_to_le64(ts.tv_sec);
|
2005-12-16 06:31:24 +08:00
|
|
|
fe->i_mtime_nsec = fe->i_ctime_nsec = fe->i_atime_nsec =
|
2016-12-13 08:41:26 +08:00
|
|
|
cpu_to_le32(ts.tv_nsec);
|
2005-12-16 06:31:24 +08:00
|
|
|
fe->i_dtime = 0;
|
|
|
|
|
2007-09-14 07:33:54 +08:00
|
|
|
/*
|
2008-11-13 08:27:44 +08:00
|
|
|
* If supported, directories start with inline data. If inline
|
|
|
|
* isn't supported, but indexing is, we start them as indexed.
|
2007-09-14 07:33:54 +08:00
|
|
|
*/
|
2008-11-13 08:27:44 +08:00
|
|
|
feat = le16_to_cpu(fe->i_dyn_features);
|
2008-11-14 11:15:44 +08:00
|
|
|
if (S_ISDIR(inode->i_mode) && ocfs2_supports_inline_data(osb)) {
|
2007-09-14 07:33:54 +08:00
|
|
|
fe->i_dyn_features = cpu_to_le16(feat | OCFS2_INLINE_DATA_FL);
|
|
|
|
|
2009-03-05 11:06:15 +08:00
|
|
|
fe->id2.i_data.id_count = cpu_to_le16(
|
|
|
|
ocfs2_max_inline_data_with_xattr(osb->sb, fe));
|
2007-09-14 07:33:54 +08:00
|
|
|
} else {
|
|
|
|
fel = &fe->id2.i_list;
|
|
|
|
fel->l_tree_depth = 0;
|
|
|
|
fel->l_next_free_rec = 0;
|
|
|
|
fel->l_count = cpu_to_le16(ocfs2_extent_recs_per_inode(osb->sb));
|
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2010-03-20 05:13:52 +08:00
|
|
|
ocfs2_journal_dirty(handle, *new_fe_bh);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2008-11-14 06:49:11 +08:00
|
|
|
ocfs2_populate_inode(inode, fe, 1);
|
2009-02-13 08:41:25 +08:00
|
|
|
ocfs2_ci_set_new(osb, INODE_CACHE(inode));
|
2006-12-06 09:56:35 +08:00
|
|
|
if (!ocfs2_mount_local(osb)) {
|
|
|
|
status = ocfs2_create_new_inode_locks(inode);
|
|
|
|
if (status < 0)
|
|
|
|
mlog_errno(status);
|
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2020-01-31 14:11:53 +08:00
|
|
|
ocfs2_update_inode_fsync_trans(handle, inode, 1);
|
2014-04-04 05:46:48 +08:00
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
leave:
|
|
|
|
if (status < 0) {
|
|
|
|
if (*new_fe_bh) {
|
|
|
|
brelse(*new_fe_bh);
|
|
|
|
*new_fe_bh = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-07 16:43:21 +08:00
|
|
|
if (status)
|
|
|
|
mlog_errno(status);
|
2005-12-16 06:31:24 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2010-08-14 06:15:15 +08:00
|
|
|
static int ocfs2_mknod_locked(struct ocfs2_super *osb,
|
|
|
|
struct inode *dir,
|
|
|
|
struct inode *inode,
|
|
|
|
dev_t dev,
|
|
|
|
struct buffer_head **new_fe_bh,
|
|
|
|
struct buffer_head *parent_fe_bh,
|
|
|
|
handle_t *handle,
|
|
|
|
struct ocfs2_alloc_context *inode_ac)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
u64 suballoc_loc, fe_blkno = 0;
|
|
|
|
u16 suballoc_bit;
|
|
|
|
|
|
|
|
*new_fe_bh = NULL;
|
|
|
|
|
|
|
|
status = ocfs2_claim_new_inode(handle, dir, parent_fe_bh,
|
|
|
|
inode_ac, &suballoc_loc,
|
|
|
|
&suballoc_bit, &fe_blkno);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2022-10-17 21:02:26 +08:00
|
|
|
return __ocfs2_mknod_locked(dir, inode, dev, new_fe_bh,
|
2010-08-14 06:15:15 +08:00
|
|
|
parent_fe_bh, handle, inode_ac,
|
|
|
|
fe_blkno, suballoc_loc, suballoc_bit);
|
|
|
|
}
|
|
|
|
|
2021-01-21 21:19:43 +08:00
|
|
|
static int ocfs2_mkdir(struct user_namespace *mnt_userns,
|
|
|
|
struct inode *dir,
|
2005-12-16 06:31:24 +08:00
|
|
|
struct dentry *dentry,
|
2011-07-26 13:41:39 +08:00
|
|
|
umode_t mode)
|
2005-12-16 06:31:24 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_mkdir(dir, dentry, dentry->d_name.len, dentry->d_name.name,
|
|
|
|
OCFS2_I(dir)->ip_blkno, mode);
|
2021-01-21 21:19:43 +08:00
|
|
|
ret = ocfs2_mknod(&init_user_ns, dir, dentry, mode | S_IFDIR, 0);
|
2011-03-07 16:43:21 +08:00
|
|
|
if (ret)
|
|
|
|
mlog_errno(ret);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-01-21 21:19:43 +08:00
|
|
|
static int ocfs2_create(struct user_namespace *mnt_userns,
|
|
|
|
struct inode *dir,
|
2005-12-16 06:31:24 +08:00
|
|
|
struct dentry *dentry,
|
2011-07-26 13:42:34 +08:00
|
|
|
umode_t mode,
|
2012-06-11 06:05:36 +08:00
|
|
|
bool excl)
|
2005-12-16 06:31:24 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_create(dir, dentry, dentry->d_name.len, dentry->d_name.name,
|
|
|
|
(unsigned long long)OCFS2_I(dir)->ip_blkno, mode);
|
2021-01-21 21:19:43 +08:00
|
|
|
ret = ocfs2_mknod(&init_user_ns, dir, dentry, mode | S_IFREG, 0);
|
2011-03-07 16:43:21 +08:00
|
|
|
if (ret)
|
|
|
|
mlog_errno(ret);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocfs2_link(struct dentry *old_dentry,
|
|
|
|
struct inode *dir,
|
|
|
|
struct dentry *dentry)
|
|
|
|
{
|
2006-10-10 09:11:45 +08:00
|
|
|
handle_t *handle;
|
2015-03-18 06:25:59 +08:00
|
|
|
struct inode *inode = d_inode(old_dentry);
|
|
|
|
struct inode *old_dir = d_inode(old_dentry->d_parent);
|
2005-12-16 06:31:24 +08:00
|
|
|
int err;
|
|
|
|
struct buffer_head *fe_bh = NULL;
|
2015-01-09 06:32:23 +08:00
|
|
|
struct buffer_head *old_dir_bh = NULL;
|
2005-12-16 06:31:24 +08:00
|
|
|
struct buffer_head *parent_fe_bh = NULL;
|
|
|
|
struct ocfs2_dinode *fe = NULL;
|
|
|
|
struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
|
2008-11-13 07:43:34 +08:00
|
|
|
struct ocfs2_dir_lookup_result lookup = { NULL, };
|
2010-05-11 02:56:52 +08:00
|
|
|
sigset_t oldset;
|
ocfs2: check existence of old dentry in ocfs2_link()
System call linkat first calls user_path_at(), check the existence of
old dentry, and then calls vfs_link()->ocfs2_link() to do the actual
work. There may exist a race when Node A create a hard link for file
while node B rm it.
Node A Node B
user_path_at()
->ocfs2_lookup(),
find old dentry exist
rm file, add inode say inodeA
to orphan_dir
call ocfs2_link(),create a
hard link for inodeA.
rm the link, add inodeA to orphan_dir
again
When orphan_scan work start, it calls ocfs2_queue_orphans() to do the
main work. It first tranverses entrys in orphan_dir, linking all inodes
in this orphan_dir to a list look like this:
inodeA->inodeB->...->inodeA
When tranvering this list, it will fall into loop, calling iput() again
and again. And finally trigger BUG_ON(inode->i_state & I_CLEAR).
Signed-off-by: joyce <xuejiufei@huawei.com>
Reviewed-by: Mark Fasheh <mfasheh@suse.com>
Cc: Joel Becker <jlbec@evilplan.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2014-02-11 06:25:54 +08:00
|
|
|
u64 old_de_ino;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_link((unsigned long long)OCFS2_I(inode)->ip_blkno,
|
|
|
|
old_dentry->d_name.len, old_dentry->d_name.name,
|
|
|
|
dentry->d_name.len, dentry->d_name.name);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2006-10-06 07:48:23 +08:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
return -EPERM;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2015-07-14 19:36:02 +08:00
|
|
|
err = dquot_initialize(dir);
|
|
|
|
if (err) {
|
|
|
|
mlog_errno(err);
|
|
|
|
return err;
|
|
|
|
}
|
2010-03-03 22:05:06 +08:00
|
|
|
|
2015-01-09 06:32:23 +08:00
|
|
|
err = ocfs2_double_lock(osb, &old_dir_bh, old_dir,
|
|
|
|
&parent_fe_bh, dir, 0);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (err < 0) {
|
|
|
|
if (err != -ENOENT)
|
|
|
|
mlog_errno(err);
|
2006-10-06 07:48:23 +08:00
|
|
|
return err;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2015-01-09 06:32:23 +08:00
|
|
|
/* make sure both dirs have bhs
|
|
|
|
* get an extra ref on old_dir_bh if old==new */
|
|
|
|
if (!parent_fe_bh) {
|
|
|
|
if (old_dir_bh) {
|
|
|
|
parent_fe_bh = old_dir_bh;
|
|
|
|
get_bh(parent_fe_bh);
|
|
|
|
} else {
|
|
|
|
mlog(ML_ERROR, "%s: no old_dir_bh!\n", osb->uuid_str);
|
|
|
|
err = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-01 11:39:47 +08:00
|
|
|
if (!dir->i_nlink) {
|
|
|
|
err = -ENOENT;
|
2006-10-06 07:48:23 +08:00
|
|
|
goto out;
|
2006-09-01 11:39:47 +08:00
|
|
|
}
|
|
|
|
|
2015-01-09 06:32:23 +08:00
|
|
|
err = ocfs2_lookup_ino_from_name(old_dir, old_dentry->d_name.name,
|
ocfs2: check existence of old dentry in ocfs2_link()
System call linkat first calls user_path_at(), check the existence of
old dentry, and then calls vfs_link()->ocfs2_link() to do the actual
work. There may exist a race when Node A create a hard link for file
while node B rm it.
Node A Node B
user_path_at()
->ocfs2_lookup(),
find old dentry exist
rm file, add inode say inodeA
to orphan_dir
call ocfs2_link(),create a
hard link for inodeA.
rm the link, add inodeA to orphan_dir
again
When orphan_scan work start, it calls ocfs2_queue_orphans() to do the
main work. It first tranverses entrys in orphan_dir, linking all inodes
in this orphan_dir to a list look like this:
inodeA->inodeB->...->inodeA
When tranvering this list, it will fall into loop, calling iput() again
and again. And finally trigger BUG_ON(inode->i_state & I_CLEAR).
Signed-off-by: joyce <xuejiufei@huawei.com>
Reviewed-by: Mark Fasheh <mfasheh@suse.com>
Cc: Joel Becker <jlbec@evilplan.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2014-02-11 06:25:54 +08:00
|
|
|
old_dentry->d_name.len, &old_de_ino);
|
|
|
|
if (err) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check whether another node removed the source inode while we
|
|
|
|
* were in the vfs.
|
|
|
|
*/
|
|
|
|
if (old_de_ino != OCFS2_I(inode)->ip_blkno) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
err = ocfs2_check_dir_for_entry(dir, dentry->d_name.name,
|
|
|
|
dentry->d_name.len);
|
|
|
|
if (err)
|
2006-10-06 07:48:23 +08:00
|
|
|
goto out;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
err = ocfs2_prepare_dir_for_insert(osb, dir, parent_fe_bh,
|
|
|
|
dentry->d_name.name,
|
2008-11-13 07:43:34 +08:00
|
|
|
dentry->d_name.len, &lookup);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (err < 0) {
|
|
|
|
mlog_errno(err);
|
2006-10-06 07:48:23 +08:00
|
|
|
goto out;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2007-10-19 06:30:42 +08:00
|
|
|
err = ocfs2_inode_lock(inode, &fe_bh, 1);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (err < 0) {
|
|
|
|
if (err != -ENOENT)
|
|
|
|
mlog_errno(err);
|
2006-10-06 07:48:23 +08:00
|
|
|
goto out;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
fe = (struct ocfs2_dinode *) fe_bh->b_data;
|
2008-11-21 09:54:57 +08:00
|
|
|
if (ocfs2_read_links_count(fe) >= ocfs2_link_max(osb)) {
|
2005-12-16 06:31:24 +08:00
|
|
|
err = -EMLINK;
|
2006-10-06 07:48:23 +08:00
|
|
|
goto out_unlock_inode;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2008-10-10 01:38:40 +08:00
|
|
|
handle = ocfs2_start_trans(osb, ocfs2_link_credits(osb->sb));
|
2005-12-16 06:31:24 +08:00
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
err = PTR_ERR(handle);
|
|
|
|
handle = NULL;
|
|
|
|
mlog_errno(err);
|
2006-10-06 07:48:23 +08:00
|
|
|
goto out_unlock_inode;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2010-05-11 02:56:52 +08:00
|
|
|
/* Starting to change things, restart is no longer possible. */
|
|
|
|
ocfs2_block_signals(&oldset);
|
|
|
|
|
2009-02-13 08:41:25 +08:00
|
|
|
err = ocfs2_journal_access_di(handle, INODE_CACHE(inode), fe_bh,
|
2008-10-18 10:25:01 +08:00
|
|
|
OCFS2_JOURNAL_ACCESS_WRITE);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (err < 0) {
|
|
|
|
mlog_errno(err);
|
2006-10-06 07:48:23 +08:00
|
|
|
goto out_commit;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2006-10-01 14:29:04 +08:00
|
|
|
inc_nlink(inode);
|
2016-09-14 22:48:04 +08:00
|
|
|
inode->i_ctime = current_time(inode);
|
2008-11-21 09:54:57 +08:00
|
|
|
ocfs2_set_links_count(fe, inode->i_nlink);
|
2005-12-16 06:31:24 +08:00
|
|
|
fe->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
|
|
|
|
fe->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
|
2010-03-20 05:13:52 +08:00
|
|
|
ocfs2_journal_dirty(handle, fe_bh);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
err = ocfs2_add_entry(handle, dentry, inode,
|
|
|
|
OCFS2_I(inode)->ip_blkno,
|
2008-11-13 07:43:34 +08:00
|
|
|
parent_fe_bh, &lookup);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (err) {
|
2008-11-21 09:54:57 +08:00
|
|
|
ocfs2_add_links_count(fe, -1);
|
2006-10-01 14:29:03 +08:00
|
|
|
drop_nlink(inode);
|
2005-12-16 06:31:24 +08:00
|
|
|
mlog_errno(err);
|
2006-10-06 07:48:23 +08:00
|
|
|
goto out_commit;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2006-09-22 07:51:28 +08:00
|
|
|
err = ocfs2_dentry_attach_lock(dentry, inode, OCFS2_I(dir)->ip_blkno);
|
2006-09-09 05:21:03 +08:00
|
|
|
if (err) {
|
|
|
|
mlog_errno(err);
|
2006-10-06 07:48:23 +08:00
|
|
|
goto out_commit;
|
2006-09-09 05:21:03 +08:00
|
|
|
}
|
|
|
|
|
2010-10-23 23:11:40 +08:00
|
|
|
ihold(inode);
|
2005-12-16 06:31:24 +08:00
|
|
|
d_instantiate(dentry, inode);
|
2006-10-06 07:48:23 +08:00
|
|
|
|
|
|
|
out_commit:
|
2006-10-10 07:48:10 +08:00
|
|
|
ocfs2_commit_trans(osb, handle);
|
2010-05-11 02:56:52 +08:00
|
|
|
ocfs2_unblock_signals(&oldset);
|
2006-10-06 07:48:23 +08:00
|
|
|
out_unlock_inode:
|
2007-10-19 06:30:42 +08:00
|
|
|
ocfs2_inode_unlock(inode, 1);
|
2006-10-06 07:48:23 +08:00
|
|
|
|
|
|
|
out:
|
2015-01-09 06:32:23 +08:00
|
|
|
ocfs2_double_unlock(old_dir, dir);
|
2006-10-06 07:48:23 +08:00
|
|
|
|
2008-10-08 05:25:16 +08:00
|
|
|
brelse(fe_bh);
|
|
|
|
brelse(parent_fe_bh);
|
2015-01-09 06:32:23 +08:00
|
|
|
brelse(old_dir_bh);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2008-11-13 07:43:34 +08:00
|
|
|
ocfs2_free_dir_lookup_result(&lookup);
|
|
|
|
|
2011-03-07 16:43:21 +08:00
|
|
|
if (err)
|
|
|
|
mlog_errno(err);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2006-09-09 05:21:03 +08:00
|
|
|
/*
|
|
|
|
* Takes and drops an exclusive lock on the given dentry. This will
|
|
|
|
* force other nodes to drop it.
|
|
|
|
*/
|
|
|
|
static int ocfs2_remote_dentry_delete(struct dentry *dentry)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ocfs2_dentry_lock(dentry, 1);
|
|
|
|
if (ret)
|
|
|
|
mlog_errno(ret);
|
|
|
|
else
|
|
|
|
ocfs2_dentry_unlock(dentry, 1);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
ocfs2: fix readonly issue in ocfs2_unlink()
While deleting a file with ocfs2_unlink(), there is a bug in this
function. This bug will result in filesystem read-only.
After calling ocfs2_orphan_add(), the file which will be deleted is
added into orphan dir. If ocfs2_delete_entry() fails, the file still
exists in the parent dir. And this scenario introduces a conflict of
metadata.
If a file is added into orphan dir, when we put inode of the file with
iput(), the inode i_flags is setted (~OCFS2_VALID_FL) in
ocfs2_remove_inode(), and then write back to disk.
But as previously mentioned, the file still exists in the parent dir.
On other nodes, the file can be still accessed. When first read the
file with ocfs2_read_blocks() from disk, It will check and avalidate
inode using ocfs2_validate_inode_block(). So File system will be
readonly because the inode is invalid. In other words, the inode
i_flags has been set (~OCFS2_VALID_FL).
[akpm@linux-foundation.org: cleanups]
[jeff.liu@oracle.com: s/inode_is_unlinkable/ocfs2_inode_is_unlinkable/]
Signed-off-by: Younger Liu <younger.liu@huawei.com>
Signed-off-by: Jensen <shencanquan@huawei.com>
Cc: Jie Liu <jeff.liu@oracle.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Mark Fasheh <mfasheh@suse.com>
Cc: Sunil Mushran <sunil.mushran@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-07-04 06:01:01 +08:00
|
|
|
static inline int ocfs2_inode_is_unlinkable(struct inode *inode)
|
2006-10-01 14:29:05 +08:00
|
|
|
{
|
|
|
|
if (S_ISDIR(inode->i_mode)) {
|
|
|
|
if (inode->i_nlink == 2)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inode->i_nlink == 1)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
static int ocfs2_unlink(struct inode *dir,
|
|
|
|
struct dentry *dentry)
|
|
|
|
{
|
|
|
|
int status;
|
2006-10-07 02:49:45 +08:00
|
|
|
int child_locked = 0;
|
ocfs2: fix readonly issue in ocfs2_unlink()
While deleting a file with ocfs2_unlink(), there is a bug in this
function. This bug will result in filesystem read-only.
After calling ocfs2_orphan_add(), the file which will be deleted is
added into orphan dir. If ocfs2_delete_entry() fails, the file still
exists in the parent dir. And this scenario introduces a conflict of
metadata.
If a file is added into orphan dir, when we put inode of the file with
iput(), the inode i_flags is setted (~OCFS2_VALID_FL) in
ocfs2_remove_inode(), and then write back to disk.
But as previously mentioned, the file still exists in the parent dir.
On other nodes, the file can be still accessed. When first read the
file with ocfs2_read_blocks() from disk, It will check and avalidate
inode using ocfs2_validate_inode_block(). So File system will be
readonly because the inode is invalid. In other words, the inode
i_flags has been set (~OCFS2_VALID_FL).
[akpm@linux-foundation.org: cleanups]
[jeff.liu@oracle.com: s/inode_is_unlinkable/ocfs2_inode_is_unlinkable/]
Signed-off-by: Younger Liu <younger.liu@huawei.com>
Signed-off-by: Jensen <shencanquan@huawei.com>
Cc: Jie Liu <jeff.liu@oracle.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Mark Fasheh <mfasheh@suse.com>
Cc: Sunil Mushran <sunil.mushran@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-07-04 06:01:01 +08:00
|
|
|
bool is_unlinkable = false;
|
2015-03-18 06:25:59 +08:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2006-10-06 09:12:57 +08:00
|
|
|
struct inode *orphan_dir = NULL;
|
2005-12-16 06:31:24 +08:00
|
|
|
struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
|
|
|
|
u64 blkno;
|
|
|
|
struct ocfs2_dinode *fe = NULL;
|
|
|
|
struct buffer_head *fe_bh = NULL;
|
|
|
|
struct buffer_head *parent_node_bh = NULL;
|
2006-10-10 09:11:45 +08:00
|
|
|
handle_t *handle = NULL;
|
2005-12-16 06:31:24 +08:00
|
|
|
char orphan_name[OCFS2_ORPHAN_NAMELEN + 1];
|
2008-11-13 07:43:34 +08:00
|
|
|
struct ocfs2_dir_lookup_result lookup = { NULL, };
|
|
|
|
struct ocfs2_dir_lookup_result orphan_insert = { NULL, };
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_unlink(dir, dentry, dentry->d_name.len,
|
|
|
|
dentry->d_name.name,
|
|
|
|
(unsigned long long)OCFS2_I(dir)->ip_blkno,
|
|
|
|
(unsigned long long)OCFS2_I(inode)->ip_blkno);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2015-07-14 19:36:02 +08:00
|
|
|
status = dquot_initialize(dir);
|
|
|
|
if (status) {
|
|
|
|
mlog_errno(status);
|
|
|
|
return status;
|
|
|
|
}
|
2010-03-03 22:05:06 +08:00
|
|
|
|
2015-03-18 06:25:59 +08:00
|
|
|
BUG_ON(d_inode(dentry->d_parent) != dir);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
if (inode == osb->root_inode)
|
2006-10-07 02:49:45 +08:00
|
|
|
return -EPERM;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2009-06-04 21:26:50 +08:00
|
|
|
status = ocfs2_inode_lock_nested(dir, &parent_node_bh, 1,
|
|
|
|
OI_LS_PARENT);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOENT)
|
|
|
|
mlog_errno(status);
|
2006-10-07 02:49:45 +08:00
|
|
|
return status;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_find_files_on_disk(dentry->d_name.name,
|
2008-11-13 07:43:34 +08:00
|
|
|
dentry->d_name.len, &blkno, dir,
|
|
|
|
&lookup);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOENT)
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (OCFS2_I(inode)->ip_blkno != blkno) {
|
|
|
|
status = -ENOENT;
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_unlink_noent(
|
|
|
|
(unsigned long long)OCFS2_I(inode)->ip_blkno,
|
|
|
|
(unsigned long long)blkno,
|
|
|
|
OCFS2_I(inode)->ip_flags);
|
2005-12-16 06:31:24 +08:00
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2007-10-19 06:30:42 +08:00
|
|
|
status = ocfs2_inode_lock(inode, &fe_bh, 1);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOENT)
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
2006-10-07 02:49:45 +08:00
|
|
|
child_locked = 1;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
if (S_ISDIR(inode->i_mode)) {
|
2009-02-19 03:41:38 +08:00
|
|
|
if (inode->i_nlink != 2 || !ocfs2_empty_dir(inode)) {
|
2005-12-16 06:31:24 +08:00
|
|
|
status = -ENOTEMPTY;
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-09 05:21:03 +08:00
|
|
|
status = ocfs2_remote_dentry_delete(dentry);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
2007-09-25 06:56:19 +08:00
|
|
|
/* This remote delete should succeed under all normal
|
2005-12-16 06:31:24 +08:00
|
|
|
* circumstances. */
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
ocfs2: fix readonly issue in ocfs2_unlink()
While deleting a file with ocfs2_unlink(), there is a bug in this
function. This bug will result in filesystem read-only.
After calling ocfs2_orphan_add(), the file which will be deleted is
added into orphan dir. If ocfs2_delete_entry() fails, the file still
exists in the parent dir. And this scenario introduces a conflict of
metadata.
If a file is added into orphan dir, when we put inode of the file with
iput(), the inode i_flags is setted (~OCFS2_VALID_FL) in
ocfs2_remove_inode(), and then write back to disk.
But as previously mentioned, the file still exists in the parent dir.
On other nodes, the file can be still accessed. When first read the
file with ocfs2_read_blocks() from disk, It will check and avalidate
inode using ocfs2_validate_inode_block(). So File system will be
readonly because the inode is invalid. In other words, the inode
i_flags has been set (~OCFS2_VALID_FL).
[akpm@linux-foundation.org: cleanups]
[jeff.liu@oracle.com: s/inode_is_unlinkable/ocfs2_inode_is_unlinkable/]
Signed-off-by: Younger Liu <younger.liu@huawei.com>
Signed-off-by: Jensen <shencanquan@huawei.com>
Cc: Jie Liu <jeff.liu@oracle.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Mark Fasheh <mfasheh@suse.com>
Cc: Sunil Mushran <sunil.mushran@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-07-04 06:01:01 +08:00
|
|
|
if (ocfs2_inode_is_unlinkable(inode)) {
|
2009-08-18 11:44:10 +08:00
|
|
|
status = ocfs2_prepare_orphan_dir(osb, &orphan_dir,
|
|
|
|
OCFS2_I(inode)->ip_blkno,
|
2015-02-17 07:59:54 +08:00
|
|
|
orphan_name, &orphan_insert,
|
|
|
|
false);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
ocfs2: fix readonly issue in ocfs2_unlink()
While deleting a file with ocfs2_unlink(), there is a bug in this
function. This bug will result in filesystem read-only.
After calling ocfs2_orphan_add(), the file which will be deleted is
added into orphan dir. If ocfs2_delete_entry() fails, the file still
exists in the parent dir. And this scenario introduces a conflict of
metadata.
If a file is added into orphan dir, when we put inode of the file with
iput(), the inode i_flags is setted (~OCFS2_VALID_FL) in
ocfs2_remove_inode(), and then write back to disk.
But as previously mentioned, the file still exists in the parent dir.
On other nodes, the file can be still accessed. When first read the
file with ocfs2_read_blocks() from disk, It will check and avalidate
inode using ocfs2_validate_inode_block(). So File system will be
readonly because the inode is invalid. In other words, the inode
i_flags has been set (~OCFS2_VALID_FL).
[akpm@linux-foundation.org: cleanups]
[jeff.liu@oracle.com: s/inode_is_unlinkable/ocfs2_inode_is_unlinkable/]
Signed-off-by: Younger Liu <younger.liu@huawei.com>
Signed-off-by: Jensen <shencanquan@huawei.com>
Cc: Jie Liu <jeff.liu@oracle.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Mark Fasheh <mfasheh@suse.com>
Cc: Sunil Mushran <sunil.mushran@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-07-04 06:01:01 +08:00
|
|
|
is_unlinkable = true;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2008-10-10 01:38:40 +08:00
|
|
|
handle = ocfs2_start_trans(osb, ocfs2_unlink_credits(osb->sb));
|
2005-12-16 06:31:24 +08:00
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
status = PTR_ERR(handle);
|
|
|
|
handle = NULL;
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2009-02-13 08:41:25 +08:00
|
|
|
status = ocfs2_journal_access_di(handle, INODE_CACHE(inode), fe_bh,
|
2008-10-18 10:25:01 +08:00
|
|
|
OCFS2_JOURNAL_ACCESS_WRITE);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
fe = (struct ocfs2_dinode *) fe_bh->b_data;
|
|
|
|
|
|
|
|
/* delete the name from the parent dir */
|
2008-11-13 07:43:34 +08:00
|
|
|
status = ocfs2_delete_entry(handle, dir, &lookup);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2006-10-01 14:29:05 +08:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
drop_nlink(inode);
|
|
|
|
drop_nlink(inode);
|
2008-11-21 09:54:57 +08:00
|
|
|
ocfs2_set_links_count(fe, inode->i_nlink);
|
2010-03-20 05:13:52 +08:00
|
|
|
ocfs2_journal_dirty(handle, fe_bh);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2016-09-14 22:48:04 +08:00
|
|
|
dir->i_ctime = dir->i_mtime = current_time(dir);
|
2007-01-03 09:59:40 +08:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
2006-10-01 14:29:05 +08:00
|
|
|
drop_nlink(dir);
|
2007-01-03 09:59:40 +08:00
|
|
|
|
|
|
|
status = ocfs2_mark_inode_dirty(handle, dir, parent_node_bh);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
if (S_ISDIR(inode->i_mode))
|
2006-10-01 14:29:04 +08:00
|
|
|
inc_nlink(dir);
|
ocfs2: fix readonly issue in ocfs2_unlink()
While deleting a file with ocfs2_unlink(), there is a bug in this
function. This bug will result in filesystem read-only.
After calling ocfs2_orphan_add(), the file which will be deleted is
added into orphan dir. If ocfs2_delete_entry() fails, the file still
exists in the parent dir. And this scenario introduces a conflict of
metadata.
If a file is added into orphan dir, when we put inode of the file with
iput(), the inode i_flags is setted (~OCFS2_VALID_FL) in
ocfs2_remove_inode(), and then write back to disk.
But as previously mentioned, the file still exists in the parent dir.
On other nodes, the file can be still accessed. When first read the
file with ocfs2_read_blocks() from disk, It will check and avalidate
inode using ocfs2_validate_inode_block(). So File system will be
readonly because the inode is invalid. In other words, the inode
i_flags has been set (~OCFS2_VALID_FL).
[akpm@linux-foundation.org: cleanups]
[jeff.liu@oracle.com: s/inode_is_unlinkable/ocfs2_inode_is_unlinkable/]
Signed-off-by: Younger Liu <younger.liu@huawei.com>
Signed-off-by: Jensen <shencanquan@huawei.com>
Cc: Jie Liu <jeff.liu@oracle.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Mark Fasheh <mfasheh@suse.com>
Cc: Sunil Mushran <sunil.mushran@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-07-04 06:01:01 +08:00
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_unlinkable) {
|
|
|
|
status = ocfs2_orphan_add(osb, handle, inode, fe_bh,
|
2015-02-17 07:59:54 +08:00
|
|
|
orphan_name, &orphan_insert, orphan_dir, false);
|
ocfs2: fix readonly issue in ocfs2_unlink()
While deleting a file with ocfs2_unlink(), there is a bug in this
function. This bug will result in filesystem read-only.
After calling ocfs2_orphan_add(), the file which will be deleted is
added into orphan dir. If ocfs2_delete_entry() fails, the file still
exists in the parent dir. And this scenario introduces a conflict of
metadata.
If a file is added into orphan dir, when we put inode of the file with
iput(), the inode i_flags is setted (~OCFS2_VALID_FL) in
ocfs2_remove_inode(), and then write back to disk.
But as previously mentioned, the file still exists in the parent dir.
On other nodes, the file can be still accessed. When first read the
file with ocfs2_read_blocks() from disk, It will check and avalidate
inode using ocfs2_validate_inode_block(). So File system will be
readonly because the inode is invalid. In other words, the inode
i_flags has been set (~OCFS2_VALID_FL).
[akpm@linux-foundation.org: cleanups]
[jeff.liu@oracle.com: s/inode_is_unlinkable/ocfs2_inode_is_unlinkable/]
Signed-off-by: Younger Liu <younger.liu@huawei.com>
Signed-off-by: Jensen <shencanquan@huawei.com>
Cc: Jie Liu <jeff.liu@oracle.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Mark Fasheh <mfasheh@suse.com>
Cc: Sunil Mushran <sunil.mushran@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-07-04 06:01:01 +08:00
|
|
|
if (status < 0)
|
|
|
|
mlog_errno(status);
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
leave:
|
|
|
|
if (handle)
|
2006-10-10 07:48:10 +08:00
|
|
|
ocfs2_commit_trans(osb, handle);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2006-10-06 09:12:57 +08:00
|
|
|
if (orphan_dir) {
|
|
|
|
/* This was locked for us in ocfs2_prepare_orphan_dir() */
|
2007-10-19 06:30:42 +08:00
|
|
|
ocfs2_inode_unlock(orphan_dir, 1);
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_unlock(orphan_dir);
|
2006-10-06 09:12:57 +08:00
|
|
|
iput(orphan_dir);
|
|
|
|
}
|
|
|
|
|
2015-09-05 06:43:52 +08:00
|
|
|
if (child_locked)
|
|
|
|
ocfs2_inode_unlock(inode, 1);
|
|
|
|
|
|
|
|
ocfs2_inode_unlock(dir, 1);
|
|
|
|
|
2008-10-08 05:25:16 +08:00
|
|
|
brelse(fe_bh);
|
|
|
|
brelse(parent_node_bh);
|
2008-11-13 07:43:34 +08:00
|
|
|
|
|
|
|
ocfs2_free_dir_lookup_result(&orphan_insert);
|
|
|
|
ocfs2_free_dir_lookup_result(&lookup);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2014-01-28 09:06:56 +08:00
|
|
|
if (status && (status != -ENOTEMPTY) && (status != -ENOENT))
|
2011-03-07 16:43:21 +08:00
|
|
|
mlog_errno(status);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2014-06-24 04:22:09 +08:00
|
|
|
static int ocfs2_check_if_ancestor(struct ocfs2_super *osb,
|
|
|
|
u64 src_inode_no, u64 dest_inode_no)
|
|
|
|
{
|
|
|
|
int ret = 0, i = 0;
|
|
|
|
u64 parent_inode_no = 0;
|
|
|
|
u64 child_inode_no = src_inode_no;
|
|
|
|
struct inode *child_inode;
|
|
|
|
|
|
|
|
#define MAX_LOOKUP_TIMES 32
|
|
|
|
while (1) {
|
|
|
|
child_inode = ocfs2_iget(osb, child_inode_no, 0, 0);
|
|
|
|
if (IS_ERR(child_inode)) {
|
|
|
|
ret = PTR_ERR(child_inode);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ocfs2_inode_lock(child_inode, NULL, 0);
|
|
|
|
if (ret < 0) {
|
|
|
|
iput(child_inode);
|
|
|
|
if (ret != -ENOENT)
|
|
|
|
mlog_errno(ret);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ocfs2_lookup_ino_from_name(child_inode, "..", 2,
|
|
|
|
&parent_inode_no);
|
|
|
|
ocfs2_inode_unlock(child_inode, 0);
|
|
|
|
iput(child_inode);
|
|
|
|
if (ret < 0) {
|
|
|
|
ret = -ENOENT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parent_inode_no == dest_inode_no) {
|
|
|
|
ret = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parent_inode_no == osb->root_inode->i_ino) {
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
child_inode_no = parent_inode_no;
|
|
|
|
|
|
|
|
if (++i >= MAX_LOOKUP_TIMES) {
|
2020-12-15 11:03:40 +08:00
|
|
|
mlog_ratelimited(ML_NOTICE, "max lookup times reached, "
|
|
|
|
"filesystem may have nested directories, "
|
2014-06-24 04:22:09 +08:00
|
|
|
"src inode: %llu, dest inode: %llu.\n",
|
|
|
|
(unsigned long long)src_inode_no,
|
|
|
|
(unsigned long long)dest_inode_no);
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
/*
|
2015-01-09 06:32:23 +08:00
|
|
|
* The only place this should be used is rename and link!
|
2005-12-16 06:31:24 +08:00
|
|
|
* if they have the same id, then the 1st one is the only one locked.
|
|
|
|
*/
|
|
|
|
static int ocfs2_double_lock(struct ocfs2_super *osb,
|
|
|
|
struct buffer_head **bh1,
|
|
|
|
struct inode *inode1,
|
|
|
|
struct buffer_head **bh2,
|
2015-01-09 06:32:23 +08:00
|
|
|
struct inode *inode2,
|
|
|
|
int rename)
|
2005-12-16 06:31:24 +08:00
|
|
|
{
|
|
|
|
int status;
|
2014-06-24 04:22:09 +08:00
|
|
|
int inode1_is_ancestor, inode2_is_ancestor;
|
2005-12-16 06:31:24 +08:00
|
|
|
struct ocfs2_inode_info *oi1 = OCFS2_I(inode1);
|
|
|
|
struct ocfs2_inode_info *oi2 = OCFS2_I(inode2);
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_double_lock((unsigned long long)oi1->ip_blkno,
|
|
|
|
(unsigned long long)oi2->ip_blkno);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
if (*bh1)
|
|
|
|
*bh1 = NULL;
|
|
|
|
if (*bh2)
|
|
|
|
*bh2 = NULL;
|
|
|
|
|
2014-06-24 04:22:09 +08:00
|
|
|
/* we always want to lock the one with the lower lockid first.
|
|
|
|
* and if they are nested, we lock ancestor first */
|
2005-12-16 06:31:24 +08:00
|
|
|
if (oi1->ip_blkno != oi2->ip_blkno) {
|
2014-06-24 04:22:09 +08:00
|
|
|
inode1_is_ancestor = ocfs2_check_if_ancestor(osb, oi2->ip_blkno,
|
|
|
|
oi1->ip_blkno);
|
|
|
|
if (inode1_is_ancestor < 0) {
|
|
|
|
status = inode1_is_ancestor;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
inode2_is_ancestor = ocfs2_check_if_ancestor(osb, oi1->ip_blkno,
|
|
|
|
oi2->ip_blkno);
|
|
|
|
if (inode2_is_ancestor < 0) {
|
|
|
|
status = inode2_is_ancestor;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((inode1_is_ancestor == 1) ||
|
|
|
|
(oi1->ip_blkno < oi2->ip_blkno &&
|
|
|
|
inode2_is_ancestor == 0)) {
|
2005-12-16 06:31:24 +08:00
|
|
|
/* switch id1 and id2 around */
|
2015-06-25 07:55:31 +08:00
|
|
|
swap(bh2, bh1);
|
|
|
|
swap(inode2, inode1);
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
/* lock id2 */
|
2009-06-04 21:26:50 +08:00
|
|
|
status = ocfs2_inode_lock_nested(inode2, bh2, 1,
|
2015-01-09 06:32:23 +08:00
|
|
|
rename == 1 ? OI_LS_RENAME1 : OI_LS_PARENT);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOENT)
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
2006-10-07 06:04:51 +08:00
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
/* lock id1 */
|
2015-01-09 06:32:23 +08:00
|
|
|
status = ocfs2_inode_lock_nested(inode1, bh1, 1,
|
|
|
|
rename == 1 ? OI_LS_RENAME2 : OI_LS_PARENT);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
2006-10-07 06:04:51 +08:00
|
|
|
/*
|
|
|
|
* An error return must mean that no cluster locks
|
|
|
|
* were held on function exit.
|
|
|
|
*/
|
2010-12-20 16:21:11 +08:00
|
|
|
if (oi1->ip_blkno != oi2->ip_blkno) {
|
2007-10-19 06:30:42 +08:00
|
|
|
ocfs2_inode_unlock(inode2, 1);
|
2010-12-20 16:21:11 +08:00
|
|
|
brelse(*bh2);
|
|
|
|
*bh2 = NULL;
|
|
|
|
}
|
2006-10-07 06:04:51 +08:00
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status != -ENOENT)
|
|
|
|
mlog_errno(status);
|
|
|
|
}
|
2006-10-07 06:04:51 +08:00
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_double_lock_end(
|
2018-04-06 07:18:37 +08:00
|
|
|
(unsigned long long)oi1->ip_blkno,
|
|
|
|
(unsigned long long)oi2->ip_blkno);
|
2011-02-23 22:45:26 +08:00
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
bail:
|
2011-03-07 16:43:21 +08:00
|
|
|
if (status)
|
|
|
|
mlog_errno(status);
|
2005-12-16 06:31:24 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2006-10-07 06:04:51 +08:00
|
|
|
static void ocfs2_double_unlock(struct inode *inode1, struct inode *inode2)
|
|
|
|
{
|
2007-10-19 06:30:42 +08:00
|
|
|
ocfs2_inode_unlock(inode1, 1);
|
2006-10-07 06:04:51 +08:00
|
|
|
|
|
|
|
if (inode1 != inode2)
|
2007-10-19 06:30:42 +08:00
|
|
|
ocfs2_inode_unlock(inode2, 1);
|
2006-10-07 06:04:51 +08:00
|
|
|
}
|
|
|
|
|
2021-01-21 21:19:43 +08:00
|
|
|
static int ocfs2_rename(struct user_namespace *mnt_userns,
|
|
|
|
struct inode *old_dir,
|
2005-12-16 06:31:24 +08:00
|
|
|
struct dentry *old_dentry,
|
|
|
|
struct inode *new_dir,
|
fs: make remaining filesystems use .rename2
This is trivial to do:
- add flags argument to foo_rename()
- check if flags is zero
- assign foo_rename() to .rename2 instead of .rename
This doesn't mean it's impossible to support RENAME_NOREPLACE for these
filesystems, but it is not trivial, like for local filesystems.
RENAME_NOREPLACE must guarantee atomicity (i.e. it shouldn't be possible
for a file to be created on one host while it is overwritten by rename on
another host).
Filesystems converted:
9p, afs, ceph, coda, ecryptfs, kernfs, lustre, ncpfs, nfs, ocfs2, orangefs.
After this, we can get rid of the duplicate interfaces for rename.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: David Howells <dhowells@redhat.com> [AFS]
Acked-by: Mike Marshall <hubcap@omnibond.com>
Cc: Eric Van Hensbergen <ericvh@gmail.com>
Cc: Ilya Dryomov <idryomov@gmail.com>
Cc: Jan Harkes <jaharkes@cs.cmu.edu>
Cc: Tyler Hicks <tyhicks@canonical.com>
Cc: Oleg Drokin <oleg.drokin@intel.com>
Cc: Trond Myklebust <trond.myklebust@primarydata.com>
Cc: Mark Fasheh <mfasheh@suse.com>
2016-09-27 17:03:58 +08:00
|
|
|
struct dentry *new_dentry,
|
|
|
|
unsigned int flags)
|
2005-12-16 06:31:24 +08:00
|
|
|
{
|
2008-11-13 07:43:34 +08:00
|
|
|
int status = 0, rename_lock = 0, parents_locked = 0, target_exists = 0;
|
|
|
|
int old_child_locked = 0, new_child_locked = 0, update_dot_dot = 0;
|
2015-03-18 06:25:59 +08:00
|
|
|
struct inode *old_inode = d_inode(old_dentry);
|
|
|
|
struct inode *new_inode = d_inode(new_dentry);
|
2006-10-06 09:12:57 +08:00
|
|
|
struct inode *orphan_dir = NULL;
|
2005-12-16 06:31:24 +08:00
|
|
|
struct ocfs2_dinode *newfe = NULL;
|
|
|
|
char orphan_name[OCFS2_ORPHAN_NAMELEN + 1];
|
|
|
|
struct buffer_head *newfe_bh = NULL;
|
2007-01-03 09:59:40 +08:00
|
|
|
struct buffer_head *old_inode_bh = NULL;
|
2005-12-16 06:31:24 +08:00
|
|
|
struct ocfs2_super *osb = NULL;
|
2007-09-14 07:33:54 +08:00
|
|
|
u64 newfe_blkno, old_de_ino;
|
2006-10-10 09:11:45 +08:00
|
|
|
handle_t *handle = NULL;
|
2005-12-16 06:31:24 +08:00
|
|
|
struct buffer_head *old_dir_bh = NULL;
|
|
|
|
struct buffer_head *new_dir_bh = NULL;
|
2012-02-13 10:00:05 +08:00
|
|
|
u32 old_dir_nlink = old_dir->i_nlink;
|
2007-08-07 06:11:56 +08:00
|
|
|
struct ocfs2_dinode *old_di;
|
2008-11-13 07:43:34 +08:00
|
|
|
struct ocfs2_dir_lookup_result old_inode_dot_dot_res = { NULL, };
|
|
|
|
struct ocfs2_dir_lookup_result target_lookup_res = { NULL, };
|
|
|
|
struct ocfs2_dir_lookup_result old_entry_lookup = { NULL, };
|
|
|
|
struct ocfs2_dir_lookup_result orphan_insert = { NULL, };
|
|
|
|
struct ocfs2_dir_lookup_result target_insert = { NULL, };
|
2014-06-24 04:22:07 +08:00
|
|
|
bool should_add_orphan = false;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
fs: make remaining filesystems use .rename2
This is trivial to do:
- add flags argument to foo_rename()
- check if flags is zero
- assign foo_rename() to .rename2 instead of .rename
This doesn't mean it's impossible to support RENAME_NOREPLACE for these
filesystems, but it is not trivial, like for local filesystems.
RENAME_NOREPLACE must guarantee atomicity (i.e. it shouldn't be possible
for a file to be created on one host while it is overwritten by rename on
another host).
Filesystems converted:
9p, afs, ceph, coda, ecryptfs, kernfs, lustre, ncpfs, nfs, ocfs2, orangefs.
After this, we can get rid of the duplicate interfaces for rename.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: David Howells <dhowells@redhat.com> [AFS]
Acked-by: Mike Marshall <hubcap@omnibond.com>
Cc: Eric Van Hensbergen <ericvh@gmail.com>
Cc: Ilya Dryomov <idryomov@gmail.com>
Cc: Jan Harkes <jaharkes@cs.cmu.edu>
Cc: Tyler Hicks <tyhicks@canonical.com>
Cc: Oleg Drokin <oleg.drokin@intel.com>
Cc: Trond Myklebust <trond.myklebust@primarydata.com>
Cc: Mark Fasheh <mfasheh@suse.com>
2016-09-27 17:03:58 +08:00
|
|
|
if (flags)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
/* At some point it might be nice to break this function up a
|
|
|
|
* bit. */
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_rename(old_dir, old_dentry, new_dir, new_dentry,
|
|
|
|
old_dentry->d_name.len, old_dentry->d_name.name,
|
|
|
|
new_dentry->d_name.len, new_dentry->d_name.name);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2015-07-14 19:36:02 +08:00
|
|
|
status = dquot_initialize(old_dir);
|
|
|
|
if (status) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
status = dquot_initialize(new_dir);
|
|
|
|
if (status) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
2010-03-03 22:05:06 +08:00
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
osb = OCFS2_SB(old_dir->i_sb);
|
|
|
|
|
|
|
|
if (new_inode) {
|
|
|
|
if (!igrab(new_inode))
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
|
2007-02-18 02:23:03 +08:00
|
|
|
/* Assume a directory hierarchy thusly:
|
2005-12-16 06:31:24 +08:00
|
|
|
* a/b/c
|
|
|
|
* a/d
|
|
|
|
* a,b,c, and d are all directories.
|
|
|
|
*
|
|
|
|
* from cwd of 'a' on both nodes:
|
|
|
|
* node1: mv b/c d
|
|
|
|
* node2: mv d b/c
|
|
|
|
*
|
|
|
|
* And that's why, just like the VFS, we need a file system
|
|
|
|
* rename lock. */
|
2008-02-22 01:00:00 +08:00
|
|
|
if (old_dir != new_dir && S_ISDIR(old_inode->i_mode)) {
|
2005-12-16 06:31:24 +08:00
|
|
|
status = ocfs2_rename_lock(osb);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
rename_lock = 1;
|
2014-06-24 04:22:09 +08:00
|
|
|
|
|
|
|
/* here we cannot guarantee the inodes haven't just been
|
|
|
|
* changed, so check if they are nested again */
|
|
|
|
status = ocfs2_check_if_ancestor(osb, new_dir->i_ino,
|
|
|
|
old_inode->i_ino);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
} else if (status == 1) {
|
|
|
|
status = -EPERM;
|
|
|
|
trace_ocfs2_rename_not_permitted(
|
|
|
|
(unsigned long long)old_inode->i_ino,
|
|
|
|
(unsigned long long)new_dir->i_ino);
|
|
|
|
goto bail;
|
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* if old and new are the same, this'll just do one lock. */
|
2006-10-07 06:04:51 +08:00
|
|
|
status = ocfs2_double_lock(osb, &old_dir_bh, old_dir,
|
2015-01-09 06:32:23 +08:00
|
|
|
&new_dir_bh, new_dir, 1);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
2006-10-07 06:04:51 +08:00
|
|
|
parents_locked = 1;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2015-09-05 06:44:25 +08:00
|
|
|
if (!new_dir->i_nlink) {
|
|
|
|
status = -EACCES;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
/* make sure both dirs have bhs
|
|
|
|
* get an extra ref on old_dir_bh if old==new */
|
|
|
|
if (!new_dir_bh) {
|
|
|
|
if (old_dir_bh) {
|
|
|
|
new_dir_bh = old_dir_bh;
|
|
|
|
get_bh(new_dir_bh);
|
|
|
|
} else {
|
|
|
|
mlog(ML_ERROR, "no old_dir_bh!\n");
|
|
|
|
status = -EIO;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-09 05:21:03 +08:00
|
|
|
/*
|
2007-01-03 09:59:40 +08:00
|
|
|
* Aside from allowing a meta data update, the locking here
|
2007-09-25 06:56:19 +08:00
|
|
|
* also ensures that the downconvert thread on other nodes
|
|
|
|
* won't have to concurrently downconvert the inode and the
|
|
|
|
* dentry locks.
|
2006-09-09 05:21:03 +08:00
|
|
|
*/
|
2009-06-04 21:26:50 +08:00
|
|
|
status = ocfs2_inode_lock_nested(old_inode, &old_inode_bh, 1,
|
|
|
|
OI_LS_PARENT);
|
2006-09-09 05:21:03 +08:00
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOENT)
|
2005-12-16 06:31:24 +08:00
|
|
|
mlog_errno(status);
|
2006-09-09 05:21:03 +08:00
|
|
|
goto bail;
|
|
|
|
}
|
2006-10-07 06:04:51 +08:00
|
|
|
old_child_locked = 1;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2006-09-09 05:21:03 +08:00
|
|
|
status = ocfs2_remote_dentry_delete(old_dentry);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (S_ISDIR(old_inode->i_mode)) {
|
2007-09-12 08:21:56 +08:00
|
|
|
u64 old_inode_parent;
|
|
|
|
|
2008-11-13 07:43:34 +08:00
|
|
|
update_dot_dot = 1;
|
2007-09-12 08:21:56 +08:00
|
|
|
status = ocfs2_find_files_on_disk("..", 2, &old_inode_parent,
|
2008-11-13 07:43:34 +08:00
|
|
|
old_inode,
|
|
|
|
&old_inode_dot_dot_res);
|
2007-09-12 08:21:56 +08:00
|
|
|
if (status) {
|
|
|
|
status = -EIO;
|
2005-12-16 06:31:24 +08:00
|
|
|
goto bail;
|
2007-09-12 08:21:56 +08:00
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2007-09-12 08:21:56 +08:00
|
|
|
if (old_inode_parent != OCFS2_I(old_dir)->ip_blkno) {
|
|
|
|
status = -EIO;
|
2005-12-16 06:31:24 +08:00
|
|
|
goto bail;
|
2007-09-12 08:21:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!new_inode && new_dir != old_dir &&
|
2008-11-21 09:54:57 +08:00
|
|
|
new_dir->i_nlink >= ocfs2_link_max(osb)) {
|
2007-09-12 08:21:56 +08:00
|
|
|
status = -EMLINK;
|
2005-12-16 06:31:24 +08:00
|
|
|
goto bail;
|
2007-09-12 08:21:56 +08:00
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2007-09-14 07:33:54 +08:00
|
|
|
status = ocfs2_lookup_ino_from_name(old_dir, old_dentry->d_name.name,
|
|
|
|
old_dentry->d_name.len,
|
|
|
|
&old_de_ino);
|
|
|
|
if (status) {
|
|
|
|
status = -ENOENT;
|
2005-12-16 06:31:24 +08:00
|
|
|
goto bail;
|
2007-09-14 07:33:54 +08:00
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for inode number is _not_ due to possible IO errors.
|
|
|
|
* We might rmdir the source, keep it as pwd of some process
|
|
|
|
* and merrily kill the link to whatever was created under the
|
|
|
|
* same name. Goodbye sticky bit ;-<
|
|
|
|
*/
|
2007-09-14 07:33:54 +08:00
|
|
|
if (old_de_ino != OCFS2_I(old_inode)->ip_blkno) {
|
|
|
|
status = -ENOENT;
|
2005-12-16 06:31:24 +08:00
|
|
|
goto bail;
|
2007-09-14 07:33:54 +08:00
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
/* check if the target already exists (in which case we need
|
|
|
|
* to delete it */
|
|
|
|
status = ocfs2_find_files_on_disk(new_dentry->d_name.name,
|
|
|
|
new_dentry->d_name.len,
|
2008-11-13 07:43:34 +08:00
|
|
|
&newfe_blkno, new_dir,
|
|
|
|
&target_lookup_res);
|
2005-12-16 06:31:24 +08:00
|
|
|
/* The only error we allow here is -ENOENT because the new
|
|
|
|
* file not existing is perfectly valid. */
|
|
|
|
if ((status < 0) && (status != -ENOENT)) {
|
|
|
|
/* If we cannot find the file specified we should just */
|
|
|
|
/* return the error... */
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
2008-11-13 07:43:34 +08:00
|
|
|
if (status == 0)
|
|
|
|
target_exists = 1;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2008-11-13 07:43:34 +08:00
|
|
|
if (!target_exists && new_inode) {
|
2007-11-01 07:49:43 +08:00
|
|
|
/*
|
|
|
|
* Target was unlinked by another node while we were
|
|
|
|
* waiting to get to ocfs2_rename(). There isn't
|
|
|
|
* anything we can do here to help the situation, so
|
|
|
|
* bubble up the appropriate error.
|
|
|
|
*/
|
|
|
|
status = -ENOENT;
|
|
|
|
goto bail;
|
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
/* In case we need to overwrite an existing file, we blow it
|
|
|
|
* away first */
|
2008-11-13 07:43:34 +08:00
|
|
|
if (target_exists) {
|
2005-12-16 06:31:24 +08:00
|
|
|
/* VFS didn't think there existed an inode here, but
|
|
|
|
* someone else in the cluster must have raced our
|
|
|
|
* rename to create one. Today we error cleanly, in
|
|
|
|
* the future we should consider calling iget to build
|
|
|
|
* a new struct inode for this entry. */
|
|
|
|
if (!new_inode) {
|
|
|
|
status = -EACCES;
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_rename_target_exists(new_dentry->d_name.len,
|
|
|
|
new_dentry->d_name.name);
|
2005-12-16 06:31:24 +08:00
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (OCFS2_I(new_inode)->ip_blkno != newfe_blkno) {
|
|
|
|
status = -EACCES;
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_rename_disagree(
|
2006-03-04 02:24:33 +08:00
|
|
|
(unsigned long long)OCFS2_I(new_inode)->ip_blkno,
|
|
|
|
(unsigned long long)newfe_blkno,
|
2005-12-16 06:31:24 +08:00
|
|
|
OCFS2_I(new_inode)->ip_flags);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2007-10-19 06:30:42 +08:00
|
|
|
status = ocfs2_inode_lock(new_inode, &newfe_bh, 1);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOENT)
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
2006-10-07 06:04:51 +08:00
|
|
|
new_child_locked = 1;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2006-09-09 05:21:03 +08:00
|
|
|
status = ocfs2_remote_dentry_delete(new_dentry);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
newfe = (struct ocfs2_dinode *) newfe_bh->b_data;
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_rename_over_existing(
|
2006-03-04 02:24:33 +08:00
|
|
|
(unsigned long long)newfe_blkno, newfe_bh, newfe_bh ?
|
2005-12-16 06:31:24 +08:00
|
|
|
(unsigned long long)newfe_bh->b_blocknr : 0ULL);
|
|
|
|
|
|
|
|
if (S_ISDIR(new_inode->i_mode) || (new_inode->i_nlink == 1)) {
|
2006-10-06 09:12:57 +08:00
|
|
|
status = ocfs2_prepare_orphan_dir(osb, &orphan_dir,
|
2009-08-18 11:44:10 +08:00
|
|
|
OCFS2_I(new_inode)->ip_blkno,
|
2015-02-17 07:59:54 +08:00
|
|
|
orphan_name, &orphan_insert,
|
|
|
|
false);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
2014-06-24 04:22:07 +08:00
|
|
|
should_add_orphan = true;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
} else {
|
2015-03-18 06:25:59 +08:00
|
|
|
BUG_ON(d_inode(new_dentry->d_parent) != new_dir);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
status = ocfs2_check_dir_for_entry(new_dir,
|
|
|
|
new_dentry->d_name.name,
|
|
|
|
new_dentry->d_name.len);
|
|
|
|
if (status)
|
|
|
|
goto bail;
|
|
|
|
|
|
|
|
status = ocfs2_prepare_dir_for_insert(osb, new_dir, new_dir_bh,
|
|
|
|
new_dentry->d_name.name,
|
|
|
|
new_dentry->d_name.len,
|
2008-11-13 07:43:34 +08:00
|
|
|
&target_insert);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-10 01:38:40 +08:00
|
|
|
handle = ocfs2_start_trans(osb, ocfs2_rename_credits(osb->sb));
|
2005-12-16 06:31:24 +08:00
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
status = PTR_ERR(handle);
|
|
|
|
handle = NULL;
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2008-11-13 07:43:34 +08:00
|
|
|
if (target_exists) {
|
2005-12-16 06:31:24 +08:00
|
|
|
if (S_ISDIR(new_inode->i_mode)) {
|
2009-02-19 03:41:38 +08:00
|
|
|
if (new_inode->i_nlink != 2 ||
|
|
|
|
!ocfs2_empty_dir(new_inode)) {
|
2005-12-16 06:31:24 +08:00
|
|
|
status = -ENOTEMPTY;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
2009-02-13 08:41:25 +08:00
|
|
|
status = ocfs2_journal_access_di(handle, INODE_CACHE(new_inode),
|
|
|
|
newfe_bh,
|
2008-10-18 10:25:01 +08:00
|
|
|
OCFS2_JOURNAL_ACCESS_WRITE);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* change the dirent to point to the correct inode */
|
2008-11-13 07:43:34 +08:00
|
|
|
status = ocfs2_update_entry(new_dir, handle, &target_lookup_res,
|
|
|
|
old_inode);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
2017-12-11 19:35:17 +08:00
|
|
|
inode_inc_iversion(new_dir);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
if (S_ISDIR(new_inode->i_mode))
|
2008-11-21 09:54:57 +08:00
|
|
|
ocfs2_set_links_count(newfe, 0);
|
2005-12-16 06:31:24 +08:00
|
|
|
else
|
2008-11-21 09:54:57 +08:00
|
|
|
ocfs2_add_links_count(newfe, -1);
|
2010-03-20 05:13:52 +08:00
|
|
|
ocfs2_journal_dirty(handle, newfe_bh);
|
2014-06-24 04:22:07 +08:00
|
|
|
if (should_add_orphan) {
|
|
|
|
status = ocfs2_orphan_add(osb, handle, new_inode,
|
|
|
|
newfe_bh, orphan_name,
|
2015-02-17 07:59:54 +08:00
|
|
|
&orphan_insert, orphan_dir, false);
|
2014-06-24 04:22:07 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
} else {
|
|
|
|
/* if the name was not found in new_dir, add it now */
|
|
|
|
status = ocfs2_add_entry(handle, new_dentry, old_inode,
|
|
|
|
OCFS2_I(old_inode)->ip_blkno,
|
2008-11-13 07:43:34 +08:00
|
|
|
new_dir_bh, &target_insert);
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-14 22:48:04 +08:00
|
|
|
old_inode->i_ctime = current_time(old_inode);
|
2005-12-16 06:31:24 +08:00
|
|
|
mark_inode_dirty(old_inode);
|
2007-08-07 06:11:56 +08:00
|
|
|
|
2009-02-13 08:41:25 +08:00
|
|
|
status = ocfs2_journal_access_di(handle, INODE_CACHE(old_inode),
|
|
|
|
old_inode_bh,
|
2008-10-18 10:25:01 +08:00
|
|
|
OCFS2_JOURNAL_ACCESS_WRITE);
|
2007-08-07 06:11:56 +08:00
|
|
|
if (status >= 0) {
|
|
|
|
old_di = (struct ocfs2_dinode *) old_inode_bh->b_data;
|
|
|
|
|
|
|
|
old_di->i_ctime = cpu_to_le64(old_inode->i_ctime.tv_sec);
|
|
|
|
old_di->i_ctime_nsec = cpu_to_le32(old_inode->i_ctime.tv_nsec);
|
2010-03-20 05:13:52 +08:00
|
|
|
ocfs2_journal_dirty(handle, old_inode_bh);
|
2007-08-07 06:11:56 +08:00
|
|
|
} else
|
|
|
|
mlog_errno(status);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2007-09-14 07:33:54 +08:00
|
|
|
/*
|
|
|
|
* Now that the name has been added to new_dir, remove the old name.
|
|
|
|
*
|
|
|
|
* We don't keep any directory entry context around until now
|
|
|
|
* because the insert might have changed the type of directory
|
|
|
|
* we're dealing with.
|
|
|
|
*/
|
2008-11-13 07:43:34 +08:00
|
|
|
status = ocfs2_find_entry(old_dentry->d_name.name,
|
|
|
|
old_dentry->d_name.len, old_dir,
|
|
|
|
&old_entry_lookup);
|
2015-09-05 06:44:06 +08:00
|
|
|
if (status) {
|
|
|
|
if (!is_journal_aborted(osb->journal->j_journal)) {
|
|
|
|
ocfs2_error(osb->sb, "new entry %.*s is added, but old entry %.*s "
|
|
|
|
"is not deleted.",
|
|
|
|
new_dentry->d_name.len, new_dentry->d_name.name,
|
|
|
|
old_dentry->d_name.len, old_dentry->d_name.name);
|
|
|
|
}
|
2007-09-14 07:33:54 +08:00
|
|
|
goto bail;
|
2015-09-05 06:44:06 +08:00
|
|
|
}
|
2007-09-14 07:33:54 +08:00
|
|
|
|
2008-11-13 07:43:34 +08:00
|
|
|
status = ocfs2_delete_entry(handle, old_dir, &old_entry_lookup);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
2015-09-05 06:44:06 +08:00
|
|
|
if (!is_journal_aborted(osb->journal->j_journal)) {
|
|
|
|
ocfs2_error(osb->sb, "new entry %.*s is added, but old entry %.*s "
|
|
|
|
"is not deleted.",
|
|
|
|
new_dentry->d_name.len, new_dentry->d_name.name,
|
|
|
|
old_dentry->d_name.len, old_dentry->d_name.name);
|
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_inode) {
|
2011-10-28 20:13:28 +08:00
|
|
|
drop_nlink(new_inode);
|
2016-09-14 22:48:04 +08:00
|
|
|
new_inode->i_ctime = current_time(new_inode);
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
2016-09-14 22:48:04 +08:00
|
|
|
old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir);
|
2008-11-13 07:43:34 +08:00
|
|
|
|
|
|
|
if (update_dot_dot) {
|
|
|
|
status = ocfs2_update_entry(old_inode, handle,
|
|
|
|
&old_inode_dot_dot_res, new_dir);
|
2011-10-28 20:13:28 +08:00
|
|
|
drop_nlink(old_dir);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (new_inode) {
|
2011-10-28 20:13:28 +08:00
|
|
|
drop_nlink(new_inode);
|
2005-12-16 06:31:24 +08:00
|
|
|
} else {
|
2006-10-01 14:29:04 +08:00
|
|
|
inc_nlink(new_dir);
|
2005-12-16 06:31:24 +08:00
|
|
|
mark_inode_dirty(new_dir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mark_inode_dirty(old_dir);
|
2007-01-03 09:59:40 +08:00
|
|
|
ocfs2_mark_inode_dirty(handle, old_dir, old_dir_bh);
|
|
|
|
if (new_inode) {
|
2005-12-16 06:31:24 +08:00
|
|
|
mark_inode_dirty(new_inode);
|
2007-01-03 09:59:40 +08:00
|
|
|
ocfs2_mark_inode_dirty(handle, new_inode, newfe_bh);
|
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2007-01-03 09:59:40 +08:00
|
|
|
if (old_dir != new_dir) {
|
|
|
|
/* Keep the same times on both directories.*/
|
|
|
|
new_dir->i_ctime = new_dir->i_mtime = old_dir->i_ctime;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This will also pick up the i_nlink change from the
|
|
|
|
* block above.
|
|
|
|
*/
|
|
|
|
ocfs2_mark_inode_dirty(handle, new_dir, new_dir_bh);
|
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
if (old_dir_nlink != old_dir->i_nlink) {
|
|
|
|
if (!old_dir_bh) {
|
|
|
|
mlog(ML_ERROR, "need to change nlink for old dir "
|
2006-03-04 02:24:33 +08:00
|
|
|
"%llu from %d to %d but bh is NULL!\n",
|
|
|
|
(unsigned long long)OCFS2_I(old_dir)->ip_blkno,
|
|
|
|
(int)old_dir_nlink, old_dir->i_nlink);
|
2005-12-16 06:31:24 +08:00
|
|
|
} else {
|
|
|
|
struct ocfs2_dinode *fe;
|
2009-02-13 08:41:25 +08:00
|
|
|
status = ocfs2_journal_access_di(handle,
|
|
|
|
INODE_CACHE(old_dir),
|
|
|
|
old_dir_bh,
|
|
|
|
OCFS2_JOURNAL_ACCESS_WRITE);
|
2005-12-16 06:31:24 +08:00
|
|
|
fe = (struct ocfs2_dinode *) old_dir_bh->b_data;
|
2008-11-21 09:54:57 +08:00
|
|
|
ocfs2_set_links_count(fe, old_dir->i_nlink);
|
2010-03-20 05:13:52 +08:00
|
|
|
ocfs2_journal_dirty(handle, old_dir_bh);
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
}
|
2006-09-09 05:21:03 +08:00
|
|
|
ocfs2_dentry_move(old_dentry, new_dentry, old_dir, new_dir);
|
2005-12-16 06:31:24 +08:00
|
|
|
status = 0;
|
|
|
|
bail:
|
|
|
|
if (handle)
|
2006-10-10 07:48:10 +08:00
|
|
|
ocfs2_commit_trans(osb, handle);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2006-10-06 09:12:57 +08:00
|
|
|
if (orphan_dir) {
|
|
|
|
/* This was locked for us in ocfs2_prepare_orphan_dir() */
|
2007-10-19 06:30:42 +08:00
|
|
|
ocfs2_inode_unlock(orphan_dir, 1);
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_unlock(orphan_dir);
|
2006-10-06 09:12:57 +08:00
|
|
|
iput(orphan_dir);
|
|
|
|
}
|
|
|
|
|
2015-09-05 06:43:52 +08:00
|
|
|
if (new_child_locked)
|
|
|
|
ocfs2_inode_unlock(new_inode, 1);
|
|
|
|
|
|
|
|
if (old_child_locked)
|
|
|
|
ocfs2_inode_unlock(old_inode, 1);
|
|
|
|
|
|
|
|
if (parents_locked)
|
|
|
|
ocfs2_double_unlock(old_dir, new_dir);
|
|
|
|
|
|
|
|
if (rename_lock)
|
|
|
|
ocfs2_rename_unlock(osb);
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
if (new_inode)
|
|
|
|
sync_mapping_buffers(old_inode->i_mapping);
|
|
|
|
|
2016-01-15 07:17:27 +08:00
|
|
|
iput(new_inode);
|
2008-11-13 07:43:34 +08:00
|
|
|
|
|
|
|
ocfs2_free_dir_lookup_result(&target_lookup_res);
|
|
|
|
ocfs2_free_dir_lookup_result(&old_entry_lookup);
|
|
|
|
ocfs2_free_dir_lookup_result(&old_inode_dot_dot_res);
|
|
|
|
ocfs2_free_dir_lookup_result(&orphan_insert);
|
|
|
|
ocfs2_free_dir_lookup_result(&target_insert);
|
|
|
|
|
2008-10-08 05:25:16 +08:00
|
|
|
brelse(newfe_bh);
|
|
|
|
brelse(old_inode_bh);
|
|
|
|
brelse(old_dir_bh);
|
|
|
|
brelse(new_dir_bh);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2011-03-07 16:43:21 +08:00
|
|
|
if (status)
|
|
|
|
mlog_errno(status);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we expect i_size = strlen(symname). Copy symname into the file
|
|
|
|
* data, including the null terminator.
|
|
|
|
*/
|
|
|
|
static int ocfs2_create_symlink_data(struct ocfs2_super *osb,
|
2006-10-10 09:11:45 +08:00
|
|
|
handle_t *handle,
|
2005-12-16 06:31:24 +08:00
|
|
|
struct inode *inode,
|
|
|
|
const char *symname)
|
|
|
|
{
|
|
|
|
struct buffer_head **bhs = NULL;
|
|
|
|
const char *c;
|
|
|
|
struct super_block *sb = osb->sb;
|
2007-03-10 08:26:50 +08:00
|
|
|
u64 p_blkno, p_blocks;
|
2005-12-16 06:31:24 +08:00
|
|
|
int virtual, blocks, status, i, bytes_left;
|
|
|
|
|
|
|
|
bytes_left = i_size_read(inode) + 1;
|
|
|
|
/* we can't trust i_blocks because we're actually going to
|
|
|
|
* write i_size + 1 bytes. */
|
|
|
|
blocks = (bytes_left + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_create_symlink_data((unsigned long long)inode->i_blocks,
|
|
|
|
i_size_read(inode), blocks);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
/* Sanity check -- make sure we're going to fit. */
|
|
|
|
if (bytes_left >
|
|
|
|
ocfs2_clusters_to_bytes(sb, OCFS2_I(inode)->ip_clusters)) {
|
|
|
|
status = -EIO;
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
bhs = kcalloc(blocks, sizeof(struct buffer_head *), GFP_KERNEL);
|
|
|
|
if (!bhs) {
|
|
|
|
status = -ENOMEM;
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2007-03-10 08:21:46 +08:00
|
|
|
status = ocfs2_extent_map_get_blocks(inode, 0, &p_blkno, &p_blocks,
|
|
|
|
NULL);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* links can never be larger than one cluster so we know this
|
|
|
|
* is all going to be contiguous, but do a sanity check
|
|
|
|
* anyway. */
|
|
|
|
if ((p_blocks << sb->s_blocksize_bits) < bytes_left) {
|
|
|
|
status = -EIO;
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual = 0;
|
|
|
|
while(bytes_left > 0) {
|
|
|
|
c = &symname[virtual * sb->s_blocksize];
|
|
|
|
|
|
|
|
bhs[virtual] = sb_getblk(sb, p_blkno);
|
|
|
|
if (!bhs[virtual]) {
|
|
|
|
status = -ENOMEM;
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
2009-02-11 12:00:41 +08:00
|
|
|
ocfs2_set_new_buffer_uptodate(INODE_CACHE(inode),
|
|
|
|
bhs[virtual]);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2009-02-13 08:41:25 +08:00
|
|
|
status = ocfs2_journal_access(handle, INODE_CACHE(inode),
|
|
|
|
bhs[virtual],
|
2005-12-16 06:31:24 +08:00
|
|
|
OCFS2_JOURNAL_ACCESS_CREATE);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(bhs[virtual]->b_data, 0, sb->s_blocksize);
|
|
|
|
|
|
|
|
memcpy(bhs[virtual]->b_data, c,
|
|
|
|
(bytes_left > sb->s_blocksize) ? sb->s_blocksize :
|
|
|
|
bytes_left);
|
|
|
|
|
2010-03-20 05:13:52 +08:00
|
|
|
ocfs2_journal_dirty(handle, bhs[virtual]);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
virtual++;
|
|
|
|
p_blkno++;
|
|
|
|
bytes_left -= sb->s_blocksize;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = 0;
|
|
|
|
bail:
|
|
|
|
|
|
|
|
if (bhs) {
|
|
|
|
for(i = 0; i < blocks; i++)
|
2008-10-08 05:25:16 +08:00
|
|
|
brelse(bhs[i]);
|
2005-12-16 06:31:24 +08:00
|
|
|
kfree(bhs);
|
|
|
|
}
|
|
|
|
|
2011-03-07 16:43:21 +08:00
|
|
|
if (status)
|
|
|
|
mlog_errno(status);
|
2005-12-16 06:31:24 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2021-01-21 21:19:43 +08:00
|
|
|
static int ocfs2_symlink(struct user_namespace *mnt_userns,
|
|
|
|
struct inode *dir,
|
2005-12-16 06:31:24 +08:00
|
|
|
struct dentry *dentry,
|
|
|
|
const char *symname)
|
|
|
|
{
|
|
|
|
int status, l, credits;
|
|
|
|
u64 newsize;
|
|
|
|
struct ocfs2_super *osb = NULL;
|
|
|
|
struct inode *inode = NULL;
|
|
|
|
struct super_block *sb;
|
|
|
|
struct buffer_head *new_fe_bh = NULL;
|
|
|
|
struct buffer_head *parent_fe_bh = NULL;
|
|
|
|
struct ocfs2_dinode *fe = NULL;
|
|
|
|
struct ocfs2_dinode *dirfe;
|
2006-10-10 09:11:45 +08:00
|
|
|
handle_t *handle = NULL;
|
2005-12-16 06:31:24 +08:00
|
|
|
struct ocfs2_alloc_context *inode_ac = NULL;
|
|
|
|
struct ocfs2_alloc_context *data_ac = NULL;
|
2008-11-14 11:16:41 +08:00
|
|
|
struct ocfs2_alloc_context *xattr_ac = NULL;
|
|
|
|
int want_clusters = 0;
|
|
|
|
int xattr_credits = 0;
|
|
|
|
struct ocfs2_security_xattr_info si = {
|
|
|
|
.enable = 1,
|
|
|
|
};
|
2008-10-10 01:38:40 +08:00
|
|
|
int did_quota = 0, did_quota_inode = 0;
|
2008-11-13 07:43:34 +08:00
|
|
|
struct ocfs2_dir_lookup_result lookup = { NULL, };
|
2010-05-11 02:56:52 +08:00
|
|
|
sigset_t oldset;
|
|
|
|
int did_block_signals = 0;
|
2014-06-24 04:22:09 +08:00
|
|
|
struct ocfs2_dentry_lock *dl = NULL;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_symlink_begin(dir, dentry, symname,
|
|
|
|
dentry->d_name.len, dentry->d_name.name);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2015-07-14 19:36:02 +08:00
|
|
|
status = dquot_initialize(dir);
|
|
|
|
if (status) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
2010-03-03 22:05:06 +08:00
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
sb = dir->i_sb;
|
|
|
|
osb = OCFS2_SB(sb);
|
|
|
|
|
|
|
|
l = strlen(symname) + 1;
|
|
|
|
|
|
|
|
credits = ocfs2_calc_symlink_credits(sb);
|
|
|
|
|
|
|
|
/* lock the parent directory */
|
2007-10-19 06:30:42 +08:00
|
|
|
status = ocfs2_inode_lock(dir, &parent_fe_bh, 1);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOENT)
|
|
|
|
mlog_errno(status);
|
2006-10-07 02:54:33 +08:00
|
|
|
return status;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
dirfe = (struct ocfs2_dinode *) parent_fe_bh->b_data;
|
2008-11-21 09:54:57 +08:00
|
|
|
if (!ocfs2_read_links_count(dirfe)) {
|
2005-12-16 06:31:24 +08:00
|
|
|
/* can't make a file in a deleted directory. */
|
|
|
|
status = -ENOENT;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_check_dir_for_entry(dir, dentry->d_name.name,
|
|
|
|
dentry->d_name.len);
|
|
|
|
if (status)
|
|
|
|
goto bail;
|
|
|
|
|
|
|
|
status = ocfs2_prepare_dir_for_insert(osb, dir, parent_fe_bh,
|
|
|
|
dentry->d_name.name,
|
2008-11-13 07:43:34 +08:00
|
|
|
dentry->d_name.len, &lookup);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2006-10-07 09:34:35 +08:00
|
|
|
status = ocfs2_reserve_new_inode(osb, &inode_ac);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOSPC)
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2008-11-14 11:15:44 +08:00
|
|
|
inode = ocfs2_get_init_inode(dir, S_IFLNK | S_IRWXUGO);
|
2015-07-14 19:36:02 +08:00
|
|
|
if (IS_ERR(inode)) {
|
|
|
|
status = PTR_ERR(inode);
|
|
|
|
inode = NULL;
|
2008-11-14 11:15:44 +08:00
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2008-11-14 11:16:41 +08:00
|
|
|
/* get security xattr */
|
2011-02-02 00:05:39 +08:00
|
|
|
status = ocfs2_init_security_get(inode, dir, &dentry->d_name, &si);
|
2008-11-14 11:16:41 +08:00
|
|
|
if (status) {
|
|
|
|
if (status == -EOPNOTSUPP)
|
|
|
|
si.enable = 0;
|
|
|
|
else {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* calculate meta data/clusters for setting security xattr */
|
|
|
|
if (si.enable) {
|
|
|
|
status = ocfs2_calc_security_init(dir, &si, &want_clusters,
|
|
|
|
&xattr_credits, &xattr_ac);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
2008-11-14 11:16:41 +08:00
|
|
|
mlog_errno(status);
|
2005-12-16 06:31:24 +08:00
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-14 11:16:41 +08:00
|
|
|
/* don't reserve bitmap space for fast symlinks. */
|
|
|
|
if (l > ocfs2_fast_symlink_chars(sb))
|
|
|
|
want_clusters += 1;
|
|
|
|
|
|
|
|
status = ocfs2_reserve_clusters(osb, want_clusters, &data_ac);
|
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOSPC)
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle = ocfs2_start_trans(osb, credits + xattr_credits);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
status = PTR_ERR(handle);
|
|
|
|
handle = NULL;
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2010-05-11 02:56:52 +08:00
|
|
|
/* Starting to change things, restart is no longer possible. */
|
|
|
|
ocfs2_block_signals(&oldset);
|
|
|
|
did_block_signals = 1;
|
|
|
|
|
2010-03-03 22:05:01 +08:00
|
|
|
status = dquot_alloc_inode(inode);
|
|
|
|
if (status)
|
2008-10-10 01:38:40 +08:00
|
|
|
goto bail;
|
|
|
|
did_quota_inode = 1;
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_symlink_create(dir, dentry, dentry->d_name.len,
|
|
|
|
dentry->d_name.name,
|
|
|
|
(unsigned long long)OCFS2_I(dir)->ip_blkno,
|
|
|
|
inode->i_mode);
|
2009-08-18 11:44:10 +08:00
|
|
|
|
|
|
|
status = ocfs2_mknod_locked(osb, dir, inode,
|
2008-11-14 11:15:44 +08:00
|
|
|
0, &new_fe_bh, parent_fe_bh, handle,
|
|
|
|
inode_ac);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
fe = (struct ocfs2_dinode *) new_fe_bh->b_data;
|
|
|
|
inode->i_rdev = 0;
|
|
|
|
newsize = l - 1;
|
2012-05-03 22:14:29 +08:00
|
|
|
inode->i_op = &ocfs2_symlink_inode_operations;
|
2015-11-17 14:07:57 +08:00
|
|
|
inode_nohighmem(inode);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (l > ocfs2_fast_symlink_chars(sb)) {
|
2007-01-17 03:32:23 +08:00
|
|
|
u32 offset = 0;
|
|
|
|
|
2010-03-03 22:05:00 +08:00
|
|
|
status = dquot_alloc_space_nodirty(inode,
|
|
|
|
ocfs2_clusters_to_bytes(osb->sb, 1));
|
|
|
|
if (status)
|
2008-10-10 01:38:40 +08:00
|
|
|
goto bail;
|
|
|
|
did_quota = 1;
|
2012-05-03 22:14:29 +08:00
|
|
|
inode->i_mapping->a_ops = &ocfs2_aops;
|
2008-08-18 17:38:45 +08:00
|
|
|
status = ocfs2_add_inode_data(osb, inode, &offset, 1, 0,
|
|
|
|
new_fe_bh,
|
|
|
|
handle, data_ac, NULL,
|
|
|
|
NULL);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOSPC && status != -EINTR) {
|
2006-03-04 02:24:33 +08:00
|
|
|
mlog(ML_ERROR,
|
|
|
|
"Failed to extend file to %llu\n",
|
|
|
|
(unsigned long long)newsize);
|
2005-12-16 06:31:24 +08:00
|
|
|
mlog_errno(status);
|
|
|
|
status = -ENOSPC;
|
|
|
|
}
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
i_size_write(inode, newsize);
|
2007-03-23 07:53:23 +08:00
|
|
|
inode->i_blocks = ocfs2_inode_sector_count(inode);
|
2005-12-16 06:31:24 +08:00
|
|
|
} else {
|
2012-05-03 22:14:29 +08:00
|
|
|
inode->i_mapping->a_ops = &ocfs2_fast_symlink_aops;
|
2005-12-16 06:31:24 +08:00
|
|
|
memcpy((char *) fe->id2.i_symlink, symname, l);
|
|
|
|
i_size_write(inode, newsize);
|
|
|
|
inode->i_blocks = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_mark_inode_dirty(handle, inode, new_fe_bh);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ocfs2_inode_is_fast_symlink(inode)) {
|
|
|
|
status = ocfs2_create_symlink_data(osb, handle, inode,
|
|
|
|
symname);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-14 11:16:41 +08:00
|
|
|
if (si.enable) {
|
|
|
|
status = ocfs2_init_security_set(handle, inode, new_fe_bh, &si,
|
|
|
|
xattr_ac, data_ac);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-24 02:42:22 +08:00
|
|
|
/*
|
|
|
|
* Do this before adding the entry to the directory. We add
|
|
|
|
* also set d_op after success so that ->d_iput() will cleanup
|
|
|
|
* the dentry lock even if ocfs2_add_entry() fails below.
|
|
|
|
*/
|
|
|
|
status = ocfs2_dentry_attach_lock(dentry, inode, OCFS2_I(dir)->ip_blkno);
|
|
|
|
if (status) {
|
2005-12-16 06:31:24 +08:00
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2014-06-24 04:22:09 +08:00
|
|
|
dl = dentry->d_fsdata;
|
|
|
|
|
2010-04-24 02:42:22 +08:00
|
|
|
status = ocfs2_add_entry(handle, dentry, inode,
|
|
|
|
le64_to_cpu(fe->i_blkno), parent_fe_bh,
|
|
|
|
&lookup);
|
|
|
|
if (status < 0) {
|
2006-09-09 05:21:03 +08:00
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
insert_inode_hash(inode);
|
|
|
|
d_instantiate(dentry, inode);
|
|
|
|
bail:
|
2008-10-10 01:38:40 +08:00
|
|
|
if (status < 0 && did_quota)
|
2010-03-03 22:05:00 +08:00
|
|
|
dquot_free_space_nodirty(inode,
|
2008-10-10 01:38:40 +08:00
|
|
|
ocfs2_clusters_to_bytes(osb->sb, 1));
|
|
|
|
if (status < 0 && did_quota_inode)
|
2010-03-03 22:05:01 +08:00
|
|
|
dquot_free_inode(inode);
|
2022-10-17 21:02:27 +08:00
|
|
|
if (handle) {
|
|
|
|
if (status < 0 && fe)
|
|
|
|
ocfs2_set_links_count(fe, 0);
|
2006-10-10 07:48:10 +08:00
|
|
|
ocfs2_commit_trans(osb, handle);
|
2022-10-17 21:02:27 +08:00
|
|
|
}
|
2006-10-07 02:54:33 +08:00
|
|
|
|
2007-10-19 06:30:42 +08:00
|
|
|
ocfs2_inode_unlock(dir, 1);
|
2010-05-11 02:56:52 +08:00
|
|
|
if (did_block_signals)
|
|
|
|
ocfs2_unblock_signals(&oldset);
|
2006-10-07 02:54:33 +08:00
|
|
|
|
2008-10-08 05:25:16 +08:00
|
|
|
brelse(new_fe_bh);
|
|
|
|
brelse(parent_fe_bh);
|
2008-11-14 11:16:41 +08:00
|
|
|
kfree(si.value);
|
2008-11-13 07:43:34 +08:00
|
|
|
ocfs2_free_dir_lookup_result(&lookup);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (inode_ac)
|
|
|
|
ocfs2_free_alloc_context(inode_ac);
|
|
|
|
if (data_ac)
|
|
|
|
ocfs2_free_alloc_context(data_ac);
|
2008-11-14 11:16:41 +08:00
|
|
|
if (xattr_ac)
|
|
|
|
ocfs2_free_alloc_context(xattr_ac);
|
2008-11-14 11:15:44 +08:00
|
|
|
if ((status < 0) && inode) {
|
2014-06-24 04:22:09 +08:00
|
|
|
if (dl)
|
|
|
|
ocfs2_cleanup_add_entry_failure(osb, dentry, inode);
|
|
|
|
|
2010-04-22 16:11:25 +08:00
|
|
|
OCFS2_I(inode)->ip_flags |= OCFS2_INODE_SKIP_ORPHAN_DIR;
|
2008-11-14 11:15:44 +08:00
|
|
|
clear_nlink(inode);
|
2005-12-16 06:31:24 +08:00
|
|
|
iput(inode);
|
2008-11-14 11:15:44 +08:00
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2011-03-07 16:43:21 +08:00
|
|
|
if (status)
|
|
|
|
mlog_errno(status);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocfs2_blkno_stringify(u64 blkno, char *name)
|
|
|
|
{
|
|
|
|
int status, namelen;
|
|
|
|
|
2006-03-04 02:24:33 +08:00
|
|
|
namelen = snprintf(name, OCFS2_ORPHAN_NAMELEN + 1, "%016llx",
|
|
|
|
(long long)blkno);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (namelen <= 0) {
|
|
|
|
if (namelen)
|
|
|
|
status = namelen;
|
|
|
|
else
|
|
|
|
status = -EINVAL;
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
if (namelen != OCFS2_ORPHAN_NAMELEN) {
|
|
|
|
status = -EINVAL;
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_blkno_stringify(blkno, name, namelen);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
status = 0;
|
|
|
|
bail:
|
2011-03-07 16:43:21 +08:00
|
|
|
if (status < 0)
|
|
|
|
mlog_errno(status);
|
2005-12-16 06:31:24 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2010-08-14 06:15:18 +08:00
|
|
|
static int ocfs2_lookup_lock_orphan_dir(struct ocfs2_super *osb,
|
|
|
|
struct inode **ret_orphan_dir,
|
|
|
|
struct buffer_head **ret_orphan_dir_bh)
|
2005-12-16 06:31:24 +08:00
|
|
|
{
|
2006-10-06 09:12:57 +08:00
|
|
|
struct inode *orphan_dir_inode;
|
2005-12-16 06:31:24 +08:00
|
|
|
struct buffer_head *orphan_dir_bh = NULL;
|
2010-08-14 06:15:18 +08:00
|
|
|
int ret = 0;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
orphan_dir_inode = ocfs2_get_system_file_inode(osb,
|
|
|
|
ORPHAN_DIR_SYSTEM_INODE,
|
|
|
|
osb->slot_num);
|
|
|
|
if (!orphan_dir_inode) {
|
2010-08-14 06:15:18 +08:00
|
|
|
ret = -ENOENT;
|
|
|
|
mlog_errno(ret);
|
|
|
|
return ret;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_lock(orphan_dir_inode);
|
2006-10-06 09:12:57 +08:00
|
|
|
|
2010-08-14 06:15:18 +08:00
|
|
|
ret = ocfs2_inode_lock(orphan_dir_inode, &orphan_dir_bh, 1);
|
|
|
|
if (ret < 0) {
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_unlock(orphan_dir_inode);
|
2010-08-14 06:15:18 +08:00
|
|
|
iput(orphan_dir_inode);
|
|
|
|
|
|
|
|
mlog_errno(ret);
|
|
|
|
return ret;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2010-08-14 06:15:18 +08:00
|
|
|
*ret_orphan_dir = orphan_dir_inode;
|
|
|
|
*ret_orphan_dir_bh = orphan_dir_bh;
|
2006-10-06 09:12:57 +08:00
|
|
|
|
2010-08-14 06:15:18 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __ocfs2_prepare_orphan_dir(struct inode *orphan_dir_inode,
|
|
|
|
struct buffer_head *orphan_dir_bh,
|
|
|
|
u64 blkno,
|
|
|
|
char *name,
|
2015-02-17 07:59:54 +08:00
|
|
|
struct ocfs2_dir_lookup_result *lookup,
|
|
|
|
bool dio)
|
2010-08-14 06:15:18 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct ocfs2_super *osb = OCFS2_SB(orphan_dir_inode->i_sb);
|
2015-02-17 07:59:54 +08:00
|
|
|
int namelen = dio ?
|
|
|
|
(OCFS2_DIO_ORPHAN_PREFIX_LEN + OCFS2_ORPHAN_NAMELEN) :
|
|
|
|
OCFS2_ORPHAN_NAMELEN;
|
|
|
|
|
|
|
|
if (dio) {
|
|
|
|
ret = snprintf(name, OCFS2_DIO_ORPHAN_PREFIX_LEN + 1, "%s",
|
|
|
|
OCFS2_DIO_ORPHAN_PREFIX);
|
|
|
|
if (ret != OCFS2_DIO_ORPHAN_PREFIX_LEN) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
mlog_errno(ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2010-08-14 06:15:18 +08:00
|
|
|
|
2015-02-17 07:59:54 +08:00
|
|
|
ret = ocfs2_blkno_stringify(blkno,
|
|
|
|
name + OCFS2_DIO_ORPHAN_PREFIX_LEN);
|
|
|
|
} else
|
|
|
|
ret = ocfs2_blkno_stringify(blkno, name);
|
2010-08-14 06:15:18 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
mlog_errno(ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ocfs2_prepare_dir_for_insert(osb, orphan_dir_inode,
|
|
|
|
orphan_dir_bh, name,
|
2015-02-17 07:59:54 +08:00
|
|
|
namelen, lookup);
|
2010-08-14 06:15:18 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
mlog_errno(ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ocfs2_prepare_orphan_dir() - Prepare an orphan directory for
|
|
|
|
* insertion of an orphan.
|
|
|
|
* @osb: ocfs2 file system
|
|
|
|
* @ret_orphan_dir: Orphan dir inode - returned locked!
|
|
|
|
* @blkno: Actual block number of the inode to be inserted into orphan dir.
|
|
|
|
* @lookup: dir lookup result, to be passed back into functions like
|
|
|
|
* ocfs2_orphan_add
|
|
|
|
*
|
|
|
|
* Returns zero on success and the ret_orphan_dir, name and lookup
|
|
|
|
* fields will be populated.
|
|
|
|
*
|
|
|
|
* Returns non-zero on failure.
|
|
|
|
*/
|
|
|
|
static int ocfs2_prepare_orphan_dir(struct ocfs2_super *osb,
|
|
|
|
struct inode **ret_orphan_dir,
|
|
|
|
u64 blkno,
|
|
|
|
char *name,
|
2015-02-17 07:59:54 +08:00
|
|
|
struct ocfs2_dir_lookup_result *lookup,
|
|
|
|
bool dio)
|
2010-08-14 06:15:18 +08:00
|
|
|
{
|
|
|
|
struct inode *orphan_dir_inode = NULL;
|
|
|
|
struct buffer_head *orphan_dir_bh = NULL;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
ret = ocfs2_lookup_lock_orphan_dir(osb, &orphan_dir_inode,
|
|
|
|
&orphan_dir_bh);
|
|
|
|
if (ret < 0) {
|
|
|
|
mlog_errno(ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = __ocfs2_prepare_orphan_dir(orphan_dir_inode, orphan_dir_bh,
|
2015-02-17 07:59:54 +08:00
|
|
|
blkno, name, lookup, dio);
|
2010-08-14 06:15:18 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
mlog_errno(ret);
|
|
|
|
goto out;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
2006-10-06 09:12:57 +08:00
|
|
|
*ret_orphan_dir = orphan_dir_inode;
|
|
|
|
|
2010-08-14 06:15:18 +08:00
|
|
|
out:
|
|
|
|
brelse(orphan_dir_bh);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
ocfs2_inode_unlock(orphan_dir_inode, 1);
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_unlock(orphan_dir_inode);
|
2005-12-16 06:31:24 +08:00
|
|
|
iput(orphan_dir_inode);
|
2006-10-06 09:12:57 +08:00
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2011-03-07 16:43:21 +08:00
|
|
|
if (ret)
|
|
|
|
mlog_errno(ret);
|
2010-08-14 06:15:18 +08:00
|
|
|
return ret;
|
2005-12-16 06:31:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ocfs2_orphan_add(struct ocfs2_super *osb,
|
2006-10-10 09:11:45 +08:00
|
|
|
handle_t *handle,
|
2005-12-16 06:31:24 +08:00
|
|
|
struct inode *inode,
|
2010-03-19 09:21:09 +08:00
|
|
|
struct buffer_head *fe_bh,
|
2005-12-16 06:31:24 +08:00
|
|
|
char *name,
|
2008-11-13 07:43:34 +08:00
|
|
|
struct ocfs2_dir_lookup_result *lookup,
|
2015-02-17 07:59:54 +08:00
|
|
|
struct inode *orphan_dir_inode,
|
|
|
|
bool dio)
|
2005-12-16 06:31:24 +08:00
|
|
|
{
|
|
|
|
struct buffer_head *orphan_dir_bh = NULL;
|
|
|
|
int status = 0;
|
|
|
|
struct ocfs2_dinode *orphan_fe;
|
2010-03-19 09:21:09 +08:00
|
|
|
struct ocfs2_dinode *fe = (struct ocfs2_dinode *) fe_bh->b_data;
|
2015-02-17 07:59:54 +08:00
|
|
|
int namelen = dio ?
|
|
|
|
(OCFS2_DIO_ORPHAN_PREFIX_LEN + OCFS2_ORPHAN_NAMELEN) :
|
|
|
|
OCFS2_ORPHAN_NAMELEN;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_orphan_add_begin(
|
|
|
|
(unsigned long long)OCFS2_I(inode)->ip_blkno);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2008-11-14 06:49:11 +08:00
|
|
|
status = ocfs2_read_inode_block(orphan_dir_inode, &orphan_dir_bh);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2009-02-13 08:41:25 +08:00
|
|
|
status = ocfs2_journal_access_di(handle,
|
|
|
|
INODE_CACHE(orphan_dir_inode),
|
|
|
|
orphan_dir_bh,
|
2008-10-18 10:25:01 +08:00
|
|
|
OCFS2_JOURNAL_ACCESS_WRITE);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2013-07-04 06:00:58 +08:00
|
|
|
/*
|
|
|
|
* We're going to journal the change of i_flags and i_orphaned_slot.
|
|
|
|
* It's safe anyway, though some callers may duplicate the journaling.
|
|
|
|
* Journaling within the func just make the logic look more
|
|
|
|
* straightforward.
|
|
|
|
*/
|
|
|
|
status = ocfs2_journal_access_di(handle,
|
|
|
|
INODE_CACHE(inode),
|
|
|
|
fe_bh,
|
|
|
|
OCFS2_JOURNAL_ACCESS_WRITE);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
/* we're a cluster, and nlink can change on disk from
|
|
|
|
* underneath us... */
|
|
|
|
orphan_fe = (struct ocfs2_dinode *) orphan_dir_bh->b_data;
|
|
|
|
if (S_ISDIR(inode->i_mode))
|
2008-11-21 09:54:57 +08:00
|
|
|
ocfs2_add_links_count(orphan_fe, 1);
|
2011-10-28 20:13:29 +08:00
|
|
|
set_nlink(orphan_dir_inode, ocfs2_read_links_count(orphan_fe));
|
2010-03-20 05:13:52 +08:00
|
|
|
ocfs2_journal_dirty(handle, orphan_dir_bh);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
status = __ocfs2_add_entry(handle, orphan_dir_inode, name,
|
2015-02-17 07:59:54 +08:00
|
|
|
namelen, inode,
|
2005-12-16 06:31:24 +08:00
|
|
|
OCFS2_I(inode)->ip_blkno,
|
2008-11-13 07:43:34 +08:00
|
|
|
orphan_dir_bh, lookup);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
2013-07-04 06:00:58 +08:00
|
|
|
goto rollback;
|
2010-03-19 09:21:09 +08:00
|
|
|
}
|
|
|
|
|
2015-02-17 07:59:54 +08:00
|
|
|
if (dio) {
|
|
|
|
/* Update flag OCFS2_DIO_ORPHANED_FL and record the orphan
|
|
|
|
* slot.
|
|
|
|
*/
|
|
|
|
fe->i_flags |= cpu_to_le32(OCFS2_DIO_ORPHANED_FL);
|
|
|
|
fe->i_dio_orphaned_slot = cpu_to_le16(osb->slot_num);
|
|
|
|
} else {
|
|
|
|
fe->i_flags |= cpu_to_le32(OCFS2_ORPHANED_FL);
|
|
|
|
OCFS2_I(inode)->ip_flags &= ~OCFS2_INODE_SKIP_ORPHAN_DIR;
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2015-02-17 07:59:54 +08:00
|
|
|
/* Record which orphan dir our inode now resides
|
|
|
|
* in. delete_inode will use this to determine which orphan
|
|
|
|
* dir to lock. */
|
|
|
|
fe->i_orphaned_slot = cpu_to_le16(osb->slot_num);
|
|
|
|
}
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2010-03-19 09:21:09 +08:00
|
|
|
ocfs2_journal_dirty(handle, fe_bh);
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_orphan_add_end((unsigned long long)OCFS2_I(inode)->ip_blkno,
|
|
|
|
osb->slot_num);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2013-07-04 06:00:58 +08:00
|
|
|
rollback:
|
|
|
|
if (status < 0) {
|
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
ocfs2_add_links_count(orphan_fe, -1);
|
|
|
|
set_nlink(orphan_dir_inode, ocfs2_read_links_count(orphan_fe));
|
|
|
|
}
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
leave:
|
2008-10-08 05:25:16 +08:00
|
|
|
brelse(orphan_dir_bh);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unlike orphan_add, we expect the orphan dir to already be locked here. */
|
|
|
|
int ocfs2_orphan_del(struct ocfs2_super *osb,
|
2006-10-10 09:11:45 +08:00
|
|
|
handle_t *handle,
|
2005-12-16 06:31:24 +08:00
|
|
|
struct inode *orphan_dir_inode,
|
|
|
|
struct inode *inode,
|
2015-02-17 07:59:54 +08:00
|
|
|
struct buffer_head *orphan_dir_bh,
|
|
|
|
bool dio)
|
2005-12-16 06:31:24 +08:00
|
|
|
{
|
2018-06-08 08:04:55 +08:00
|
|
|
char name[OCFS2_DIO_ORPHAN_PREFIX_LEN + OCFS2_ORPHAN_NAMELEN + 1];
|
2005-12-16 06:31:24 +08:00
|
|
|
struct ocfs2_dinode *orphan_fe;
|
|
|
|
int status = 0;
|
2008-11-13 07:43:34 +08:00
|
|
|
struct ocfs2_dir_lookup_result lookup = { NULL, };
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2015-02-17 07:59:54 +08:00
|
|
|
if (dio) {
|
|
|
|
status = snprintf(name, OCFS2_DIO_ORPHAN_PREFIX_LEN + 1, "%s",
|
|
|
|
OCFS2_DIO_ORPHAN_PREFIX);
|
|
|
|
if (status != OCFS2_DIO_ORPHAN_PREFIX_LEN) {
|
|
|
|
status = -EINVAL;
|
|
|
|
mlog_errno(status);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_blkno_stringify(OCFS2_I(inode)->ip_blkno,
|
|
|
|
name + OCFS2_DIO_ORPHAN_PREFIX_LEN);
|
|
|
|
} else
|
|
|
|
status = ocfs2_blkno_stringify(OCFS2_I(inode)->ip_blkno, name);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_orphan_del(
|
|
|
|
(unsigned long long)OCFS2_I(orphan_dir_inode)->ip_blkno,
|
2015-04-15 06:43:22 +08:00
|
|
|
name, strlen(name));
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2016-01-15 07:17:44 +08:00
|
|
|
status = ocfs2_journal_access_di(handle,
|
|
|
|
INODE_CACHE(orphan_dir_inode),
|
|
|
|
orphan_dir_bh,
|
|
|
|
OCFS2_JOURNAL_ACCESS_WRITE);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2005-12-16 06:31:24 +08:00
|
|
|
/* find it's spot in the orphan directory */
|
2015-04-15 06:43:22 +08:00
|
|
|
status = ocfs2_find_entry(name, strlen(name), orphan_dir_inode,
|
2008-11-13 07:43:34 +08:00
|
|
|
&lookup);
|
|
|
|
if (status) {
|
2005-12-16 06:31:24 +08:00
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove it from the orphan directory */
|
2008-11-13 07:43:34 +08:00
|
|
|
status = ocfs2_delete_entry(handle, orphan_dir_inode, &lookup);
|
2005-12-16 06:31:24 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do the i_nlink dance! :) */
|
|
|
|
orphan_fe = (struct ocfs2_dinode *) orphan_dir_bh->b_data;
|
|
|
|
if (S_ISDIR(inode->i_mode))
|
2008-11-21 09:54:57 +08:00
|
|
|
ocfs2_add_links_count(orphan_fe, -1);
|
2011-10-28 20:13:29 +08:00
|
|
|
set_nlink(orphan_dir_inode, ocfs2_read_links_count(orphan_fe));
|
2010-03-20 05:13:52 +08:00
|
|
|
ocfs2_journal_dirty(handle, orphan_dir_bh);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
|
|
|
leave:
|
2008-11-13 07:43:34 +08:00
|
|
|
ocfs2_free_dir_lookup_result(&lookup);
|
2005-12-16 06:31:24 +08:00
|
|
|
|
2011-03-07 16:43:21 +08:00
|
|
|
if (status)
|
|
|
|
mlog_errno(status);
|
2005-12-16 06:31:24 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
ocfs2: Fix orphan add in ocfs2_create_inode_in_orphan
ocfs2_create_inode_in_orphan() is used by reflink to create the newly
reflinked inode simultaneously in the orphan dir. This allows us to easily
handle partially-reflinked files during recovery cleanup.
We have a problem though - the orphan dir stringifies inode # to determine
a unique name under which the orphan entry dirent can be created. Since
ocfs2_create_inode_in_orphan() needs the space allocated in the orphan dir
before it can allocate the inode, we currently call into the orphan code:
/*
* We give the orphan dir the root blkno to fake an orphan name,
* and allocate enough space for our insertion.
*/
status = ocfs2_prepare_orphan_dir(osb, &orphan_dir,
osb->root_blkno,
orphan_name, &orphan_insert);
Using osb->root_blkno might work fine on unindexed directories, but the
orphan dir can have an index. When it has that index, the above code fails
to allocate the proper index entry. Later, when we try to remove the file
from the orphan dir (using the actual inode #), the reflink operation will
fail.
To fix this, I created a function ocfs2_alloc_orphaned_file() which uses the
newly split out orphan and inode alloc code to figure out what the inode
block number will be (once allocated) and then prepare the orphan dir from
that data.
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Signed-off-by: Tao Ma <tao.ma@oracle.com>
2010-08-14 06:15:19 +08:00
|
|
|
/**
|
2011-03-31 09:57:33 +08:00
|
|
|
* ocfs2_prep_new_orphaned_file() - Prepare the orphan dir to receive a newly
|
ocfs2: Fix orphan add in ocfs2_create_inode_in_orphan
ocfs2_create_inode_in_orphan() is used by reflink to create the newly
reflinked inode simultaneously in the orphan dir. This allows us to easily
handle partially-reflinked files during recovery cleanup.
We have a problem though - the orphan dir stringifies inode # to determine
a unique name under which the orphan entry dirent can be created. Since
ocfs2_create_inode_in_orphan() needs the space allocated in the orphan dir
before it can allocate the inode, we currently call into the orphan code:
/*
* We give the orphan dir the root blkno to fake an orphan name,
* and allocate enough space for our insertion.
*/
status = ocfs2_prepare_orphan_dir(osb, &orphan_dir,
osb->root_blkno,
orphan_name, &orphan_insert);
Using osb->root_blkno might work fine on unindexed directories, but the
orphan dir can have an index. When it has that index, the above code fails
to allocate the proper index entry. Later, when we try to remove the file
from the orphan dir (using the actual inode #), the reflink operation will
fail.
To fix this, I created a function ocfs2_alloc_orphaned_file() which uses the
newly split out orphan and inode alloc code to figure out what the inode
block number will be (once allocated) and then prepare the orphan dir from
that data.
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Signed-off-by: Tao Ma <tao.ma@oracle.com>
2010-08-14 06:15:19 +08:00
|
|
|
* allocated file. This is different from the typical 'add to orphan dir'
|
|
|
|
* operation in that the inode does not yet exist. This is a problem because
|
|
|
|
* the orphan dir stringifies the inode block number to come up with it's
|
|
|
|
* dirent. Obviously if the inode does not yet exist we have a chicken and egg
|
|
|
|
* problem. This function works around it by calling deeper into the orphan
|
|
|
|
* and suballoc code than other callers. Use this only by necessity.
|
|
|
|
* @dir: The directory which this inode will ultimately wind up under - not the
|
|
|
|
* orphan dir!
|
|
|
|
* @dir_bh: buffer_head the @dir inode block
|
|
|
|
* @orphan_name: string of length (CFS2_ORPHAN_NAMELEN + 1). Will be filled
|
|
|
|
* with the string to be used for orphan dirent. Pass back to the orphan dir
|
|
|
|
* code.
|
|
|
|
* @ret_orphan_dir: orphan dir inode returned to be passed back into orphan
|
|
|
|
* dir code.
|
|
|
|
* @ret_di_blkno: block number where the new inode will be allocated.
|
|
|
|
* @orphan_insert: Dir insert context to be passed back into orphan dir code.
|
|
|
|
* @ret_inode_ac: Inode alloc context to be passed back to the allocator.
|
|
|
|
*
|
|
|
|
* Returns zero on success and the ret_orphan_dir, name and lookup
|
|
|
|
* fields will be populated.
|
|
|
|
*
|
|
|
|
* Returns non-zero on failure.
|
|
|
|
*/
|
|
|
|
static int ocfs2_prep_new_orphaned_file(struct inode *dir,
|
|
|
|
struct buffer_head *dir_bh,
|
|
|
|
char *orphan_name,
|
|
|
|
struct inode **ret_orphan_dir,
|
|
|
|
u64 *ret_di_blkno,
|
|
|
|
struct ocfs2_dir_lookup_result *orphan_insert,
|
|
|
|
struct ocfs2_alloc_context **ret_inode_ac)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
u64 di_blkno;
|
|
|
|
struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
|
|
|
|
struct inode *orphan_dir = NULL;
|
|
|
|
struct buffer_head *orphan_dir_bh = NULL;
|
|
|
|
struct ocfs2_alloc_context *inode_ac = NULL;
|
|
|
|
|
|
|
|
ret = ocfs2_lookup_lock_orphan_dir(osb, &orphan_dir, &orphan_dir_bh);
|
|
|
|
if (ret < 0) {
|
|
|
|
mlog_errno(ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reserve an inode spot */
|
|
|
|
ret = ocfs2_reserve_new_inode(osb, &inode_ac);
|
|
|
|
if (ret < 0) {
|
|
|
|
if (ret != -ENOSPC)
|
|
|
|
mlog_errno(ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ocfs2_find_new_inode_loc(dir, dir_bh, inode_ac,
|
|
|
|
&di_blkno);
|
|
|
|
if (ret) {
|
|
|
|
mlog_errno(ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = __ocfs2_prepare_orphan_dir(orphan_dir, orphan_dir_bh,
|
2015-02-17 07:59:54 +08:00
|
|
|
di_blkno, orphan_name, orphan_insert,
|
|
|
|
false);
|
ocfs2: Fix orphan add in ocfs2_create_inode_in_orphan
ocfs2_create_inode_in_orphan() is used by reflink to create the newly
reflinked inode simultaneously in the orphan dir. This allows us to easily
handle partially-reflinked files during recovery cleanup.
We have a problem though - the orphan dir stringifies inode # to determine
a unique name under which the orphan entry dirent can be created. Since
ocfs2_create_inode_in_orphan() needs the space allocated in the orphan dir
before it can allocate the inode, we currently call into the orphan code:
/*
* We give the orphan dir the root blkno to fake an orphan name,
* and allocate enough space for our insertion.
*/
status = ocfs2_prepare_orphan_dir(osb, &orphan_dir,
osb->root_blkno,
orphan_name, &orphan_insert);
Using osb->root_blkno might work fine on unindexed directories, but the
orphan dir can have an index. When it has that index, the above code fails
to allocate the proper index entry. Later, when we try to remove the file
from the orphan dir (using the actual inode #), the reflink operation will
fail.
To fix this, I created a function ocfs2_alloc_orphaned_file() which uses the
newly split out orphan and inode alloc code to figure out what the inode
block number will be (once allocated) and then prepare the orphan dir from
that data.
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Signed-off-by: Tao Ma <tao.ma@oracle.com>
2010-08-14 06:15:19 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
mlog_errno(ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (ret == 0) {
|
|
|
|
*ret_orphan_dir = orphan_dir;
|
|
|
|
*ret_di_blkno = di_blkno;
|
|
|
|
*ret_inode_ac = inode_ac;
|
|
|
|
/*
|
|
|
|
* orphan_name and orphan_insert are already up to
|
|
|
|
* date via prepare_orphan_dir
|
|
|
|
*/
|
|
|
|
} else {
|
|
|
|
/* Unroll reserve_new_inode* */
|
|
|
|
if (inode_ac)
|
|
|
|
ocfs2_free_alloc_context(inode_ac);
|
|
|
|
|
|
|
|
/* Unroll orphan dir locking */
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_unlock(orphan_dir);
|
ocfs2: Fix orphan add in ocfs2_create_inode_in_orphan
ocfs2_create_inode_in_orphan() is used by reflink to create the newly
reflinked inode simultaneously in the orphan dir. This allows us to easily
handle partially-reflinked files during recovery cleanup.
We have a problem though - the orphan dir stringifies inode # to determine
a unique name under which the orphan entry dirent can be created. Since
ocfs2_create_inode_in_orphan() needs the space allocated in the orphan dir
before it can allocate the inode, we currently call into the orphan code:
/*
* We give the orphan dir the root blkno to fake an orphan name,
* and allocate enough space for our insertion.
*/
status = ocfs2_prepare_orphan_dir(osb, &orphan_dir,
osb->root_blkno,
orphan_name, &orphan_insert);
Using osb->root_blkno might work fine on unindexed directories, but the
orphan dir can have an index. When it has that index, the above code fails
to allocate the proper index entry. Later, when we try to remove the file
from the orphan dir (using the actual inode #), the reflink operation will
fail.
To fix this, I created a function ocfs2_alloc_orphaned_file() which uses the
newly split out orphan and inode alloc code to figure out what the inode
block number will be (once allocated) and then prepare the orphan dir from
that data.
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Signed-off-by: Tao Ma <tao.ma@oracle.com>
2010-08-14 06:15:19 +08:00
|
|
|
ocfs2_inode_unlock(orphan_dir, 1);
|
|
|
|
iput(orphan_dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
brelse(orphan_dir_bh);
|
|
|
|
|
2013-06-13 05:04:41 +08:00
|
|
|
return ret;
|
ocfs2: Fix orphan add in ocfs2_create_inode_in_orphan
ocfs2_create_inode_in_orphan() is used by reflink to create the newly
reflinked inode simultaneously in the orphan dir. This allows us to easily
handle partially-reflinked files during recovery cleanup.
We have a problem though - the orphan dir stringifies inode # to determine
a unique name under which the orphan entry dirent can be created. Since
ocfs2_create_inode_in_orphan() needs the space allocated in the orphan dir
before it can allocate the inode, we currently call into the orphan code:
/*
* We give the orphan dir the root blkno to fake an orphan name,
* and allocate enough space for our insertion.
*/
status = ocfs2_prepare_orphan_dir(osb, &orphan_dir,
osb->root_blkno,
orphan_name, &orphan_insert);
Using osb->root_blkno might work fine on unindexed directories, but the
orphan dir can have an index. When it has that index, the above code fails
to allocate the proper index entry. Later, when we try to remove the file
from the orphan dir (using the actual inode #), the reflink operation will
fail.
To fix this, I created a function ocfs2_alloc_orphaned_file() which uses the
newly split out orphan and inode alloc code to figure out what the inode
block number will be (once allocated) and then prepare the orphan dir from
that data.
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Signed-off-by: Tao Ma <tao.ma@oracle.com>
2010-08-14 06:15:19 +08:00
|
|
|
}
|
|
|
|
|
2009-08-18 11:44:14 +08:00
|
|
|
int ocfs2_create_inode_in_orphan(struct inode *dir,
|
|
|
|
int mode,
|
|
|
|
struct inode **new_inode)
|
|
|
|
{
|
|
|
|
int status, did_quota_inode = 0;
|
|
|
|
struct inode *inode = NULL;
|
|
|
|
struct inode *orphan_dir = NULL;
|
|
|
|
struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
|
|
|
|
handle_t *handle = NULL;
|
|
|
|
char orphan_name[OCFS2_ORPHAN_NAMELEN + 1];
|
|
|
|
struct buffer_head *parent_di_bh = NULL;
|
|
|
|
struct buffer_head *new_di_bh = NULL;
|
|
|
|
struct ocfs2_alloc_context *inode_ac = NULL;
|
|
|
|
struct ocfs2_dir_lookup_result orphan_insert = { NULL, };
|
treewide: Remove uninitialized_var() usage
Using uninitialized_var() is dangerous as it papers over real bugs[1]
(or can in the future), and suppresses unrelated compiler warnings
(e.g. "unused variable"). If the compiler thinks it is uninitialized,
either simply initialize the variable or make compiler changes.
In preparation for removing[2] the[3] macro[4], remove all remaining
needless uses with the following script:
git grep '\buninitialized_var\b' | cut -d: -f1 | sort -u | \
xargs perl -pi -e \
's/\buninitialized_var\(([^\)]+)\)/\1/g;
s:\s*/\* (GCC be quiet|to make compiler happy) \*/$::g;'
drivers/video/fbdev/riva/riva_hw.c was manually tweaked to avoid
pathological white-space.
No outstanding warnings were found building allmodconfig with GCC 9.3.0
for x86_64, i386, arm64, arm, powerpc, powerpc64le, s390x, mips, sparc64,
alpha, and m68k.
[1] https://lore.kernel.org/lkml/20200603174714.192027-1-glider@google.com/
[2] https://lore.kernel.org/lkml/CA+55aFw+Vbj0i=1TGqCR5vQkCzWJ0QxK6CernOU6eedsudAixw@mail.gmail.com/
[3] https://lore.kernel.org/lkml/CA+55aFwgbgqhbp1fkxvRKEpzyR5J8n1vKT1VZdz9knmPuXhOeg@mail.gmail.com/
[4] https://lore.kernel.org/lkml/CA+55aFz2500WfbKXAx8s67wrm9=yVJu65TpLgN_ybYNv0VEOKA@mail.gmail.com/
Reviewed-by: Leon Romanovsky <leonro@mellanox.com> # drivers/infiniband and mlx4/mlx5
Acked-by: Jason Gunthorpe <jgg@mellanox.com> # IB
Acked-by: Kalle Valo <kvalo@codeaurora.org> # wireless drivers
Reviewed-by: Chao Yu <yuchao0@huawei.com> # erofs
Signed-off-by: Kees Cook <keescook@chromium.org>
2020-06-04 04:09:38 +08:00
|
|
|
u64 di_blkno, suballoc_loc;
|
ocfs2: Fix orphan add in ocfs2_create_inode_in_orphan
ocfs2_create_inode_in_orphan() is used by reflink to create the newly
reflinked inode simultaneously in the orphan dir. This allows us to easily
handle partially-reflinked files during recovery cleanup.
We have a problem though - the orphan dir stringifies inode # to determine
a unique name under which the orphan entry dirent can be created. Since
ocfs2_create_inode_in_orphan() needs the space allocated in the orphan dir
before it can allocate the inode, we currently call into the orphan code:
/*
* We give the orphan dir the root blkno to fake an orphan name,
* and allocate enough space for our insertion.
*/
status = ocfs2_prepare_orphan_dir(osb, &orphan_dir,
osb->root_blkno,
orphan_name, &orphan_insert);
Using osb->root_blkno might work fine on unindexed directories, but the
orphan dir can have an index. When it has that index, the above code fails
to allocate the proper index entry. Later, when we try to remove the file
from the orphan dir (using the actual inode #), the reflink operation will
fail.
To fix this, I created a function ocfs2_alloc_orphaned_file() which uses the
newly split out orphan and inode alloc code to figure out what the inode
block number will be (once allocated) and then prepare the orphan dir from
that data.
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Signed-off-by: Tao Ma <tao.ma@oracle.com>
2010-08-14 06:15:19 +08:00
|
|
|
u16 suballoc_bit;
|
2009-08-18 11:44:14 +08:00
|
|
|
|
|
|
|
status = ocfs2_inode_lock(dir, &parent_di_bh, 1);
|
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOENT)
|
|
|
|
mlog_errno(status);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
ocfs2: Fix orphan add in ocfs2_create_inode_in_orphan
ocfs2_create_inode_in_orphan() is used by reflink to create the newly
reflinked inode simultaneously in the orphan dir. This allows us to easily
handle partially-reflinked files during recovery cleanup.
We have a problem though - the orphan dir stringifies inode # to determine
a unique name under which the orphan entry dirent can be created. Since
ocfs2_create_inode_in_orphan() needs the space allocated in the orphan dir
before it can allocate the inode, we currently call into the orphan code:
/*
* We give the orphan dir the root blkno to fake an orphan name,
* and allocate enough space for our insertion.
*/
status = ocfs2_prepare_orphan_dir(osb, &orphan_dir,
osb->root_blkno,
orphan_name, &orphan_insert);
Using osb->root_blkno might work fine on unindexed directories, but the
orphan dir can have an index. When it has that index, the above code fails
to allocate the proper index entry. Later, when we try to remove the file
from the orphan dir (using the actual inode #), the reflink operation will
fail.
To fix this, I created a function ocfs2_alloc_orphaned_file() which uses the
newly split out orphan and inode alloc code to figure out what the inode
block number will be (once allocated) and then prepare the orphan dir from
that data.
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Signed-off-by: Tao Ma <tao.ma@oracle.com>
2010-08-14 06:15:19 +08:00
|
|
|
status = ocfs2_prep_new_orphaned_file(dir, parent_di_bh,
|
|
|
|
orphan_name, &orphan_dir,
|
|
|
|
&di_blkno, &orphan_insert, &inode_ac);
|
2009-08-18 11:44:14 +08:00
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOSPC)
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
inode = ocfs2_get_init_inode(dir, mode);
|
2015-07-14 19:36:02 +08:00
|
|
|
if (IS_ERR(inode)) {
|
|
|
|
status = PTR_ERR(inode);
|
|
|
|
inode = NULL;
|
2009-08-18 11:44:14 +08:00
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle = ocfs2_start_trans(osb, ocfs2_mknod_credits(osb->sb, 0, 0));
|
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
status = PTR_ERR(handle);
|
|
|
|
handle = NULL;
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2010-03-03 22:05:01 +08:00
|
|
|
status = dquot_alloc_inode(inode);
|
|
|
|
if (status)
|
2009-08-18 11:44:14 +08:00
|
|
|
goto leave;
|
|
|
|
did_quota_inode = 1;
|
|
|
|
|
ocfs2: Fix orphan add in ocfs2_create_inode_in_orphan
ocfs2_create_inode_in_orphan() is used by reflink to create the newly
reflinked inode simultaneously in the orphan dir. This allows us to easily
handle partially-reflinked files during recovery cleanup.
We have a problem though - the orphan dir stringifies inode # to determine
a unique name under which the orphan entry dirent can be created. Since
ocfs2_create_inode_in_orphan() needs the space allocated in the orphan dir
before it can allocate the inode, we currently call into the orphan code:
/*
* We give the orphan dir the root blkno to fake an orphan name,
* and allocate enough space for our insertion.
*/
status = ocfs2_prepare_orphan_dir(osb, &orphan_dir,
osb->root_blkno,
orphan_name, &orphan_insert);
Using osb->root_blkno might work fine on unindexed directories, but the
orphan dir can have an index. When it has that index, the above code fails
to allocate the proper index entry. Later, when we try to remove the file
from the orphan dir (using the actual inode #), the reflink operation will
fail.
To fix this, I created a function ocfs2_alloc_orphaned_file() which uses the
newly split out orphan and inode alloc code to figure out what the inode
block number will be (once allocated) and then prepare the orphan dir from
that data.
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Signed-off-by: Tao Ma <tao.ma@oracle.com>
2010-08-14 06:15:19 +08:00
|
|
|
status = ocfs2_claim_new_inode_at_loc(handle, dir, inode_ac,
|
|
|
|
&suballoc_loc,
|
|
|
|
&suballoc_bit, di_blkno);
|
2009-08-18 11:44:14 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2011-10-28 20:13:28 +08:00
|
|
|
clear_nlink(inode);
|
ocfs2: Fix orphan add in ocfs2_create_inode_in_orphan
ocfs2_create_inode_in_orphan() is used by reflink to create the newly
reflinked inode simultaneously in the orphan dir. This allows us to easily
handle partially-reflinked files during recovery cleanup.
We have a problem though - the orphan dir stringifies inode # to determine
a unique name under which the orphan entry dirent can be created. Since
ocfs2_create_inode_in_orphan() needs the space allocated in the orphan dir
before it can allocate the inode, we currently call into the orphan code:
/*
* We give the orphan dir the root blkno to fake an orphan name,
* and allocate enough space for our insertion.
*/
status = ocfs2_prepare_orphan_dir(osb, &orphan_dir,
osb->root_blkno,
orphan_name, &orphan_insert);
Using osb->root_blkno might work fine on unindexed directories, but the
orphan dir can have an index. When it has that index, the above code fails
to allocate the proper index entry. Later, when we try to remove the file
from the orphan dir (using the actual inode #), the reflink operation will
fail.
To fix this, I created a function ocfs2_alloc_orphaned_file() which uses the
newly split out orphan and inode alloc code to figure out what the inode
block number will be (once allocated) and then prepare the orphan dir from
that data.
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Signed-off-by: Tao Ma <tao.ma@oracle.com>
2010-08-14 06:15:19 +08:00
|
|
|
/* do the real work now. */
|
|
|
|
status = __ocfs2_mknod_locked(dir, inode,
|
|
|
|
0, &new_di_bh, parent_di_bh, handle,
|
|
|
|
inode_ac, di_blkno, suballoc_loc,
|
|
|
|
suballoc_bit);
|
2009-08-18 11:44:14 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2010-03-19 09:21:09 +08:00
|
|
|
status = ocfs2_orphan_add(osb, handle, inode, new_di_bh, orphan_name,
|
2015-02-17 07:59:54 +08:00
|
|
|
&orphan_insert, orphan_dir, false);
|
2009-08-18 11:44:14 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get open lock so that only nodes can't remove it from orphan dir. */
|
|
|
|
status = ocfs2_open_lock(inode);
|
|
|
|
if (status < 0)
|
|
|
|
mlog_errno(status);
|
|
|
|
|
2009-12-18 10:24:54 +08:00
|
|
|
insert_inode_hash(inode);
|
2009-08-18 11:44:14 +08:00
|
|
|
leave:
|
|
|
|
if (status < 0 && did_quota_inode)
|
2010-03-03 22:05:01 +08:00
|
|
|
dquot_free_inode(inode);
|
2009-08-18 11:44:14 +08:00
|
|
|
if (handle)
|
|
|
|
ocfs2_commit_trans(osb, handle);
|
|
|
|
|
|
|
|
if (orphan_dir) {
|
|
|
|
/* This was locked for us in ocfs2_prepare_orphan_dir() */
|
|
|
|
ocfs2_inode_unlock(orphan_dir, 1);
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_unlock(orphan_dir);
|
2009-08-18 11:44:14 +08:00
|
|
|
iput(orphan_dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((status < 0) && inode) {
|
|
|
|
clear_nlink(inode);
|
|
|
|
iput(inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inode_ac)
|
|
|
|
ocfs2_free_alloc_context(inode_ac);
|
|
|
|
|
|
|
|
brelse(new_di_bh);
|
|
|
|
|
|
|
|
if (!status)
|
|
|
|
*new_inode = inode;
|
|
|
|
|
|
|
|
ocfs2_free_dir_lookup_result(&orphan_insert);
|
|
|
|
|
|
|
|
ocfs2_inode_unlock(dir, 1);
|
|
|
|
brelse(parent_di_bh);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2015-02-17 07:59:54 +08:00
|
|
|
int ocfs2_add_inode_to_orphan(struct ocfs2_super *osb,
|
|
|
|
struct inode *inode)
|
|
|
|
{
|
|
|
|
char orphan_name[OCFS2_DIO_ORPHAN_PREFIX_LEN + OCFS2_ORPHAN_NAMELEN + 1];
|
|
|
|
struct inode *orphan_dir_inode = NULL;
|
|
|
|
struct ocfs2_dir_lookup_result orphan_insert = { NULL, };
|
|
|
|
struct buffer_head *di_bh = NULL;
|
|
|
|
int status = 0;
|
|
|
|
handle_t *handle = NULL;
|
2015-02-17 08:00:12 +08:00
|
|
|
struct ocfs2_dinode *di = NULL;
|
2015-02-17 07:59:54 +08:00
|
|
|
|
|
|
|
status = ocfs2_inode_lock(inode, &di_bh, 1);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2015-02-17 08:00:12 +08:00
|
|
|
di = (struct ocfs2_dinode *) di_bh->b_data;
|
|
|
|
/*
|
|
|
|
* Another append dio crashed?
|
2015-09-05 06:43:37 +08:00
|
|
|
* If so, manually recover it first.
|
2015-02-17 08:00:12 +08:00
|
|
|
*/
|
|
|
|
if (unlikely(di->i_flags & cpu_to_le32(OCFS2_DIO_ORPHANED_FL))) {
|
2015-09-05 06:43:37 +08:00
|
|
|
status = ocfs2_truncate_file(inode, di_bh, i_size_read(inode));
|
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOSPC)
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail_unlock_inode;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_del_inode_from_orphan(osb, inode, di_bh, 0, 0);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail_unlock_inode;
|
|
|
|
}
|
2015-02-17 08:00:12 +08:00
|
|
|
}
|
|
|
|
|
2015-02-17 07:59:54 +08:00
|
|
|
status = ocfs2_prepare_orphan_dir(osb, &orphan_dir_inode,
|
|
|
|
OCFS2_I(inode)->ip_blkno,
|
|
|
|
orphan_name,
|
|
|
|
&orphan_insert,
|
|
|
|
true);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail_unlock_inode;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle = ocfs2_start_trans(osb,
|
|
|
|
OCFS2_INODE_ADD_TO_ORPHAN_CREDITS);
|
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
status = PTR_ERR(handle);
|
|
|
|
goto bail_unlock_orphan;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_orphan_add(osb, handle, inode, di_bh, orphan_name,
|
|
|
|
&orphan_insert, orphan_dir_inode, true);
|
|
|
|
if (status)
|
|
|
|
mlog_errno(status);
|
|
|
|
|
|
|
|
ocfs2_commit_trans(osb, handle);
|
|
|
|
|
|
|
|
bail_unlock_orphan:
|
|
|
|
ocfs2_inode_unlock(orphan_dir_inode, 1);
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_unlock(orphan_dir_inode);
|
2015-02-17 07:59:54 +08:00
|
|
|
iput(orphan_dir_inode);
|
|
|
|
|
|
|
|
ocfs2_free_dir_lookup_result(&orphan_insert);
|
|
|
|
|
|
|
|
bail_unlock_inode:
|
|
|
|
ocfs2_inode_unlock(inode, 1);
|
|
|
|
brelse(di_bh);
|
|
|
|
|
|
|
|
bail:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ocfs2_del_inode_from_orphan(struct ocfs2_super *osb,
|
2015-06-25 07:54:59 +08:00
|
|
|
struct inode *inode, struct buffer_head *di_bh,
|
|
|
|
int update_isize, loff_t end)
|
2015-02-17 07:59:54 +08:00
|
|
|
{
|
|
|
|
struct inode *orphan_dir_inode = NULL;
|
|
|
|
struct buffer_head *orphan_dir_bh = NULL;
|
2015-06-25 07:54:59 +08:00
|
|
|
struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
|
2015-02-17 07:59:54 +08:00
|
|
|
handle_t *handle = NULL;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
orphan_dir_inode = ocfs2_get_system_file_inode(osb,
|
|
|
|
ORPHAN_DIR_SYSTEM_INODE,
|
|
|
|
le16_to_cpu(di->i_dio_orphaned_slot));
|
|
|
|
if (!orphan_dir_inode) {
|
|
|
|
status = -ENOENT;
|
|
|
|
mlog_errno(status);
|
2015-06-25 07:54:59 +08:00
|
|
|
goto bail;
|
2015-02-17 07:59:54 +08:00
|
|
|
}
|
|
|
|
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_lock(orphan_dir_inode);
|
2015-02-17 07:59:54 +08:00
|
|
|
status = ocfs2_inode_lock(orphan_dir_inode, &orphan_dir_bh, 1);
|
|
|
|
if (status < 0) {
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_unlock(orphan_dir_inode);
|
2015-02-17 07:59:54 +08:00
|
|
|
iput(orphan_dir_inode);
|
|
|
|
mlog_errno(status);
|
2015-06-25 07:54:59 +08:00
|
|
|
goto bail;
|
2015-02-17 07:59:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
handle = ocfs2_start_trans(osb,
|
|
|
|
OCFS2_INODE_DEL_FROM_ORPHAN_CREDITS);
|
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
status = PTR_ERR(handle);
|
|
|
|
goto bail_unlock_orphan;
|
|
|
|
}
|
|
|
|
|
|
|
|
BUG_ON(!(di->i_flags & cpu_to_le32(OCFS2_DIO_ORPHANED_FL)));
|
|
|
|
|
|
|
|
status = ocfs2_orphan_del(osb, handle, orphan_dir_inode,
|
|
|
|
inode, orphan_dir_bh, true);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail_commit;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_journal_access_di(handle,
|
|
|
|
INODE_CACHE(inode),
|
|
|
|
di_bh,
|
|
|
|
OCFS2_JOURNAL_ACCESS_WRITE);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto bail_commit;
|
|
|
|
}
|
|
|
|
|
|
|
|
di->i_flags &= ~cpu_to_le32(OCFS2_DIO_ORPHANED_FL);
|
|
|
|
di->i_dio_orphaned_slot = 0;
|
|
|
|
|
|
|
|
if (update_isize) {
|
|
|
|
status = ocfs2_set_inode_size(handle, inode, di_bh, end);
|
|
|
|
if (status)
|
|
|
|
mlog_errno(status);
|
|
|
|
} else
|
|
|
|
ocfs2_journal_dirty(handle, di_bh);
|
|
|
|
|
|
|
|
bail_commit:
|
|
|
|
ocfs2_commit_trans(osb, handle);
|
|
|
|
|
|
|
|
bail_unlock_orphan:
|
|
|
|
ocfs2_inode_unlock(orphan_dir_inode, 1);
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_unlock(orphan_dir_inode);
|
2015-02-17 07:59:54 +08:00
|
|
|
brelse(orphan_dir_bh);
|
|
|
|
iput(orphan_dir_inode);
|
|
|
|
|
|
|
|
bail:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2009-08-18 11:44:14 +08:00
|
|
|
int ocfs2_mv_orphaned_inode_to_new(struct inode *dir,
|
|
|
|
struct inode *inode,
|
|
|
|
struct dentry *dentry)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
struct buffer_head *parent_di_bh = NULL;
|
|
|
|
handle_t *handle = NULL;
|
|
|
|
struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
|
|
|
|
struct ocfs2_dinode *dir_di, *di;
|
|
|
|
struct inode *orphan_dir_inode = NULL;
|
|
|
|
struct buffer_head *orphan_dir_bh = NULL;
|
|
|
|
struct buffer_head *di_bh = NULL;
|
|
|
|
struct ocfs2_dir_lookup_result lookup = { NULL, };
|
|
|
|
|
2011-02-23 22:45:26 +08:00
|
|
|
trace_ocfs2_mv_orphaned_inode_to_new(dir, dentry,
|
|
|
|
dentry->d_name.len, dentry->d_name.name,
|
|
|
|
(unsigned long long)OCFS2_I(dir)->ip_blkno,
|
|
|
|
(unsigned long long)OCFS2_I(inode)->ip_blkno);
|
2009-08-18 11:44:14 +08:00
|
|
|
|
|
|
|
status = ocfs2_inode_lock(dir, &parent_di_bh, 1);
|
|
|
|
if (status < 0) {
|
|
|
|
if (status != -ENOENT)
|
|
|
|
mlog_errno(status);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
dir_di = (struct ocfs2_dinode *) parent_di_bh->b_data;
|
|
|
|
if (!dir_di->i_links_count) {
|
|
|
|
/* can't make a file in a deleted directory. */
|
|
|
|
status = -ENOENT;
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_check_dir_for_entry(dir, dentry->d_name.name,
|
|
|
|
dentry->d_name.len);
|
|
|
|
if (status)
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
/* get a spot inside the dir. */
|
|
|
|
status = ocfs2_prepare_dir_for_insert(osb, dir, parent_di_bh,
|
|
|
|
dentry->d_name.name,
|
|
|
|
dentry->d_name.len, &lookup);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
orphan_dir_inode = ocfs2_get_system_file_inode(osb,
|
|
|
|
ORPHAN_DIR_SYSTEM_INODE,
|
|
|
|
osb->slot_num);
|
|
|
|
if (!orphan_dir_inode) {
|
2015-04-15 06:43:24 +08:00
|
|
|
status = -ENOENT;
|
2009-08-18 11:44:14 +08:00
|
|
|
mlog_errno(status);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_lock(orphan_dir_inode);
|
2009-08-18 11:44:14 +08:00
|
|
|
|
|
|
|
status = ocfs2_inode_lock(orphan_dir_inode, &orphan_dir_bh, 1);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_unlock(orphan_dir_inode);
|
2009-08-18 11:44:14 +08:00
|
|
|
iput(orphan_dir_inode);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_read_inode_block(inode, &di_bh);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto orphan_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle = ocfs2_start_trans(osb, ocfs2_rename_credits(osb->sb));
|
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
status = PTR_ERR(handle);
|
|
|
|
handle = NULL;
|
|
|
|
mlog_errno(status);
|
|
|
|
goto orphan_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_journal_access_di(handle, INODE_CACHE(inode),
|
|
|
|
di_bh, OCFS2_JOURNAL_ACCESS_WRITE);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto out_commit;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_orphan_del(osb, handle, orphan_dir_inode, inode,
|
2015-02-17 07:59:54 +08:00
|
|
|
orphan_dir_bh, false);
|
2009-08-18 11:44:14 +08:00
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto out_commit;
|
|
|
|
}
|
|
|
|
|
|
|
|
di = (struct ocfs2_dinode *)di_bh->b_data;
|
2013-07-04 06:00:48 +08:00
|
|
|
di->i_flags &= ~cpu_to_le32(OCFS2_ORPHANED_FL);
|
2009-08-18 11:44:14 +08:00
|
|
|
di->i_orphaned_slot = 0;
|
2011-10-28 20:13:29 +08:00
|
|
|
set_nlink(inode, 1);
|
2009-12-18 10:24:55 +08:00
|
|
|
ocfs2_set_links_count(di, inode->i_nlink);
|
2014-04-04 05:47:08 +08:00
|
|
|
ocfs2_update_inode_fsync_trans(handle, inode, 1);
|
2009-08-18 11:44:14 +08:00
|
|
|
ocfs2_journal_dirty(handle, di_bh);
|
|
|
|
|
|
|
|
status = ocfs2_add_entry(handle, dentry, inode,
|
|
|
|
OCFS2_I(inode)->ip_blkno, parent_di_bh,
|
|
|
|
&lookup);
|
|
|
|
if (status < 0) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto out_commit;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ocfs2_dentry_attach_lock(dentry, inode,
|
|
|
|
OCFS2_I(dir)->ip_blkno);
|
|
|
|
if (status) {
|
|
|
|
mlog_errno(status);
|
|
|
|
goto out_commit;
|
|
|
|
}
|
|
|
|
|
|
|
|
d_instantiate(dentry, inode);
|
|
|
|
status = 0;
|
|
|
|
out_commit:
|
|
|
|
ocfs2_commit_trans(osb, handle);
|
|
|
|
orphan_unlock:
|
|
|
|
ocfs2_inode_unlock(orphan_dir_inode, 1);
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_unlock(orphan_dir_inode);
|
2009-08-18 11:44:14 +08:00
|
|
|
iput(orphan_dir_inode);
|
|
|
|
leave:
|
|
|
|
|
|
|
|
ocfs2_inode_unlock(dir, 1);
|
|
|
|
|
|
|
|
brelse(di_bh);
|
|
|
|
brelse(parent_di_bh);
|
|
|
|
brelse(orphan_dir_bh);
|
|
|
|
|
|
|
|
ocfs2_free_dir_lookup_result(&lookup);
|
|
|
|
|
2011-03-07 16:43:21 +08:00
|
|
|
if (status)
|
|
|
|
mlog_errno(status);
|
2009-08-18 11:44:14 +08:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2007-02-12 16:55:39 +08:00
|
|
|
const struct inode_operations ocfs2_dir_iops = {
|
2005-12-16 06:31:24 +08:00
|
|
|
.create = ocfs2_create,
|
|
|
|
.lookup = ocfs2_lookup,
|
|
|
|
.link = ocfs2_link,
|
|
|
|
.unlink = ocfs2_unlink,
|
|
|
|
.rmdir = ocfs2_unlink,
|
|
|
|
.symlink = ocfs2_symlink,
|
|
|
|
.mkdir = ocfs2_mkdir,
|
|
|
|
.mknod = ocfs2_mknod,
|
|
|
|
.rename = ocfs2_rename,
|
|
|
|
.setattr = ocfs2_setattr,
|
|
|
|
.getattr = ocfs2_getattr,
|
2006-11-27 09:59:21 +08:00
|
|
|
.permission = ocfs2_permission,
|
2008-08-18 17:11:00 +08:00
|
|
|
.listxattr = ocfs2_listxattr,
|
2009-12-17 18:42:16 +08:00
|
|
|
.fiemap = ocfs2_fiemap,
|
2011-07-23 23:37:31 +08:00
|
|
|
.get_acl = ocfs2_iop_get_acl,
|
2013-12-20 21:16:48 +08:00
|
|
|
.set_acl = ocfs2_iop_set_acl,
|
2021-04-07 20:36:44 +08:00
|
|
|
.fileattr_get = ocfs2_fileattr_get,
|
|
|
|
.fileattr_set = ocfs2_fileattr_set,
|
2005-12-16 06:31:24 +08:00
|
|
|
};
|