2006-01-17 00:50:04 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
|
2011-05-13 19:11:17 +08:00
|
|
|
* Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
|
2006-01-17 00:50:04 +08:00
|
|
|
*
|
|
|
|
* This copyrighted material is made available to anyone wishing to use,
|
|
|
|
* modify, copy, or redistribute it subject to the terms and conditions
|
2006-09-01 23:05:15 +08:00
|
|
|
* of the GNU General Public License version 2.
|
2006-01-17 00:50:04 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/completion.h>
|
|
|
|
#include <linux/buffer_head.h>
|
|
|
|
#include <linux/namei.h>
|
|
|
|
#include <linux/mm.h>
|
2017-02-03 00:54:15 +08:00
|
|
|
#include <linux/cred.h>
|
2006-01-17 00:50:04 +08:00
|
|
|
#include <linux/xattr.h>
|
|
|
|
#include <linux/posix_acl.h>
|
2006-02-28 06:23:27 +08:00
|
|
|
#include <linux/gfs2_ondisk.h>
|
2006-03-29 03:14:04 +08:00
|
|
|
#include <linux/crc32.h>
|
2008-10-14 21:43:29 +08:00
|
|
|
#include <linux/fiemap.h>
|
2011-05-09 21:06:38 +08:00
|
|
|
#include <linux/security.h>
|
2016-12-25 03:46:01 +08:00
|
|
|
#include <linux/uaccess.h>
|
2006-01-17 00:50:04 +08:00
|
|
|
|
|
|
|
#include "gfs2.h"
|
2006-02-28 06:23:27 +08:00
|
|
|
#include "incore.h"
|
2006-01-17 00:50:04 +08:00
|
|
|
#include "acl.h"
|
|
|
|
#include "bmap.h"
|
|
|
|
#include "dir.h"
|
2009-08-27 01:51:04 +08:00
|
|
|
#include "xattr.h"
|
2006-01-17 00:50:04 +08:00
|
|
|
#include "glock.h"
|
|
|
|
#include "inode.h"
|
|
|
|
#include "meta_io.h"
|
|
|
|
#include "quota.h"
|
|
|
|
#include "rgrp.h"
|
|
|
|
#include "trans.h"
|
2006-02-28 06:23:27 +08:00
|
|
|
#include "util.h"
|
2008-10-14 23:05:55 +08:00
|
|
|
#include "super.h"
|
2011-05-09 21:06:38 +08:00
|
|
|
#include "glops.h"
|
|
|
|
|
2016-06-15 01:24:50 +08:00
|
|
|
static int iget_test(struct inode *inode, void *opaque)
|
2011-05-09 21:06:38 +08:00
|
|
|
{
|
2016-06-15 01:24:50 +08:00
|
|
|
u64 no_addr = *(u64 *)opaque;
|
|
|
|
|
|
|
|
return GFS2_I(inode)->i_no_addr == no_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int iget_set(struct inode *inode, void *opaque)
|
|
|
|
{
|
|
|
|
u64 no_addr = *(u64 *)opaque;
|
|
|
|
|
|
|
|
GFS2_I(inode)->i_no_addr = no_addr;
|
|
|
|
inode->i_ino = no_addr;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
static struct inode *gfs2_iget(struct super_block *sb, u64 no_addr)
|
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
|
|
|
|
repeat:
|
2016-06-15 01:24:50 +08:00
|
|
|
inode = iget5_locked(sb, no_addr, iget_test, iget_set, &no_addr);
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
if (!inode)
|
|
|
|
return inode;
|
|
|
|
if (is_bad_inode(inode)) {
|
|
|
|
iput(inode);
|
|
|
|
goto repeat;
|
|
|
|
}
|
|
|
|
return inode;
|
2011-05-09 21:06:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gfs2_set_iop - Sets inode operations
|
|
|
|
* @inode: The inode with correct i_mode filled in
|
|
|
|
*
|
|
|
|
* GFS2 lookup code fills in vfs inode contents based on info obtained
|
|
|
|
* from directory entry inside gfs2_inode_lookup().
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void gfs2_set_iop(struct inode *inode)
|
|
|
|
{
|
|
|
|
struct gfs2_sbd *sdp = GFS2_SB(inode);
|
|
|
|
umode_t mode = inode->i_mode;
|
|
|
|
|
|
|
|
if (S_ISREG(mode)) {
|
|
|
|
inode->i_op = &gfs2_file_iops;
|
|
|
|
if (gfs2_localflocks(sdp))
|
|
|
|
inode->i_fop = &gfs2_file_fops_nolock;
|
|
|
|
else
|
|
|
|
inode->i_fop = &gfs2_file_fops;
|
|
|
|
} else if (S_ISDIR(mode)) {
|
|
|
|
inode->i_op = &gfs2_dir_iops;
|
|
|
|
if (gfs2_localflocks(sdp))
|
|
|
|
inode->i_fop = &gfs2_dir_fops_nolock;
|
|
|
|
else
|
|
|
|
inode->i_fop = &gfs2_dir_fops;
|
|
|
|
} else if (S_ISLNK(mode)) {
|
|
|
|
inode->i_op = &gfs2_symlink_iops;
|
|
|
|
} else {
|
|
|
|
inode->i_op = &gfs2_file_iops;
|
|
|
|
init_special_inode(inode, inode->i_mode, inode->i_rdev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gfs2_inode_lookup - Lookup an inode
|
|
|
|
* @sb: The super block
|
|
|
|
* @type: The type of the inode
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
* @no_addr: The inode number
|
|
|
|
* @no_formal_ino: The inode generation number
|
|
|
|
* @blktype: Requested block type (GFS2_BLKST_DINODE or GFS2_BLKST_UNLINKED;
|
|
|
|
* GFS2_BLKST_FREE do indicate not to verify)
|
|
|
|
*
|
|
|
|
* If @type is DT_UNKNOWN, the inode type is fetched from disk.
|
|
|
|
*
|
|
|
|
* If @blktype is anything other than GFS2_BLKST_FREE (which is used as a
|
|
|
|
* placeholder because it doesn't otherwise make sense), the on-disk block type
|
|
|
|
* is verified to be @blktype.
|
2011-05-09 21:06:38 +08:00
|
|
|
*
|
|
|
|
* Returns: A VFS inode, or an error
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type,
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
u64 no_addr, u64 no_formal_ino,
|
|
|
|
unsigned int blktype)
|
2011-05-09 21:06:38 +08:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
struct gfs2_inode *ip;
|
|
|
|
struct gfs2_glock *io_gl = NULL;
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
struct gfs2_holder i_gh;
|
2011-05-09 21:06:38 +08:00
|
|
|
int error;
|
|
|
|
|
2016-06-17 20:31:27 +08:00
|
|
|
gfs2_holder_mark_uninitialized(&i_gh);
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
inode = gfs2_iget(sb, no_addr);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (!inode)
|
2014-01-16 18:31:13 +08:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2011-05-09 21:06:38 +08:00
|
|
|
|
2016-04-13 04:14:26 +08:00
|
|
|
ip = GFS2_I(inode);
|
|
|
|
|
2011-05-09 21:06:38 +08:00
|
|
|
if (inode->i_state & I_NEW) {
|
|
|
|
struct gfs2_sbd *sdp = GFS2_SB(inode);
|
|
|
|
ip->i_no_formal_ino = no_formal_ino;
|
|
|
|
|
|
|
|
error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl);
|
|
|
|
if (unlikely(error))
|
|
|
|
goto fail;
|
|
|
|
ip->i_gl->gl_object = ip;
|
|
|
|
|
|
|
|
error = gfs2_glock_get(sdp, no_addr, &gfs2_iopen_glops, CREATE, &io_gl);
|
|
|
|
if (unlikely(error))
|
|
|
|
goto fail_put;
|
|
|
|
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
if (type == DT_UNKNOWN || blktype != GFS2_BLKST_FREE) {
|
|
|
|
/*
|
|
|
|
* The GL_SKIP flag indicates to skip reading the inode
|
|
|
|
* block. We read the inode with gfs2_inode_refresh
|
|
|
|
* after possibly checking the block type.
|
|
|
|
*/
|
|
|
|
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE,
|
|
|
|
GL_SKIP, &i_gh);
|
|
|
|
if (error)
|
|
|
|
goto fail_put;
|
|
|
|
|
|
|
|
if (blktype != GFS2_BLKST_FREE) {
|
|
|
|
error = gfs2_check_blk_type(sdp, no_addr,
|
|
|
|
blktype);
|
|
|
|
if (error)
|
|
|
|
goto fail_put;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-09 21:06:38 +08:00
|
|
|
set_bit(GIF_INVALID, &ip->i_flags);
|
|
|
|
error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, GL_EXACT, &ip->i_iopen_gh);
|
|
|
|
if (unlikely(error))
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
goto fail_put;
|
2011-05-09 21:06:38 +08:00
|
|
|
|
|
|
|
ip->i_iopen_gh.gh_gl->gl_object = ip;
|
|
|
|
gfs2_glock_put(io_gl);
|
|
|
|
io_gl = NULL;
|
|
|
|
|
|
|
|
if (type == DT_UNKNOWN) {
|
|
|
|
/* Inode glock must be locked already */
|
|
|
|
error = gfs2_inode_refresh(GFS2_I(inode));
|
|
|
|
if (error)
|
|
|
|
goto fail_refresh;
|
|
|
|
} else {
|
|
|
|
inode->i_mode = DT2IF(type);
|
|
|
|
}
|
|
|
|
|
|
|
|
gfs2_set_iop(inode);
|
2016-09-27 02:24:34 +08:00
|
|
|
|
|
|
|
inode->i_atime.tv_sec = 0;
|
|
|
|
inode->i_atime.tv_nsec = 0;
|
|
|
|
|
2011-05-09 21:06:38 +08:00
|
|
|
unlock_new_inode(inode);
|
|
|
|
}
|
|
|
|
|
2016-06-17 20:31:27 +08:00
|
|
|
if (gfs2_holder_initialized(&i_gh))
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
gfs2_glock_dq_uninit(&i_gh);
|
2011-05-09 21:06:38 +08:00
|
|
|
return inode;
|
|
|
|
|
|
|
|
fail_refresh:
|
2013-05-29 23:51:52 +08:00
|
|
|
ip->i_iopen_gh.gh_flags |= GL_NOCACHE;
|
2011-05-09 21:06:38 +08:00
|
|
|
ip->i_iopen_gh.gh_gl->gl_object = NULL;
|
2017-02-23 03:05:09 +08:00
|
|
|
gfs2_glock_dq_uninit(&ip->i_iopen_gh);
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
fail_put:
|
2011-05-09 21:06:38 +08:00
|
|
|
if (io_gl)
|
|
|
|
gfs2_glock_put(io_gl);
|
2016-06-17 20:31:27 +08:00
|
|
|
if (gfs2_holder_initialized(&i_gh))
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
gfs2_glock_dq_uninit(&i_gh);
|
2011-05-09 21:06:38 +08:00
|
|
|
ip->i_gl->gl_object = NULL;
|
|
|
|
fail:
|
|
|
|
iget_failed(inode);
|
|
|
|
return ERR_PTR(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr,
|
|
|
|
u64 *no_formal_ino, unsigned int blktype)
|
|
|
|
{
|
|
|
|
struct super_block *sb = sdp->sd_vfs;
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
struct inode *inode;
|
2011-05-09 21:06:38 +08:00
|
|
|
int error;
|
|
|
|
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
inode = gfs2_inode_lookup(sb, DT_UNKNOWN, no_addr, 0, blktype);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (IS_ERR(inode))
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
return inode;
|
2011-05-09 21:06:38 +08:00
|
|
|
|
|
|
|
/* Two extra checks for NFS only */
|
|
|
|
if (no_formal_ino) {
|
|
|
|
error = -ESTALE;
|
|
|
|
if (GFS2_I(inode)->i_no_formal_ino != *no_formal_ino)
|
|
|
|
goto fail_iput;
|
|
|
|
|
|
|
|
error = -EIO;
|
|
|
|
if (GFS2_I(inode)->i_diskflags & GFS2_DIF_SYSTEM)
|
|
|
|
goto fail_iput;
|
|
|
|
}
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
return inode;
|
2011-05-09 21:06:38 +08:00
|
|
|
|
|
|
|
fail_iput:
|
|
|
|
iput(inode);
|
gfs2: Fix gfs2_lookup_by_inum lock inversion
The current gfs2_lookup_by_inum takes the glock of a presumed inode
identified by block number, verifies that the block is indeed an inode,
and then instantiates and reads the new inode via gfs2_inode_lookup.
However, instantiating a new inode may block on freeing a previous
instance of that inode (__wait_on_freeing_inode), and freeing an inode
requires to take the glock already held, leading to lock inversion and
deadlock.
Fix this by first instantiating the new inode, then verifying that the
block is an inode (if required), and then reading in the new inode, all
in gfs2_inode_lookup.
If the block we are looking for is not an inode, we discard the new
inode via iget_failed, which marks inodes as bad and unhashes them.
Other tasks waiting on that inode will get back a bad inode back from
ilookup or iget_locked; in that case, retry the lookup.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2016-06-15 01:22:27 +08:00
|
|
|
return ERR_PTR(error);
|
2011-05-09 21:06:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct inode *gfs2_lookup_simple(struct inode *dip, const char *name)
|
|
|
|
{
|
|
|
|
struct qstr qstr;
|
|
|
|
struct inode *inode;
|
|
|
|
gfs2_str2qstr(&qstr, name);
|
|
|
|
inode = gfs2_lookupi(dip, &qstr, 1);
|
|
|
|
/* gfs2_lookupi has inconsistent callers: vfs
|
|
|
|
* related routines expect NULL for no entry found,
|
|
|
|
* gfs2_lookup_simple callers expect ENOENT
|
|
|
|
* and do not check for NULL.
|
|
|
|
*/
|
|
|
|
if (inode == NULL)
|
|
|
|
return ERR_PTR(-ENOENT);
|
|
|
|
else
|
|
|
|
return inode;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gfs2_lookupi - Look up a filename in a directory and return its inode
|
|
|
|
* @d_gh: An initialized holder for the directory glock
|
|
|
|
* @name: The name of the inode to look for
|
|
|
|
* @is_root: If 1, ignore the caller's permissions
|
|
|
|
* @i_gh: An uninitialized holder for the new inode glock
|
|
|
|
*
|
|
|
|
* This can be called via the VFS filldir function when NFS is doing
|
|
|
|
* a readdirplus and the inode which its intending to stat isn't
|
|
|
|
* already in cache. In this case we must not take the directory glock
|
|
|
|
* again, since the readdir call will have already taken that lock.
|
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
|
|
|
|
int is_root)
|
|
|
|
{
|
|
|
|
struct super_block *sb = dir->i_sb;
|
|
|
|
struct gfs2_inode *dip = GFS2_I(dir);
|
|
|
|
struct gfs2_holder d_gh;
|
|
|
|
int error = 0;
|
|
|
|
struct inode *inode = NULL;
|
|
|
|
|
2016-06-17 20:31:27 +08:00
|
|
|
gfs2_holder_mark_uninitialized(&d_gh);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (!name->len || name->len > GFS2_FNAMESIZE)
|
|
|
|
return ERR_PTR(-ENAMETOOLONG);
|
|
|
|
|
|
|
|
if ((name->len == 1 && memcmp(name->name, ".", 1) == 0) ||
|
|
|
|
(name->len == 2 && memcmp(name->name, "..", 2) == 0 &&
|
2015-03-18 06:25:59 +08:00
|
|
|
dir == d_inode(sb->s_root))) {
|
2011-05-09 21:06:38 +08:00
|
|
|
igrab(dir);
|
|
|
|
return dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gfs2_glock_is_locked_by_me(dip->i_gl) == NULL) {
|
|
|
|
error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh);
|
|
|
|
if (error)
|
|
|
|
return ERR_PTR(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!is_root) {
|
2011-06-21 07:28:19 +08:00
|
|
|
error = gfs2_permission(dir, MAY_EXEC);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2013-06-11 20:45:29 +08:00
|
|
|
inode = gfs2_dir_search(dir, name, false);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (IS_ERR(inode))
|
|
|
|
error = PTR_ERR(inode);
|
|
|
|
out:
|
2016-06-17 20:31:27 +08:00
|
|
|
if (gfs2_holder_initialized(&d_gh))
|
2011-05-09 21:06:38 +08:00
|
|
|
gfs2_glock_dq_uninit(&d_gh);
|
|
|
|
if (error == -ENOENT)
|
|
|
|
return NULL;
|
|
|
|
return inode ? inode : ERR_PTR(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* create_ok - OK to create a new on-disk inode here?
|
|
|
|
* @dip: Directory in which dinode is to be created
|
|
|
|
* @name: Name of new dinode
|
|
|
|
* @mode:
|
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int create_ok(struct gfs2_inode *dip, const struct qstr *name,
|
2011-07-26 15:30:54 +08:00
|
|
|
umode_t mode)
|
2011-05-09 21:06:38 +08:00
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
2011-06-21 07:28:19 +08:00
|
|
|
error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
/* Don't create entries in an unlinked directory */
|
|
|
|
if (!dip->i_inode.i_nlink)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
if (dip->i_entries == (u32)-1)
|
|
|
|
return -EFBIG;
|
|
|
|
if (S_ISDIR(mode) && dip->i_inode.i_nlink == (u32)-1)
|
|
|
|
return -EMLINK;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-31 18:30:22 +08:00
|
|
|
static void munge_mode_uid_gid(const struct gfs2_inode *dip,
|
|
|
|
struct inode *inode)
|
2011-05-09 21:06:38 +08:00
|
|
|
{
|
|
|
|
if (GFS2_SB(&dip->i_inode)->sd_args.ar_suiddir &&
|
2013-02-01 13:56:13 +08:00
|
|
|
(dip->i_inode.i_mode & S_ISUID) &&
|
|
|
|
!uid_eq(dip->i_inode.i_uid, GLOBAL_ROOT_UID)) {
|
2012-10-31 18:30:22 +08:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
inode->i_mode |= S_ISUID;
|
2013-02-01 13:56:13 +08:00
|
|
|
else if (!uid_eq(dip->i_inode.i_uid, current_fsuid()))
|
2012-10-31 18:30:22 +08:00
|
|
|
inode->i_mode &= ~07111;
|
|
|
|
inode->i_uid = dip->i_inode.i_uid;
|
2011-05-09 21:06:38 +08:00
|
|
|
} else
|
2012-10-31 18:30:22 +08:00
|
|
|
inode->i_uid = current_fsuid();
|
2011-05-09 21:06:38 +08:00
|
|
|
|
|
|
|
if (dip->i_inode.i_mode & S_ISGID) {
|
2012-10-31 18:30:22 +08:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
inode->i_mode |= S_ISGID;
|
|
|
|
inode->i_gid = dip->i_inode.i_gid;
|
2011-05-09 21:06:38 +08:00
|
|
|
} else
|
2012-10-31 18:30:22 +08:00
|
|
|
inode->i_gid = current_fsgid();
|
2011-05-09 21:06:38 +08:00
|
|
|
}
|
|
|
|
|
2014-02-04 23:45:11 +08:00
|
|
|
static int alloc_dinode(struct gfs2_inode *ip, u32 flags, unsigned *dblocks)
|
2011-05-09 21:06:38 +08:00
|
|
|
{
|
2012-10-31 18:30:22 +08:00
|
|
|
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
2014-02-04 23:45:11 +08:00
|
|
|
struct gfs2_alloc_parms ap = { .target = *dblocks, .aflags = flags, };
|
2011-05-09 21:06:38 +08:00
|
|
|
int error;
|
|
|
|
|
2015-03-19 01:03:41 +08:00
|
|
|
error = gfs2_quota_lock_check(ip, &ap);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
|
2013-10-02 18:13:25 +08:00
|
|
|
error = gfs2_inplace_reserve(ip, &ap);
|
2013-02-27 00:15:20 +08:00
|
|
|
if (error)
|
|
|
|
goto out_quota;
|
|
|
|
|
2014-02-04 23:45:11 +08:00
|
|
|
error = gfs2_trans_begin(sdp, (*dblocks * RES_RG_BIT) + RES_STATFS + RES_QUOTA, 0);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
|
|
|
goto out_ipreserv;
|
|
|
|
|
2014-02-04 23:45:11 +08:00
|
|
|
error = gfs2_alloc_blocks(ip, &ip->i_no_addr, dblocks, 1, &ip->i_generation);
|
2012-10-31 18:30:22 +08:00
|
|
|
ip->i_no_formal_ino = ip->i_generation;
|
|
|
|
ip->i_inode.i_ino = ip->i_no_addr;
|
|
|
|
ip->i_goal = ip->i_no_addr;
|
2011-05-09 21:06:38 +08:00
|
|
|
|
|
|
|
gfs2_trans_end(sdp);
|
|
|
|
|
|
|
|
out_ipreserv:
|
2012-10-31 18:30:22 +08:00
|
|
|
gfs2_inplace_release(ip);
|
2013-02-27 00:15:20 +08:00
|
|
|
out_quota:
|
|
|
|
gfs2_quota_unlock(ip);
|
2011-05-09 21:06:38 +08:00
|
|
|
out:
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2011-05-13 19:11:17 +08:00
|
|
|
static void gfs2_init_dir(struct buffer_head *dibh,
|
|
|
|
const struct gfs2_inode *parent)
|
2011-05-13 16:55:55 +08:00
|
|
|
{
|
|
|
|
struct gfs2_dinode *di = (struct gfs2_dinode *)dibh->b_data;
|
|
|
|
struct gfs2_dirent *dent = (struct gfs2_dirent *)(di+1);
|
|
|
|
|
|
|
|
gfs2_qstr2dirent(&gfs2_qdot, GFS2_DIRENT_SIZE(gfs2_qdot.len), dent);
|
|
|
|
dent->de_inum = di->di_num; /* already GFS2 endian */
|
|
|
|
dent->de_type = cpu_to_be16(DT_DIR);
|
|
|
|
|
|
|
|
dent = (struct gfs2_dirent *)((char*)dent + GFS2_DIRENT_SIZE(1));
|
|
|
|
gfs2_qstr2dirent(&gfs2_qdotdot, dibh->b_size - GFS2_DIRENT_SIZE(1) - sizeof(struct gfs2_dinode), dent);
|
|
|
|
gfs2_inum_out(parent, dent);
|
|
|
|
dent->de_type = cpu_to_be16(DT_DIR);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-02-04 23:45:11 +08:00
|
|
|
/**
|
|
|
|
* gfs2_init_xattr - Initialise an xattr block for a new inode
|
|
|
|
* @ip: The inode in question
|
|
|
|
*
|
|
|
|
* This sets up an empty xattr block for a new inode, ready to
|
|
|
|
* take any ACLs, LSM xattrs, etc.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void gfs2_init_xattr(struct gfs2_inode *ip)
|
|
|
|
{
|
|
|
|
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
|
|
|
struct buffer_head *bh;
|
|
|
|
struct gfs2_ea_header *ea;
|
|
|
|
|
|
|
|
bh = gfs2_meta_new(ip->i_gl, ip->i_eattr);
|
|
|
|
gfs2_trans_add_meta(ip->i_gl, bh);
|
|
|
|
gfs2_metatype_set(bh, GFS2_METATYPE_EA, GFS2_FORMAT_EA);
|
|
|
|
gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header));
|
|
|
|
|
|
|
|
ea = GFS2_EA_BH2FIRST(bh);
|
|
|
|
ea->ea_rec_len = cpu_to_be32(sdp->sd_jbsize);
|
|
|
|
ea->ea_type = GFS2_EATYPE_UNUSED;
|
|
|
|
ea->ea_flags = GFS2_EAFLAG_LAST;
|
|
|
|
|
|
|
|
brelse(bh);
|
|
|
|
}
|
|
|
|
|
2011-05-09 21:06:38 +08:00
|
|
|
/**
|
|
|
|
* init_dinode - Fill in a new dinode structure
|
2011-05-13 19:11:17 +08:00
|
|
|
* @dip: The directory this inode is being created in
|
2012-10-31 18:30:22 +08:00
|
|
|
* @ip: The inode
|
2011-05-13 19:11:17 +08:00
|
|
|
* @symname: The symlink destination (if a symlink)
|
|
|
|
* @bhp: The buffer head (returned to caller)
|
2011-05-09 21:06:38 +08:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2012-10-31 18:30:22 +08:00
|
|
|
static void init_dinode(struct gfs2_inode *dip, struct gfs2_inode *ip,
|
2013-03-01 17:29:12 +08:00
|
|
|
const char *symname)
|
2011-05-09 21:06:38 +08:00
|
|
|
{
|
|
|
|
struct gfs2_dinode *di;
|
|
|
|
struct buffer_head *dibh;
|
|
|
|
|
2012-10-31 18:30:22 +08:00
|
|
|
dibh = gfs2_meta_new(ip->i_gl, ip->i_no_addr);
|
2012-12-14 20:36:02 +08:00
|
|
|
gfs2_trans_add_meta(ip->i_gl, dibh);
|
2011-05-09 21:06:38 +08:00
|
|
|
di = (struct gfs2_dinode *)dibh->b_data;
|
2013-03-01 17:29:12 +08:00
|
|
|
gfs2_dinode_out(ip, di);
|
2011-05-09 21:06:38 +08:00
|
|
|
|
2012-10-31 18:30:22 +08:00
|
|
|
di->di_major = cpu_to_be32(MAJOR(ip->i_inode.i_rdev));
|
|
|
|
di->di_minor = cpu_to_be32(MINOR(ip->i_inode.i_rdev));
|
2011-05-09 21:06:38 +08:00
|
|
|
di->__pad1 = 0;
|
|
|
|
di->__pad2 = 0;
|
|
|
|
di->__pad3 = 0;
|
|
|
|
memset(&di->__pad4, 0, sizeof(di->__pad4));
|
|
|
|
memset(&di->di_reserved, 0, sizeof(di->di_reserved));
|
2013-03-01 17:29:12 +08:00
|
|
|
gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode));
|
2011-05-13 17:34:59 +08:00
|
|
|
|
2012-10-31 18:30:22 +08:00
|
|
|
switch(ip->i_inode.i_mode & S_IFMT) {
|
2011-05-13 17:34:59 +08:00
|
|
|
case S_IFDIR:
|
2011-05-13 16:55:55 +08:00
|
|
|
gfs2_init_dir(dibh, dip);
|
2011-05-13 17:34:59 +08:00
|
|
|
break;
|
|
|
|
case S_IFLNK:
|
2012-10-31 18:30:22 +08:00
|
|
|
memcpy(dibh->b_data + sizeof(struct gfs2_dinode), symname, ip->i_inode.i_size);
|
2011-05-13 17:34:59 +08:00
|
|
|
break;
|
2011-05-13 16:55:55 +08:00
|
|
|
}
|
|
|
|
|
2011-05-09 21:06:38 +08:00
|
|
|
set_buffer_uptodate(dibh);
|
2013-03-01 17:29:12 +08:00
|
|
|
brelse(dibh);
|
2011-05-09 21:06:38 +08:00
|
|
|
}
|
|
|
|
|
2014-01-06 20:03:05 +08:00
|
|
|
/**
|
|
|
|
* gfs2_trans_da_blocks - Calculate number of blocks to link inode
|
|
|
|
* @dip: The directory we are linking into
|
|
|
|
* @da: The dir add information
|
|
|
|
* @nr_inodes: The number of inodes involved
|
|
|
|
*
|
|
|
|
* This calculate the number of blocks we need to reserve in a
|
|
|
|
* transaction to link @nr_inodes into a directory. In most cases
|
|
|
|
* @nr_inodes will be 2 (the directory plus the inode being linked in)
|
|
|
|
* but in case of rename, 4 may be required.
|
|
|
|
*
|
|
|
|
* Returns: Number of blocks
|
|
|
|
*/
|
|
|
|
|
|
|
|
static unsigned gfs2_trans_da_blks(const struct gfs2_inode *dip,
|
|
|
|
const struct gfs2_diradd *da,
|
|
|
|
unsigned nr_inodes)
|
|
|
|
{
|
|
|
|
return da->nr_blocks + gfs2_rg_blocks(dip, da->nr_blocks) +
|
|
|
|
(nr_inodes * RES_DINODE) + RES_QUOTA + RES_STATFS;
|
|
|
|
}
|
|
|
|
|
2011-05-09 21:06:38 +08:00
|
|
|
static int link_dinode(struct gfs2_inode *dip, const struct qstr *name,
|
2014-01-06 19:28:41 +08:00
|
|
|
struct gfs2_inode *ip, struct gfs2_diradd *da)
|
2011-05-09 21:06:38 +08:00
|
|
|
{
|
|
|
|
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
|
2014-01-06 19:28:41 +08:00
|
|
|
struct gfs2_alloc_parms ap = { .target = da->nr_blocks, };
|
2011-05-09 21:06:38 +08:00
|
|
|
int error;
|
|
|
|
|
2014-01-06 19:28:41 +08:00
|
|
|
if (da->nr_blocks) {
|
2015-03-19 01:03:41 +08:00
|
|
|
error = gfs2_quota_lock_check(dip, &ap);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
|
|
|
goto fail_quota_locks;
|
|
|
|
|
2013-10-02 18:13:25 +08:00
|
|
|
error = gfs2_inplace_reserve(dip, &ap);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
|
|
|
goto fail_quota_locks;
|
|
|
|
|
2014-01-06 20:03:05 +08:00
|
|
|
error = gfs2_trans_begin(sdp, gfs2_trans_da_blks(dip, da, 2), 0);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
|
|
|
goto fail_ipreserv;
|
|
|
|
} else {
|
|
|
|
error = gfs2_trans_begin(sdp, RES_LEAF + 2 * RES_DINODE, 0);
|
|
|
|
if (error)
|
|
|
|
goto fail_quota_locks;
|
|
|
|
}
|
|
|
|
|
2014-01-06 20:49:43 +08:00
|
|
|
error = gfs2_dir_add(&dip->i_inode, name, ip, da);
|
2011-05-09 21:06:38 +08:00
|
|
|
|
|
|
|
gfs2_trans_end(sdp);
|
|
|
|
fail_ipreserv:
|
2013-02-27 00:15:20 +08:00
|
|
|
gfs2_inplace_release(dip);
|
2011-05-09 21:06:38 +08:00
|
|
|
fail_quota_locks:
|
|
|
|
gfs2_quota_unlock(dip);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2011-09-24 06:51:32 +08:00
|
|
|
static int gfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
|
2011-06-07 03:29:25 +08:00
|
|
|
void *fs_info)
|
2011-05-09 21:06:38 +08:00
|
|
|
{
|
2011-06-07 03:29:25 +08:00
|
|
|
const struct xattr *xattr;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
|
|
|
|
err = __gfs2_xattr_set(inode, xattr->name, xattr->value,
|
|
|
|
xattr->value_len, 0,
|
|
|
|
GFS2_EATYPE_SECURITY);
|
|
|
|
if (err < 0)
|
|
|
|
break;
|
2011-05-09 21:06:38 +08:00
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2011-05-09 21:06:38 +08:00
|
|
|
/**
|
2011-05-13 19:11:17 +08:00
|
|
|
* gfs2_create_inode - Create a new inode
|
|
|
|
* @dir: The parent directory
|
|
|
|
* @dentry: The new dentry
|
2013-06-14 18:17:15 +08:00
|
|
|
* @file: If non-NULL, the file which is being opened
|
2011-05-13 19:11:17 +08:00
|
|
|
* @mode: The permissions on the new inode
|
|
|
|
* @dev: For device nodes, this is the device number
|
|
|
|
* @symname: For symlinks, this is the link destination
|
|
|
|
* @size: The initial size of the inode (ignored for directories)
|
2011-05-09 21:06:38 +08:00
|
|
|
*
|
2011-05-13 19:11:17 +08:00
|
|
|
* Returns: 0 on success, or error code
|
2011-05-09 21:06:38 +08:00
|
|
|
*/
|
|
|
|
|
2011-05-13 19:11:17 +08:00
|
|
|
static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
|
2013-06-14 18:17:15 +08:00
|
|
|
struct file *file,
|
2011-07-26 15:30:54 +08:00
|
|
|
umode_t mode, dev_t dev, const char *symname,
|
2013-06-14 18:17:15 +08:00
|
|
|
unsigned int size, int excl, int *opened)
|
2011-05-09 21:06:38 +08:00
|
|
|
{
|
2011-05-13 19:11:17 +08:00
|
|
|
const struct qstr *name = &dentry->d_name;
|
2013-12-20 21:16:52 +08:00
|
|
|
struct posix_acl *default_acl, *acl;
|
2011-05-13 19:11:17 +08:00
|
|
|
struct gfs2_holder ghs[2];
|
2011-05-09 21:06:38 +08:00
|
|
|
struct inode *inode = NULL;
|
2012-07-19 20:12:40 +08:00
|
|
|
struct gfs2_inode *dip = GFS2_I(dir), *ip;
|
2011-05-09 21:06:38 +08:00
|
|
|
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
|
2015-12-08 06:24:27 +08:00
|
|
|
struct gfs2_glock *io_gl = NULL;
|
2015-12-05 00:19:14 +08:00
|
|
|
int error, free_vfs_inode = 1;
|
2012-10-31 18:37:10 +08:00
|
|
|
u32 aflags = 0;
|
2014-02-04 23:45:11 +08:00
|
|
|
unsigned blocks = 1;
|
2014-09-29 20:52:04 +08:00
|
|
|
struct gfs2_diradd da = { .bh = NULL, .save_loc = 1, };
|
2011-05-09 21:06:38 +08:00
|
|
|
|
|
|
|
if (!name->len || name->len > GFS2_FNAMESIZE)
|
2011-05-13 19:11:17 +08:00
|
|
|
return -ENAMETOOLONG;
|
2011-05-09 21:06:38 +08:00
|
|
|
|
2015-10-26 23:40:28 +08:00
|
|
|
error = gfs2_rsqa_alloc(dip);
|
2012-06-06 18:17:59 +08:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2013-02-27 00:15:20 +08:00
|
|
|
error = gfs2_rindex_update(sdp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2011-05-13 19:11:17 +08:00
|
|
|
error = gfs2_glock_nq_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
error = create_ok(dip, name, mode);
|
2013-06-11 20:45:29 +08:00
|
|
|
if (error)
|
|
|
|
goto fail_gunlock;
|
|
|
|
|
|
|
|
inode = gfs2_dir_search(dir, &dentry->d_name, !S_ISREG(mode) || excl);
|
|
|
|
error = PTR_ERR(inode);
|
|
|
|
if (!IS_ERR(inode)) {
|
2014-11-20 03:34:49 +08:00
|
|
|
if (S_ISDIR(inode->i_mode)) {
|
|
|
|
iput(inode);
|
|
|
|
inode = ERR_PTR(-EISDIR);
|
|
|
|
goto fail_gunlock;
|
|
|
|
}
|
2014-11-20 03:35:24 +08:00
|
|
|
d_instantiate(dentry, inode);
|
2013-06-14 18:17:15 +08:00
|
|
|
error = 0;
|
2013-09-16 20:52:00 +08:00
|
|
|
if (file) {
|
2014-11-20 03:35:24 +08:00
|
|
|
if (S_ISREG(inode->i_mode))
|
2013-09-23 20:21:04 +08:00
|
|
|
error = finish_open(file, dentry, gfs2_open_common, opened);
|
2014-11-20 03:35:24 +08:00
|
|
|
else
|
|
|
|
error = finish_no_open(file, NULL);
|
2013-06-14 18:17:15 +08:00
|
|
|
}
|
2011-08-18 21:35:53 +08:00
|
|
|
gfs2_glock_dq_uninit(ghs);
|
2013-06-14 18:17:15 +08:00
|
|
|
return error;
|
2013-06-11 20:45:29 +08:00
|
|
|
} else if (error != -ENOENT) {
|
2011-05-09 21:06:38 +08:00
|
|
|
goto fail_gunlock;
|
2013-06-11 20:45:29 +08:00
|
|
|
}
|
2011-05-09 21:06:38 +08:00
|
|
|
|
2014-01-06 19:28:41 +08:00
|
|
|
error = gfs2_diradd_alloc_required(dir, name, &da);
|
2013-02-27 00:15:20 +08:00
|
|
|
if (error < 0)
|
|
|
|
goto fail_gunlock;
|
|
|
|
|
2012-10-31 18:30:22 +08:00
|
|
|
inode = new_inode(sdp->sd_vfs);
|
2013-02-27 00:15:20 +08:00
|
|
|
error = -ENOMEM;
|
|
|
|
if (!inode)
|
|
|
|
goto fail_gunlock;
|
|
|
|
|
2013-12-20 21:16:52 +08:00
|
|
|
error = posix_acl_create(dir, &mode, &default_acl, &acl);
|
|
|
|
if (error)
|
2015-12-05 00:19:14 +08:00
|
|
|
goto fail_gunlock;
|
2013-12-20 21:16:52 +08:00
|
|
|
|
2012-10-31 18:30:22 +08:00
|
|
|
ip = GFS2_I(inode);
|
2015-10-26 23:40:28 +08:00
|
|
|
error = gfs2_rsqa_alloc(ip);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
2013-12-20 21:16:52 +08:00
|
|
|
goto fail_free_acls;
|
2012-10-31 18:30:22 +08:00
|
|
|
|
|
|
|
inode->i_mode = mode;
|
2013-03-01 17:29:12 +08:00
|
|
|
set_nlink(inode, S_ISDIR(mode) ? 2 : 1);
|
2012-10-31 18:30:22 +08:00
|
|
|
inode->i_rdev = dev;
|
|
|
|
inode->i_size = size;
|
2016-09-14 22:48:04 +08:00
|
|
|
inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
|
2013-02-27 03:09:35 +08:00
|
|
|
gfs2_set_inode_blocks(inode, 1);
|
2012-10-31 18:30:22 +08:00
|
|
|
munge_mode_uid_gid(dip, inode);
|
2014-09-19 10:40:28 +08:00
|
|
|
check_and_update_goal(dip);
|
2012-10-31 18:30:22 +08:00
|
|
|
ip->i_goal = dip->i_goal;
|
2013-02-27 03:09:35 +08:00
|
|
|
ip->i_diskflags = 0;
|
|
|
|
ip->i_eattr = 0;
|
|
|
|
ip->i_height = 0;
|
|
|
|
ip->i_depth = 0;
|
|
|
|
ip->i_entries = 0;
|
2017-03-17 03:29:13 +08:00
|
|
|
ip->i_no_addr = 0; /* Temporarily zero until real addr is assigned */
|
2013-02-27 03:09:35 +08:00
|
|
|
|
|
|
|
switch(mode & S_IFMT) {
|
|
|
|
case S_IFREG:
|
|
|
|
if ((dip->i_diskflags & GFS2_DIF_INHERIT_JDATA) ||
|
|
|
|
gfs2_tune_get(sdp, gt_new_files_jdata))
|
|
|
|
ip->i_diskflags |= GFS2_DIF_JDATA;
|
|
|
|
gfs2_set_aops(inode);
|
|
|
|
break;
|
|
|
|
case S_IFDIR:
|
|
|
|
ip->i_diskflags |= (dip->i_diskflags & GFS2_DIF_INHERIT_JDATA);
|
|
|
|
ip->i_diskflags |= GFS2_DIF_JDATA;
|
|
|
|
ip->i_entries = 2;
|
|
|
|
break;
|
|
|
|
}
|
2015-11-11 05:07:26 +08:00
|
|
|
|
|
|
|
/* Force SYSTEM flag on all files and subdirs of a SYSTEM directory */
|
|
|
|
if (dip->i_diskflags & GFS2_DIF_SYSTEM)
|
|
|
|
ip->i_diskflags |= GFS2_DIF_SYSTEM;
|
|
|
|
|
2013-02-27 03:09:35 +08:00
|
|
|
gfs2_set_inode_flags(inode);
|
2011-05-09 21:06:38 +08:00
|
|
|
|
2015-03-18 06:25:59 +08:00
|
|
|
if ((GFS2_I(d_inode(sdp->sd_root_dir)) == dip) ||
|
2012-10-31 18:37:10 +08:00
|
|
|
(dip->i_diskflags & GFS2_DIF_TOPDIR))
|
|
|
|
aflags |= GFS2_AF_ORLOV;
|
|
|
|
|
2014-02-04 23:45:11 +08:00
|
|
|
if (default_acl || acl)
|
|
|
|
blocks++;
|
|
|
|
|
|
|
|
error = alloc_dinode(ip, aflags, &blocks);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
2012-10-31 18:30:22 +08:00
|
|
|
goto fail_free_inode;
|
2011-05-09 21:06:38 +08:00
|
|
|
|
2014-02-04 23:45:11 +08:00
|
|
|
gfs2_set_inode_blocks(inode, blocks);
|
|
|
|
|
2012-10-31 18:30:22 +08:00
|
|
|
error = gfs2_glock_get(sdp, ip->i_no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
2012-10-31 18:30:22 +08:00
|
|
|
goto fail_free_inode;
|
2011-05-09 21:06:38 +08:00
|
|
|
|
2012-11-21 22:56:00 +08:00
|
|
|
ip->i_gl->gl_object = ip;
|
2012-10-31 18:30:22 +08:00
|
|
|
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_SKIP, ghs + 1);
|
|
|
|
if (error)
|
|
|
|
goto fail_free_inode;
|
|
|
|
|
2014-02-04 23:45:11 +08:00
|
|
|
error = gfs2_trans_begin(sdp, blocks, 0);
|
2012-10-31 18:30:22 +08:00
|
|
|
if (error)
|
2011-05-09 21:06:38 +08:00
|
|
|
goto fail_gunlock2;
|
|
|
|
|
2014-02-04 23:45:11 +08:00
|
|
|
if (blocks > 1) {
|
|
|
|
ip->i_eattr = ip->i_no_addr + 1;
|
|
|
|
gfs2_init_xattr(ip);
|
|
|
|
}
|
2013-03-01 17:29:12 +08:00
|
|
|
init_dinode(dip, ip, symname);
|
2013-02-27 00:15:20 +08:00
|
|
|
gfs2_trans_end(sdp);
|
|
|
|
|
2012-10-31 18:30:22 +08:00
|
|
|
error = gfs2_glock_get(sdp, ip->i_no_addr, &gfs2_iopen_glops, CREATE, &io_gl);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
|
|
|
goto fail_gunlock2;
|
|
|
|
|
2015-12-08 06:24:27 +08:00
|
|
|
BUG_ON(test_and_set_bit(GLF_INODE_CREATING, &io_gl->gl_flags));
|
|
|
|
|
2012-10-31 18:30:22 +08:00
|
|
|
error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, GL_EXACT, &ip->i_iopen_gh);
|
2012-09-10 17:03:50 +08:00
|
|
|
if (error)
|
|
|
|
goto fail_gunlock2;
|
2012-06-06 18:17:59 +08:00
|
|
|
|
2012-10-31 18:30:22 +08:00
|
|
|
ip->i_iopen_gh.gh_gl->gl_object = ip;
|
|
|
|
gfs2_glock_put(io_gl);
|
|
|
|
gfs2_set_iop(inode);
|
|
|
|
insert_inode_hash(inode);
|
|
|
|
|
2015-12-05 00:19:14 +08:00
|
|
|
free_vfs_inode = 0; /* After this point, the inode is no longer
|
|
|
|
considered free. Any failures need to undo
|
|
|
|
the gfs2 structures. */
|
2013-12-20 21:16:52 +08:00
|
|
|
if (default_acl) {
|
2016-05-13 09:59:17 +08:00
|
|
|
error = __gfs2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
|
2013-12-20 21:16:52 +08:00
|
|
|
posix_acl_release(default_acl);
|
|
|
|
}
|
|
|
|
if (acl) {
|
|
|
|
if (!error)
|
2016-05-13 09:59:17 +08:00
|
|
|
error = __gfs2_set_acl(inode, acl, ACL_TYPE_ACCESS);
|
2013-12-20 21:16:52 +08:00
|
|
|
posix_acl_release(acl);
|
|
|
|
}
|
|
|
|
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
2012-10-31 18:30:22 +08:00
|
|
|
goto fail_gunlock3;
|
2011-05-09 21:06:38 +08:00
|
|
|
|
2014-03-19 21:37:00 +08:00
|
|
|
error = security_inode_init_security(&ip->i_inode, &dip->i_inode, name,
|
|
|
|
&gfs2_initxattrs, NULL);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
2012-10-31 18:30:22 +08:00
|
|
|
goto fail_gunlock3;
|
2011-05-09 21:06:38 +08:00
|
|
|
|
2014-01-06 19:28:41 +08:00
|
|
|
error = link_dinode(dip, name, ip, &da);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
2012-10-31 18:30:22 +08:00
|
|
|
goto fail_gunlock3;
|
2011-05-09 21:06:38 +08:00
|
|
|
|
2013-03-01 17:29:12 +08:00
|
|
|
mark_inode_dirty(inode);
|
2013-06-14 18:17:15 +08:00
|
|
|
d_instantiate(dentry, inode);
|
2013-09-16 20:52:03 +08:00
|
|
|
if (file) {
|
|
|
|
*opened |= FILE_CREATED;
|
2013-06-14 18:17:15 +08:00
|
|
|
error = finish_open(file, dentry, gfs2_open_common, opened);
|
2013-09-16 20:52:03 +08:00
|
|
|
}
|
2013-02-27 03:09:35 +08:00
|
|
|
gfs2_glock_dq_uninit(ghs);
|
|
|
|
gfs2_glock_dq_uninit(ghs + 1);
|
2015-12-08 06:24:27 +08:00
|
|
|
clear_bit(GLF_INODE_CREATING, &io_gl->gl_flags);
|
2013-06-14 18:17:15 +08:00
|
|
|
return error;
|
2011-05-09 21:06:38 +08:00
|
|
|
|
2012-10-31 18:30:22 +08:00
|
|
|
fail_gunlock3:
|
2015-12-05 00:19:14 +08:00
|
|
|
gfs2_glock_dq_uninit(&ip->i_iopen_gh);
|
|
|
|
gfs2_glock_put(io_gl);
|
2011-05-09 21:06:38 +08:00
|
|
|
fail_gunlock2:
|
2015-12-08 06:24:27 +08:00
|
|
|
if (io_gl)
|
|
|
|
clear_bit(GLF_INODE_CREATING, &io_gl->gl_flags);
|
2011-05-09 21:06:38 +08:00
|
|
|
gfs2_glock_dq_uninit(ghs + 1);
|
2012-10-31 18:30:22 +08:00
|
|
|
fail_free_inode:
|
|
|
|
if (ip->i_gl)
|
|
|
|
gfs2_glock_put(ip->i_gl);
|
2015-10-26 23:40:28 +08:00
|
|
|
gfs2_rsqa_delete(ip, NULL);
|
2013-12-20 21:16:52 +08:00
|
|
|
fail_free_acls:
|
|
|
|
if (default_acl)
|
|
|
|
posix_acl_release(default_acl);
|
|
|
|
if (acl)
|
|
|
|
posix_acl_release(acl);
|
2011-05-09 21:06:38 +08:00
|
|
|
fail_gunlock:
|
2014-01-06 20:49:43 +08:00
|
|
|
gfs2_dir_no_add(&da);
|
2011-05-13 19:11:17 +08:00
|
|
|
gfs2_glock_dq_uninit(ghs);
|
2011-08-02 20:17:27 +08:00
|
|
|
if (inode && !IS_ERR(inode)) {
|
2013-03-01 17:29:12 +08:00
|
|
|
clear_nlink(inode);
|
2014-03-31 23:33:17 +08:00
|
|
|
if (!free_vfs_inode)
|
|
|
|
mark_inode_dirty(inode);
|
|
|
|
set_bit(free_vfs_inode ? GIF_FREE_VFS_INODE : GIF_ALLOC_FAILED,
|
|
|
|
&GFS2_I(inode)->i_flags);
|
2011-08-02 20:17:27 +08:00
|
|
|
iput(inode);
|
|
|
|
}
|
2011-05-09 21:06:38 +08:00
|
|
|
fail:
|
2011-05-13 19:11:17 +08:00
|
|
|
return error;
|
2011-05-09 21:06:38 +08:00
|
|
|
}
|
2011-05-13 19:11:17 +08:00
|
|
|
|
2006-01-17 00:50:04 +08:00
|
|
|
/**
|
|
|
|
* gfs2_create - Create a file
|
|
|
|
* @dir: The directory in which to create the file
|
|
|
|
* @dentry: The dentry of the new file
|
|
|
|
* @mode: The mode of the new file
|
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int gfs2_create(struct inode *dir, struct dentry *dentry,
|
2012-06-11 06:05:36 +08:00
|
|
|
umode_t mode, bool excl)
|
2006-01-17 00:50:04 +08:00
|
|
|
{
|
2013-06-14 18:17:15 +08:00
|
|
|
return gfs2_create_inode(dir, dentry, NULL, S_IFREG | mode, 0, NULL, 0, excl, NULL);
|
2006-01-17 00:50:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-06-14 18:17:15 +08:00
|
|
|
* __gfs2_lookup - Look up a filename in a directory and return its inode
|
2006-01-17 00:50:04 +08:00
|
|
|
* @dir: The directory inode
|
|
|
|
* @dentry: The dentry of the new inode
|
2013-06-14 18:17:15 +08:00
|
|
|
* @file: File to be opened
|
|
|
|
* @opened: atomic_open flags
|
2006-01-17 00:50:04 +08:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
2013-06-14 18:17:15 +08:00
|
|
|
static struct dentry *__gfs2_lookup(struct inode *dir, struct dentry *dentry,
|
|
|
|
struct file *file, int *opened)
|
2006-01-17 00:50:04 +08:00
|
|
|
{
|
2013-06-14 18:17:15 +08:00
|
|
|
struct inode *inode;
|
|
|
|
struct dentry *d;
|
|
|
|
struct gfs2_holder gh;
|
|
|
|
struct gfs2_glock *gl;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
inode = gfs2_lookupi(dir, &dentry->d_name, 0);
|
2014-09-11 02:09:20 +08:00
|
|
|
if (inode == NULL) {
|
|
|
|
d_add(dentry, NULL);
|
2013-06-14 18:17:15 +08:00
|
|
|
return NULL;
|
2014-09-11 02:09:20 +08:00
|
|
|
}
|
2013-06-14 18:17:15 +08:00
|
|
|
if (IS_ERR(inode))
|
|
|
|
return ERR_CAST(inode);
|
|
|
|
|
|
|
|
gl = GFS2_I(inode)->i_gl;
|
|
|
|
error = gfs2_glock_nq_init(gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
|
|
|
|
if (error) {
|
|
|
|
iput(inode);
|
|
|
|
return ERR_PTR(error);
|
2008-01-08 16:14:30 +08:00
|
|
|
}
|
2013-06-14 18:17:15 +08:00
|
|
|
|
|
|
|
d = d_splice_alias(inode, dentry);
|
2014-01-17 02:51:07 +08:00
|
|
|
if (IS_ERR(d)) {
|
|
|
|
gfs2_glock_dq_uninit(&gh);
|
|
|
|
return d;
|
|
|
|
}
|
2013-06-14 18:17:15 +08:00
|
|
|
if (file && S_ISREG(inode->i_mode))
|
|
|
|
error = finish_open(file, dentry, gfs2_open_common, opened);
|
|
|
|
|
|
|
|
gfs2_glock_dq_uninit(&gh);
|
2013-09-23 20:21:04 +08:00
|
|
|
if (error) {
|
|
|
|
dput(d);
|
2013-06-14 18:17:15 +08:00
|
|
|
return ERR_PTR(error);
|
2013-09-23 20:21:04 +08:00
|
|
|
}
|
2013-06-14 18:17:15 +08:00
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry,
|
|
|
|
unsigned flags)
|
|
|
|
{
|
|
|
|
return __gfs2_lookup(dir, dentry, NULL, NULL);
|
2006-01-17 00:50:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gfs2_link - Link to a file
|
|
|
|
* @old_dentry: The inode to link
|
|
|
|
* @dir: Add link to this directory
|
|
|
|
* @dentry: The name of the link
|
|
|
|
*
|
|
|
|
* Link the inode in "old_dentry" into the directory "dir" with the
|
|
|
|
* name in "dentry".
|
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
|
|
|
|
struct dentry *dentry)
|
|
|
|
{
|
2006-06-15 03:32:57 +08:00
|
|
|
struct gfs2_inode *dip = GFS2_I(dir);
|
|
|
|
struct gfs2_sbd *sdp = GFS2_SB(dir);
|
2015-03-18 06:25:59 +08:00
|
|
|
struct inode *inode = d_inode(old_dentry);
|
2006-06-15 03:32:57 +08:00
|
|
|
struct gfs2_inode *ip = GFS2_I(inode);
|
2006-01-17 00:50:04 +08:00
|
|
|
struct gfs2_holder ghs[2];
|
2011-05-09 19:08:36 +08:00
|
|
|
struct buffer_head *dibh;
|
2014-09-29 20:52:04 +08:00
|
|
|
struct gfs2_diradd da = { .bh = NULL, .save_loc = 1, };
|
2006-01-17 00:50:04 +08:00
|
|
|
int error;
|
|
|
|
|
2006-11-02 01:22:46 +08:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
2006-01-17 00:50:04 +08:00
|
|
|
return -EPERM;
|
|
|
|
|
2015-10-26 23:40:28 +08:00
|
|
|
error = gfs2_rsqa_alloc(dip);
|
2012-06-06 18:17:59 +08:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
|
|
|
|
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1);
|
|
|
|
|
2008-08-13 02:39:29 +08:00
|
|
|
error = gfs2_glock_nq(ghs); /* parent */
|
|
|
|
if (error)
|
|
|
|
goto out_parent;
|
|
|
|
|
|
|
|
error = gfs2_glock_nq(ghs + 1); /* child */
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
2008-08-13 02:39:29 +08:00
|
|
|
goto out_child;
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2011-05-05 19:35:40 +08:00
|
|
|
error = -ENOENT;
|
|
|
|
if (inode->i_nlink == 0)
|
|
|
|
goto out_gunlock;
|
|
|
|
|
2011-06-21 07:28:19 +08:00
|
|
|
error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
|
2007-05-15 22:37:50 +08:00
|
|
|
error = gfs2_dir_check(dir, &dentry->d_name, NULL);
|
2006-01-17 00:50:04 +08:00
|
|
|
switch (error) {
|
|
|
|
case -ENOENT:
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
error = -EEXIST;
|
|
|
|
default:
|
|
|
|
goto out_gunlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = -EINVAL;
|
2006-11-02 03:04:17 +08:00
|
|
|
if (!dip->i_inode.i_nlink)
|
2006-01-17 00:50:04 +08:00
|
|
|
goto out_gunlock;
|
|
|
|
error = -EFBIG;
|
2008-11-03 21:59:19 +08:00
|
|
|
if (dip->i_entries == (u32)-1)
|
2006-01-17 00:50:04 +08:00
|
|
|
goto out_gunlock;
|
|
|
|
error = -EPERM;
|
|
|
|
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
|
|
|
|
goto out_gunlock;
|
|
|
|
error = -EINVAL;
|
2006-11-02 03:04:17 +08:00
|
|
|
if (!ip->i_inode.i_nlink)
|
2006-01-17 00:50:04 +08:00
|
|
|
goto out_gunlock;
|
|
|
|
error = -EMLINK;
|
2006-11-02 03:04:17 +08:00
|
|
|
if (ip->i_inode.i_nlink == (u32)-1)
|
2006-01-17 00:50:04 +08:00
|
|
|
goto out_gunlock;
|
|
|
|
|
2014-01-06 19:28:41 +08:00
|
|
|
error = gfs2_diradd_alloc_required(dir, &dentry->d_name, &da);
|
2006-03-21 01:30:04 +08:00
|
|
|
if (error < 0)
|
2006-01-17 00:50:04 +08:00
|
|
|
goto out_gunlock;
|
|
|
|
|
2014-01-06 19:28:41 +08:00
|
|
|
if (da.nr_blocks) {
|
|
|
|
struct gfs2_alloc_parms ap = { .target = da.nr_blocks, };
|
2015-03-19 01:03:41 +08:00
|
|
|
error = gfs2_quota_lock_check(dip, &ap);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
2012-05-18 21:28:23 +08:00
|
|
|
goto out_gunlock;
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2013-10-02 18:13:25 +08:00
|
|
|
error = gfs2_inplace_reserve(dip, &ap);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out_gunlock_q;
|
|
|
|
|
2014-01-06 20:03:05 +08:00
|
|
|
error = gfs2_trans_begin(sdp, gfs2_trans_da_blks(dip, &da, 2), 0);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out_ipres;
|
|
|
|
} else {
|
|
|
|
error = gfs2_trans_begin(sdp, 2 * RES_DINODE + RES_LEAF, 0);
|
|
|
|
if (error)
|
|
|
|
goto out_ipres;
|
|
|
|
}
|
|
|
|
|
2011-05-09 19:08:36 +08:00
|
|
|
error = gfs2_meta_inode_buffer(ip, &dibh);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out_end_trans;
|
|
|
|
|
2014-01-06 20:49:43 +08:00
|
|
|
error = gfs2_dir_add(dir, &dentry->d_name, ip, &da);
|
2011-05-09 19:08:36 +08:00
|
|
|
if (error)
|
|
|
|
goto out_brelse;
|
|
|
|
|
2012-12-14 20:36:02 +08:00
|
|
|
gfs2_trans_add_meta(ip->i_gl, dibh);
|
2011-05-09 19:08:36 +08:00
|
|
|
inc_nlink(&ip->i_inode);
|
2016-09-14 22:48:04 +08:00
|
|
|
ip->i_inode.i_ctime = current_time(&ip->i_inode);
|
2011-08-15 21:20:36 +08:00
|
|
|
ihold(inode);
|
|
|
|
d_instantiate(dentry, inode);
|
|
|
|
mark_inode_dirty(inode);
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2011-05-09 19:08:36 +08:00
|
|
|
out_brelse:
|
|
|
|
brelse(dibh);
|
2006-06-15 03:32:57 +08:00
|
|
|
out_end_trans:
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_trans_end(sdp);
|
2006-06-15 03:32:57 +08:00
|
|
|
out_ipres:
|
2014-01-06 19:28:41 +08:00
|
|
|
if (da.nr_blocks)
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_inplace_release(dip);
|
2006-06-15 03:32:57 +08:00
|
|
|
out_gunlock_q:
|
2014-01-06 19:28:41 +08:00
|
|
|
if (da.nr_blocks)
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_quota_unlock(dip);
|
2006-06-15 03:32:57 +08:00
|
|
|
out_gunlock:
|
2014-01-06 20:49:43 +08:00
|
|
|
gfs2_dir_no_add(&da);
|
2008-08-13 02:39:29 +08:00
|
|
|
gfs2_glock_dq(ghs + 1);
|
|
|
|
out_child:
|
|
|
|
gfs2_glock_dq(ghs);
|
|
|
|
out_parent:
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_holder_uninit(ghs);
|
|
|
|
gfs2_holder_uninit(ghs + 1);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2009-05-22 17:54:50 +08:00
|
|
|
/*
|
|
|
|
* gfs2_unlink_ok - check to see that a inode is still in a directory
|
|
|
|
* @dip: the directory
|
|
|
|
* @name: the name of the file
|
|
|
|
* @ip: the inode
|
|
|
|
*
|
|
|
|
* Assumes that the lock on (at least) @dip is held.
|
|
|
|
*
|
|
|
|
* Returns: 0 if the parent/child relationship is correct, errno if it isn't
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
|
|
|
|
const struct gfs2_inode *ip)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (IS_IMMUTABLE(&ip->i_inode) || IS_APPEND(&ip->i_inode))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if ((dip->i_inode.i_mode & S_ISVTX) &&
|
2013-02-01 13:56:13 +08:00
|
|
|
!uid_eq(dip->i_inode.i_uid, current_fsuid()) &&
|
|
|
|
!uid_eq(ip->i_inode.i_uid, current_fsuid()) && !capable(CAP_FOWNER))
|
2009-05-22 17:54:50 +08:00
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (IS_APPEND(&dip->i_inode))
|
|
|
|
return -EPERM;
|
|
|
|
|
2011-06-21 07:28:19 +08:00
|
|
|
error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC);
|
2009-05-22 17:54:50 +08:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2014-10-10 04:49:21 +08:00
|
|
|
return gfs2_dir_check(&dip->i_inode, name, ip);
|
2009-05-22 17:54:50 +08:00
|
|
|
}
|
|
|
|
|
2006-01-17 00:50:04 +08:00
|
|
|
/**
|
2011-05-09 23:42:37 +08:00
|
|
|
* gfs2_unlink_inode - Removes an inode from its parent dir and unlinks it
|
|
|
|
* @dip: The parent directory
|
|
|
|
* @name: The name of the entry in the parent directory
|
|
|
|
* @inode: The inode to be removed
|
|
|
|
*
|
|
|
|
* Called with all the locks and in a transaction. This will only be
|
|
|
|
* called for a directory after it has been checked to ensure it is empty.
|
|
|
|
*
|
|
|
|
* Returns: 0 on success, or an error
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int gfs2_unlink_inode(struct gfs2_inode *dip,
|
2012-11-13 02:03:29 +08:00
|
|
|
const struct dentry *dentry)
|
2011-05-09 23:42:37 +08:00
|
|
|
{
|
2015-03-18 06:25:59 +08:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2011-05-09 23:42:37 +08:00
|
|
|
struct gfs2_inode *ip = GFS2_I(inode);
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = gfs2_dir_del(dip, dentry);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
ip->i_entries = 0;
|
2016-09-14 22:48:04 +08:00
|
|
|
inode->i_ctime = current_time(inode);
|
2011-05-09 23:42:37 +08:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
clear_nlink(inode);
|
|
|
|
else
|
|
|
|
drop_nlink(inode);
|
|
|
|
mark_inode_dirty(inode);
|
|
|
|
if (inode->i_nlink == 0)
|
|
|
|
gfs2_unlink_di(inode);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gfs2_unlink - Unlink an inode (this does rmdir as well)
|
|
|
|
* @dir: The inode of the directory containing the inode to unlink
|
2006-01-17 00:50:04 +08:00
|
|
|
* @dentry: The file itself
|
|
|
|
*
|
2011-05-09 23:42:37 +08:00
|
|
|
* This routine uses the type of the inode as a flag to figure out
|
|
|
|
* whether this is an unlink or an rmdir.
|
2006-01-17 00:50:04 +08:00
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int gfs2_unlink(struct inode *dir, struct dentry *dentry)
|
|
|
|
{
|
2006-06-15 03:32:57 +08:00
|
|
|
struct gfs2_inode *dip = GFS2_I(dir);
|
|
|
|
struct gfs2_sbd *sdp = GFS2_SB(dir);
|
2015-03-18 06:25:59 +08:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2011-05-09 23:42:37 +08:00
|
|
|
struct gfs2_inode *ip = GFS2_I(inode);
|
2007-01-30 07:13:44 +08:00
|
|
|
struct gfs2_holder ghs[3];
|
|
|
|
struct gfs2_rgrpd *rgd;
|
2012-04-05 10:11:16 +08:00
|
|
|
int error;
|
|
|
|
|
|
|
|
error = gfs2_rindex_update(sdp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
error = -EROFS;
|
2006-01-17 00:50:04 +08:00
|
|
|
|
|
|
|
gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
|
2007-01-30 07:13:44 +08:00
|
|
|
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1);
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2012-02-08 20:58:32 +08:00
|
|
|
rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr, 1);
|
2012-02-24 23:09:14 +08:00
|
|
|
if (!rgd)
|
2011-11-08 22:04:20 +08:00
|
|
|
goto out_inodes;
|
2012-02-24 23:09:14 +08:00
|
|
|
|
2007-01-30 07:13:44 +08:00
|
|
|
gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + 2);
|
|
|
|
|
|
|
|
|
2007-08-26 21:23:56 +08:00
|
|
|
error = gfs2_glock_nq(ghs); /* parent */
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
2007-08-26 21:23:56 +08:00
|
|
|
goto out_parent;
|
|
|
|
|
|
|
|
error = gfs2_glock_nq(ghs + 1); /* child */
|
|
|
|
if (error)
|
|
|
|
goto out_child;
|
|
|
|
|
2011-05-05 19:35:40 +08:00
|
|
|
error = -ENOENT;
|
2011-05-09 23:42:37 +08:00
|
|
|
if (inode->i_nlink == 0)
|
2011-05-05 19:35:40 +08:00
|
|
|
goto out_rgrp;
|
|
|
|
|
2011-05-09 23:42:37 +08:00
|
|
|
if (S_ISDIR(inode->i_mode)) {
|
|
|
|
error = -ENOTEMPTY;
|
|
|
|
if (ip->i_entries > 2 || inode->i_nlink > 2)
|
|
|
|
goto out_rgrp;
|
|
|
|
}
|
|
|
|
|
2007-08-26 21:23:56 +08:00
|
|
|
error = gfs2_glock_nq(ghs + 2); /* rgrp */
|
|
|
|
if (error)
|
|
|
|
goto out_rgrp;
|
2006-01-17 00:50:04 +08:00
|
|
|
|
|
|
|
error = gfs2_unlink_ok(dip, &dentry->d_name, ip);
|
|
|
|
if (error)
|
2008-08-13 02:39:29 +08:00
|
|
|
goto out_gunlock;
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2011-05-09 23:42:37 +08:00
|
|
|
error = gfs2_trans_begin(sdp, 2*RES_DINODE + 3*RES_LEAF + RES_RG_BIT, 0);
|
|
|
|
if (error)
|
|
|
|
goto out_end_trans;
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2012-11-13 02:03:29 +08:00
|
|
|
error = gfs2_unlink_inode(dip, dentry);
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2006-06-15 03:32:57 +08:00
|
|
|
out_end_trans:
|
|
|
|
gfs2_trans_end(sdp);
|
2008-08-13 02:39:29 +08:00
|
|
|
out_gunlock:
|
2007-08-26 21:23:56 +08:00
|
|
|
gfs2_glock_dq(ghs + 2);
|
|
|
|
out_rgrp:
|
|
|
|
gfs2_glock_dq(ghs + 1);
|
|
|
|
out_child:
|
|
|
|
gfs2_glock_dq(ghs);
|
|
|
|
out_parent:
|
2011-11-08 22:04:20 +08:00
|
|
|
gfs2_holder_uninit(ghs + 2);
|
|
|
|
out_inodes:
|
|
|
|
gfs2_holder_uninit(ghs + 1);
|
2007-08-26 21:23:56 +08:00
|
|
|
gfs2_holder_uninit(ghs);
|
2006-01-17 00:50:04 +08:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gfs2_symlink - Create a symlink
|
|
|
|
* @dir: The directory to create the symlink in
|
|
|
|
* @dentry: The dentry to put the symlink in
|
|
|
|
* @symname: The thing which the link points to
|
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int gfs2_symlink(struct inode *dir, struct dentry *dentry,
|
|
|
|
const char *symname)
|
|
|
|
{
|
2006-06-15 03:32:57 +08:00
|
|
|
struct gfs2_sbd *sdp = GFS2_SB(dir);
|
2011-05-13 17:34:59 +08:00
|
|
|
unsigned int size;
|
2006-01-17 00:50:04 +08:00
|
|
|
|
|
|
|
size = strlen(symname);
|
|
|
|
if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode) - 1)
|
|
|
|
return -ENAMETOOLONG;
|
|
|
|
|
2013-06-14 18:17:15 +08:00
|
|
|
return gfs2_create_inode(dir, dentry, NULL, S_IFLNK | S_IRWXUGO, 0, symname, size, 0, NULL);
|
2006-01-17 00:50:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gfs2_mkdir - Make a directory
|
|
|
|
* @dir: The parent directory of the new one
|
|
|
|
* @dentry: The dentry of the new directory
|
|
|
|
* @mode: The mode of the new directory
|
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
2011-07-26 13:41:39 +08:00
|
|
|
static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
2006-01-17 00:50:04 +08:00
|
|
|
{
|
2013-02-27 03:09:35 +08:00
|
|
|
struct gfs2_sbd *sdp = GFS2_SB(dir);
|
|
|
|
unsigned dsize = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode);
|
2013-06-14 18:17:15 +08:00
|
|
|
return gfs2_create_inode(dir, dentry, NULL, S_IFDIR | mode, 0, NULL, dsize, 0, NULL);
|
2006-01-17 00:50:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gfs2_mknod - Make a special file
|
|
|
|
* @dir: The directory in which the special file will reside
|
|
|
|
* @dentry: The dentry of the special file
|
|
|
|
* @mode: The mode of the special file
|
2011-05-13 19:11:17 +08:00
|
|
|
* @dev: The device specification of the special file
|
2006-01-17 00:50:04 +08:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2011-07-26 13:52:52 +08:00
|
|
|
static int gfs2_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
2006-01-17 00:50:04 +08:00
|
|
|
dev_t dev)
|
|
|
|
{
|
2013-06-14 18:17:15 +08:00
|
|
|
return gfs2_create_inode(dir, dentry, NULL, mode, dev, NULL, 0, 0, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gfs2_atomic_open - Atomically open a file
|
|
|
|
* @dir: The directory
|
|
|
|
* @dentry: The proposed new entry
|
|
|
|
* @file: The proposed new struct file
|
|
|
|
* @flags: open flags
|
|
|
|
* @mode: File mode
|
|
|
|
* @opened: Flag to say whether the file has been opened or not
|
|
|
|
*
|
|
|
|
* Returns: error code or 0 for success
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int gfs2_atomic_open(struct inode *dir, struct dentry *dentry,
|
2015-05-02 01:54:38 +08:00
|
|
|
struct file *file, unsigned flags,
|
|
|
|
umode_t mode, int *opened)
|
2013-06-14 18:17:15 +08:00
|
|
|
{
|
|
|
|
struct dentry *d;
|
|
|
|
bool excl = !!(flags & O_EXCL);
|
|
|
|
|
2016-07-05 21:44:53 +08:00
|
|
|
if (!d_in_lookup(dentry))
|
2014-09-13 06:21:05 +08:00
|
|
|
goto skip_lookup;
|
|
|
|
|
2013-06-14 18:17:15 +08:00
|
|
|
d = __gfs2_lookup(dir, dentry, file, opened);
|
|
|
|
if (IS_ERR(d))
|
|
|
|
return PTR_ERR(d);
|
2013-09-23 20:21:04 +08:00
|
|
|
if (d != NULL)
|
|
|
|
dentry = d;
|
2015-03-18 06:25:59 +08:00
|
|
|
if (d_really_is_positive(dentry)) {
|
2014-11-20 03:35:58 +08:00
|
|
|
if (!(*opened & FILE_OPENED))
|
|
|
|
return finish_no_open(file, d);
|
2013-09-23 20:21:04 +08:00
|
|
|
dput(d);
|
2013-06-14 18:17:15 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-09-23 20:21:04 +08:00
|
|
|
BUG_ON(d != NULL);
|
2014-09-13 06:21:05 +08:00
|
|
|
|
|
|
|
skip_lookup:
|
2013-06-14 18:17:15 +08:00
|
|
|
if (!(flags & O_CREAT))
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
return gfs2_create_inode(dir, dentry, file, S_IFREG | mode, 0, NULL, 0, excl, opened);
|
2006-01-17 00:50:04 +08:00
|
|
|
}
|
|
|
|
|
2008-08-26 16:38:26 +08:00
|
|
|
/*
|
|
|
|
* gfs2_ok_to_move - check if it's ok to move a directory to another directory
|
|
|
|
* @this: move this
|
|
|
|
* @to: to here
|
|
|
|
*
|
|
|
|
* Follow @to back to the root and make sure we don't encounter @this
|
|
|
|
* Assumes we already hold the rename lock.
|
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to)
|
|
|
|
{
|
|
|
|
struct inode *dir = &to->i_inode;
|
|
|
|
struct super_block *sb = dir->i_sb;
|
|
|
|
struct inode *tmp;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
igrab(dir);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (dir == &this->i_inode) {
|
|
|
|
error = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2015-03-18 06:25:59 +08:00
|
|
|
if (dir == d_inode(sb->s_root)) {
|
2008-08-26 16:38:26 +08:00
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-09-17 19:30:23 +08:00
|
|
|
tmp = gfs2_lookupi(dir, &gfs2_qdotdot, 1);
|
2014-03-12 16:41:44 +08:00
|
|
|
if (!tmp) {
|
|
|
|
error = -ENOENT;
|
|
|
|
break;
|
|
|
|
}
|
2008-08-26 16:38:26 +08:00
|
|
|
if (IS_ERR(tmp)) {
|
|
|
|
error = PTR_ERR(tmp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
iput(dir);
|
|
|
|
dir = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
iput(dir);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2015-05-06 01:12:19 +08:00
|
|
|
/**
|
|
|
|
* update_moved_ino - Update an inode that's being moved
|
|
|
|
* @ip: The inode being moved
|
|
|
|
* @ndip: The parent directory of the new filename
|
|
|
|
* @dir_rename: True of ip is a directory
|
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int update_moved_ino(struct gfs2_inode *ip, struct gfs2_inode *ndip,
|
|
|
|
int dir_rename)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
struct buffer_head *dibh;
|
|
|
|
|
|
|
|
if (dir_rename)
|
|
|
|
return gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR);
|
|
|
|
|
|
|
|
error = gfs2_meta_inode_buffer(ip, &dibh);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2016-09-14 22:48:04 +08:00
|
|
|
ip->i_inode.i_ctime = current_time(&ip->i_inode);
|
2015-05-06 01:12:19 +08:00
|
|
|
gfs2_trans_add_meta(ip->i_gl, dibh);
|
|
|
|
gfs2_dinode_out(ip, dibh->b_data);
|
|
|
|
brelse(dibh);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-17 00:50:04 +08:00
|
|
|
/**
|
|
|
|
* gfs2_rename - Rename a file
|
|
|
|
* @odir: Parent directory of old file name
|
|
|
|
* @odentry: The old dentry of the file
|
|
|
|
* @ndir: Parent directory of new file name
|
|
|
|
* @ndentry: The new dentry of the file
|
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int gfs2_rename(struct inode *odir, struct dentry *odentry,
|
|
|
|
struct inode *ndir, struct dentry *ndentry)
|
|
|
|
{
|
2006-06-15 03:32:57 +08:00
|
|
|
struct gfs2_inode *odip = GFS2_I(odir);
|
|
|
|
struct gfs2_inode *ndip = GFS2_I(ndir);
|
2015-03-18 06:25:59 +08:00
|
|
|
struct gfs2_inode *ip = GFS2_I(d_inode(odentry));
|
2006-01-17 00:50:04 +08:00
|
|
|
struct gfs2_inode *nip = NULL;
|
2006-06-15 03:32:57 +08:00
|
|
|
struct gfs2_sbd *sdp = GFS2_SB(odir);
|
2016-06-17 20:31:27 +08:00
|
|
|
struct gfs2_holder ghs[5], r_gh;
|
2007-01-30 07:13:44 +08:00
|
|
|
struct gfs2_rgrpd *nrgd;
|
2006-01-17 00:50:04 +08:00
|
|
|
unsigned int num_gh;
|
|
|
|
int dir_rename = 0;
|
2014-09-29 20:52:04 +08:00
|
|
|
struct gfs2_diradd da = { .nr_blocks = 0, .save_loc = 0, };
|
2006-01-17 00:50:04 +08:00
|
|
|
unsigned int x;
|
|
|
|
int error;
|
|
|
|
|
2016-06-17 20:31:27 +08:00
|
|
|
gfs2_holder_mark_uninitialized(&r_gh);
|
2015-03-18 06:25:59 +08:00
|
|
|
if (d_really_is_positive(ndentry)) {
|
|
|
|
nip = GFS2_I(d_inode(ndentry));
|
2006-01-17 00:50:04 +08:00
|
|
|
if (ip == nip)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-05 10:11:16 +08:00
|
|
|
error = gfs2_rindex_update(sdp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2015-10-26 23:40:28 +08:00
|
|
|
error = gfs2_rsqa_alloc(ndip);
|
2012-06-06 18:17:59 +08:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2008-08-26 16:38:26 +08:00
|
|
|
if (odip != ndip) {
|
|
|
|
error = gfs2_glock_nq_init(sdp->sd_rename_gl, LM_ST_EXCLUSIVE,
|
|
|
|
0, &r_gh);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
|
2008-08-26 16:38:26 +08:00
|
|
|
if (S_ISDIR(ip->i_inode.i_mode)) {
|
|
|
|
dir_rename = 1;
|
2015-05-06 01:12:19 +08:00
|
|
|
/* don't move a directory into its subdir */
|
2008-08-26 16:38:26 +08:00
|
|
|
error = gfs2_ok_to_move(ip, ndip);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock_r;
|
|
|
|
}
|
2006-01-17 00:50:04 +08:00
|
|
|
}
|
|
|
|
|
2006-06-22 03:38:17 +08:00
|
|
|
num_gh = 1;
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
|
2006-06-22 03:38:17 +08:00
|
|
|
if (odip != ndip) {
|
|
|
|
gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
|
|
|
|
num_gh++;
|
|
|
|
}
|
|
|
|
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
|
|
|
|
num_gh++;
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2006-06-22 03:38:17 +08:00
|
|
|
if (nip) {
|
|
|
|
gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
|
|
|
|
num_gh++;
|
2007-01-30 07:13:44 +08:00
|
|
|
/* grab the resource lock for unlink flag twiddling
|
|
|
|
* this is the case of the target file already existing
|
|
|
|
* so we unlink before doing the rename
|
|
|
|
*/
|
2012-02-08 20:58:32 +08:00
|
|
|
nrgd = gfs2_blk2rgrpd(sdp, nip->i_no_addr, 1);
|
2007-01-30 07:13:44 +08:00
|
|
|
if (nrgd)
|
|
|
|
gfs2_holder_init(nrgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh++);
|
2006-06-22 03:38:17 +08:00
|
|
|
}
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2008-08-13 02:39:29 +08:00
|
|
|
for (x = 0; x < num_gh; x++) {
|
|
|
|
error = gfs2_glock_nq(ghs + x);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
}
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2011-05-05 19:35:40 +08:00
|
|
|
error = -ENOENT;
|
|
|
|
if (ip->i_inode.i_nlink == 0)
|
|
|
|
goto out_gunlock;
|
|
|
|
|
2006-01-17 00:50:04 +08:00
|
|
|
/* Check out the old directory */
|
|
|
|
|
|
|
|
error = gfs2_unlink_ok(odip, &odentry->d_name, ip);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
|
|
|
|
/* Check out the new directory */
|
|
|
|
|
|
|
|
if (nip) {
|
|
|
|
error = gfs2_unlink_ok(ndip, &ndentry->d_name, nip);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
|
2011-05-05 19:35:40 +08:00
|
|
|
if (nip->i_inode.i_nlink == 0) {
|
|
|
|
error = -EAGAIN;
|
|
|
|
goto out_gunlock;
|
|
|
|
}
|
|
|
|
|
2006-11-02 01:22:46 +08:00
|
|
|
if (S_ISDIR(nip->i_inode.i_mode)) {
|
2008-11-03 21:59:19 +08:00
|
|
|
if (nip->i_entries < 2) {
|
2011-05-09 20:36:10 +08:00
|
|
|
gfs2_consist_inode(nip);
|
2006-01-17 00:50:04 +08:00
|
|
|
error = -EIO;
|
|
|
|
goto out_gunlock;
|
|
|
|
}
|
2008-11-03 21:59:19 +08:00
|
|
|
if (nip->i_entries > 2) {
|
2006-01-17 00:50:04 +08:00
|
|
|
error = -ENOTEMPTY;
|
|
|
|
goto out_gunlock;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2011-06-21 07:28:19 +08:00
|
|
|
error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
|
2007-05-15 22:37:50 +08:00
|
|
|
error = gfs2_dir_check(ndir, &ndentry->d_name, NULL);
|
2006-01-17 00:50:04 +08:00
|
|
|
switch (error) {
|
|
|
|
case -ENOENT:
|
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
error = -EEXIST;
|
|
|
|
default:
|
|
|
|
goto out_gunlock;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (odip != ndip) {
|
2006-11-02 03:04:17 +08:00
|
|
|
if (!ndip->i_inode.i_nlink) {
|
2011-05-05 19:35:40 +08:00
|
|
|
error = -ENOENT;
|
2006-01-17 00:50:04 +08:00
|
|
|
goto out_gunlock;
|
|
|
|
}
|
2008-11-03 21:59:19 +08:00
|
|
|
if (ndip->i_entries == (u32)-1) {
|
2006-01-17 00:50:04 +08:00
|
|
|
error = -EFBIG;
|
|
|
|
goto out_gunlock;
|
|
|
|
}
|
2006-11-02 01:22:46 +08:00
|
|
|
if (S_ISDIR(ip->i_inode.i_mode) &&
|
2006-11-02 03:04:17 +08:00
|
|
|
ndip->i_inode.i_nlink == (u32)-1) {
|
2006-01-17 00:50:04 +08:00
|
|
|
error = -EMLINK;
|
|
|
|
goto out_gunlock;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check out the dir to be renamed */
|
|
|
|
|
|
|
|
if (dir_rename) {
|
2015-03-18 06:25:59 +08:00
|
|
|
error = gfs2_permission(d_inode(odentry), MAY_WRITE);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
}
|
|
|
|
|
2014-01-06 19:28:41 +08:00
|
|
|
if (nip == NULL) {
|
|
|
|
error = gfs2_diradd_alloc_required(ndir, &ndentry->d_name, &da);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
}
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2014-01-06 19:28:41 +08:00
|
|
|
if (da.nr_blocks) {
|
|
|
|
struct gfs2_alloc_parms ap = { .target = da.nr_blocks, };
|
2015-03-19 01:03:41 +08:00
|
|
|
error = gfs2_quota_lock_check(ndip, &ap);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
2012-05-18 21:28:23 +08:00
|
|
|
goto out_gunlock;
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2013-10-02 18:13:25 +08:00
|
|
|
error = gfs2_inplace_reserve(ndip, &ap);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out_gunlock_q;
|
|
|
|
|
2014-01-06 20:03:05 +08:00
|
|
|
error = gfs2_trans_begin(sdp, gfs2_trans_da_blks(ndip, &da, 4) +
|
|
|
|
4 * RES_LEAF + 4, 0);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out_ipreserv;
|
|
|
|
} else {
|
|
|
|
error = gfs2_trans_begin(sdp, 4 * RES_DINODE +
|
2007-01-19 05:07:03 +08:00
|
|
|
5 * RES_LEAF + 4, 0);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove the target file, if it exists */
|
|
|
|
|
2012-11-13 02:03:29 +08:00
|
|
|
if (nip)
|
|
|
|
error = gfs2_unlink_inode(ndip, ndentry);
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2015-05-06 01:12:19 +08:00
|
|
|
error = update_moved_ino(ip, ndip, dir_rename);
|
|
|
|
if (error)
|
|
|
|
goto out_end_trans;
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2011-05-09 23:42:37 +08:00
|
|
|
error = gfs2_dir_del(odip, odentry);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out_end_trans;
|
|
|
|
|
2014-01-06 20:49:43 +08:00
|
|
|
error = gfs2_dir_add(ndir, &ndentry->d_name, ip, &da);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out_end_trans;
|
|
|
|
|
2006-06-15 03:32:57 +08:00
|
|
|
out_end_trans:
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_trans_end(sdp);
|
2006-06-15 03:32:57 +08:00
|
|
|
out_ipreserv:
|
2014-01-06 19:28:41 +08:00
|
|
|
if (da.nr_blocks)
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_inplace_release(ndip);
|
2006-06-15 03:32:57 +08:00
|
|
|
out_gunlock_q:
|
2014-01-06 19:28:41 +08:00
|
|
|
if (da.nr_blocks)
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_quota_unlock(ndip);
|
2006-06-15 03:32:57 +08:00
|
|
|
out_gunlock:
|
2014-01-06 20:49:43 +08:00
|
|
|
gfs2_dir_no_add(&da);
|
2008-08-13 02:39:29 +08:00
|
|
|
while (x--) {
|
|
|
|
gfs2_glock_dq(ghs + x);
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_holder_uninit(ghs + x);
|
2008-08-13 02:39:29 +08:00
|
|
|
}
|
2006-06-15 03:32:57 +08:00
|
|
|
out_gunlock_r:
|
2016-06-17 20:31:27 +08:00
|
|
|
if (gfs2_holder_initialized(&r_gh))
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_glock_dq_uninit(&r_gh);
|
2006-06-15 03:32:57 +08:00
|
|
|
out:
|
2006-01-17 00:50:04 +08:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2015-05-06 01:12:19 +08:00
|
|
|
/**
|
|
|
|
* gfs2_exchange - exchange two files
|
|
|
|
* @odir: Parent directory of old file name
|
|
|
|
* @odentry: The old dentry of the file
|
|
|
|
* @ndir: Parent directory of new file name
|
|
|
|
* @ndentry: The new dentry of the file
|
|
|
|
* @flags: The rename flags
|
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int gfs2_exchange(struct inode *odir, struct dentry *odentry,
|
|
|
|
struct inode *ndir, struct dentry *ndentry,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
struct gfs2_inode *odip = GFS2_I(odir);
|
|
|
|
struct gfs2_inode *ndip = GFS2_I(ndir);
|
|
|
|
struct gfs2_inode *oip = GFS2_I(odentry->d_inode);
|
|
|
|
struct gfs2_inode *nip = GFS2_I(ndentry->d_inode);
|
|
|
|
struct gfs2_sbd *sdp = GFS2_SB(odir);
|
2016-06-17 20:31:27 +08:00
|
|
|
struct gfs2_holder ghs[5], r_gh;
|
2015-05-06 01:12:19 +08:00
|
|
|
unsigned int num_gh;
|
|
|
|
unsigned int x;
|
|
|
|
umode_t old_mode = oip->i_inode.i_mode;
|
|
|
|
umode_t new_mode = nip->i_inode.i_mode;
|
|
|
|
int error;
|
|
|
|
|
2016-06-17 20:31:27 +08:00
|
|
|
gfs2_holder_mark_uninitialized(&r_gh);
|
2015-05-06 01:12:19 +08:00
|
|
|
error = gfs2_rindex_update(sdp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (odip != ndip) {
|
|
|
|
error = gfs2_glock_nq_init(sdp->sd_rename_gl, LM_ST_EXCLUSIVE,
|
|
|
|
0, &r_gh);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (S_ISDIR(old_mode)) {
|
|
|
|
/* don't move a directory into its subdir */
|
|
|
|
error = gfs2_ok_to_move(oip, ndip);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock_r;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (S_ISDIR(new_mode)) {
|
|
|
|
/* don't move a directory into its subdir */
|
|
|
|
error = gfs2_ok_to_move(nip, odip);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock_r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
num_gh = 1;
|
|
|
|
gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
|
|
|
|
if (odip != ndip) {
|
|
|
|
gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
|
|
|
|
num_gh++;
|
|
|
|
}
|
|
|
|
gfs2_holder_init(oip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
|
|
|
|
num_gh++;
|
|
|
|
|
|
|
|
gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
|
|
|
|
num_gh++;
|
|
|
|
|
|
|
|
for (x = 0; x < num_gh; x++) {
|
|
|
|
error = gfs2_glock_nq(ghs + x);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = -ENOENT;
|
|
|
|
if (oip->i_inode.i_nlink == 0 || nip->i_inode.i_nlink == 0)
|
|
|
|
goto out_gunlock;
|
|
|
|
|
|
|
|
error = gfs2_unlink_ok(odip, &odentry->d_name, oip);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
error = gfs2_unlink_ok(ndip, &ndentry->d_name, nip);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
|
|
|
|
if (S_ISDIR(old_mode)) {
|
|
|
|
error = gfs2_permission(odentry->d_inode, MAY_WRITE);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
}
|
|
|
|
if (S_ISDIR(new_mode)) {
|
|
|
|
error = gfs2_permission(ndentry->d_inode, MAY_WRITE);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
}
|
|
|
|
error = gfs2_trans_begin(sdp, 4 * RES_DINODE + 4 * RES_LEAF, 0);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock;
|
|
|
|
|
|
|
|
error = update_moved_ino(oip, ndip, S_ISDIR(old_mode));
|
|
|
|
if (error)
|
|
|
|
goto out_end_trans;
|
|
|
|
|
|
|
|
error = update_moved_ino(nip, odip, S_ISDIR(new_mode));
|
|
|
|
if (error)
|
|
|
|
goto out_end_trans;
|
|
|
|
|
|
|
|
error = gfs2_dir_mvino(ndip, &ndentry->d_name, oip,
|
|
|
|
IF2DT(old_mode));
|
|
|
|
if (error)
|
|
|
|
goto out_end_trans;
|
|
|
|
|
|
|
|
error = gfs2_dir_mvino(odip, &odentry->d_name, nip,
|
|
|
|
IF2DT(new_mode));
|
|
|
|
if (error)
|
|
|
|
goto out_end_trans;
|
|
|
|
|
|
|
|
if (odip != ndip) {
|
|
|
|
if (S_ISDIR(new_mode) && !S_ISDIR(old_mode)) {
|
|
|
|
inc_nlink(&odip->i_inode);
|
|
|
|
drop_nlink(&ndip->i_inode);
|
|
|
|
} else if (S_ISDIR(old_mode) && !S_ISDIR(new_mode)) {
|
|
|
|
inc_nlink(&ndip->i_inode);
|
|
|
|
drop_nlink(&odip->i_inode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mark_inode_dirty(&ndip->i_inode);
|
|
|
|
if (odip != ndip)
|
|
|
|
mark_inode_dirty(&odip->i_inode);
|
|
|
|
|
|
|
|
out_end_trans:
|
|
|
|
gfs2_trans_end(sdp);
|
|
|
|
out_gunlock:
|
|
|
|
while (x--) {
|
|
|
|
gfs2_glock_dq(ghs + x);
|
|
|
|
gfs2_holder_uninit(ghs + x);
|
|
|
|
}
|
|
|
|
out_gunlock_r:
|
2016-06-17 20:31:27 +08:00
|
|
|
if (gfs2_holder_initialized(&r_gh))
|
2015-05-06 01:12:19 +08:00
|
|
|
gfs2_glock_dq_uninit(&r_gh);
|
|
|
|
out:
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gfs2_rename2(struct inode *odir, struct dentry *odentry,
|
|
|
|
struct inode *ndir, struct dentry *ndentry,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
flags &= ~RENAME_NOREPLACE;
|
|
|
|
|
|
|
|
if (flags & ~RENAME_EXCHANGE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (flags & RENAME_EXCHANGE)
|
|
|
|
return gfs2_exchange(odir, odentry, ndir, ndentry, flags);
|
|
|
|
|
|
|
|
return gfs2_rename(odir, odentry, ndir, ndentry);
|
|
|
|
}
|
|
|
|
|
2009-05-22 17:48:59 +08:00
|
|
|
/**
|
2015-11-17 23:20:54 +08:00
|
|
|
* gfs2_get_link - Follow a symbolic link
|
2010-01-14 13:59:16 +08:00
|
|
|
* @dentry: The dentry of the link
|
2015-11-17 23:20:54 +08:00
|
|
|
* @inode: The inode of the link
|
2015-12-30 04:58:39 +08:00
|
|
|
* @done: destructor for return value
|
2009-05-22 17:48:59 +08:00
|
|
|
*
|
2010-01-14 13:59:16 +08:00
|
|
|
* This can handle symlinks of any size.
|
2009-05-22 17:48:59 +08:00
|
|
|
*
|
2010-01-14 13:59:16 +08:00
|
|
|
* Returns: 0 on success or error code
|
2009-05-22 17:48:59 +08:00
|
|
|
*/
|
|
|
|
|
2015-11-17 23:20:54 +08:00
|
|
|
static const char *gfs2_get_link(struct dentry *dentry,
|
2015-12-30 04:58:39 +08:00
|
|
|
struct inode *inode,
|
|
|
|
struct delayed_call *done)
|
2009-05-22 17:48:59 +08:00
|
|
|
{
|
2015-11-17 23:20:54 +08:00
|
|
|
struct gfs2_inode *ip = GFS2_I(inode);
|
2009-05-22 17:48:59 +08:00
|
|
|
struct gfs2_holder i_gh;
|
|
|
|
struct buffer_head *dibh;
|
2011-05-13 17:34:59 +08:00
|
|
|
unsigned int size;
|
2010-01-14 13:59:16 +08:00
|
|
|
char *buf;
|
2009-05-22 17:48:59 +08:00
|
|
|
int error;
|
|
|
|
|
2015-11-17 23:20:54 +08:00
|
|
|
if (!dentry)
|
|
|
|
return ERR_PTR(-ECHILD);
|
|
|
|
|
2009-05-22 17:48:59 +08:00
|
|
|
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh);
|
|
|
|
error = gfs2_glock_nq(&i_gh);
|
|
|
|
if (error) {
|
|
|
|
gfs2_holder_uninit(&i_gh);
|
2015-05-03 01:32:22 +08:00
|
|
|
return ERR_PTR(error);
|
2009-05-22 17:48:59 +08:00
|
|
|
}
|
|
|
|
|
2010-08-11 16:53:11 +08:00
|
|
|
size = (unsigned int)i_size_read(&ip->i_inode);
|
|
|
|
if (size == 0) {
|
2009-05-22 17:48:59 +08:00
|
|
|
gfs2_consist_inode(ip);
|
2010-01-14 13:59:16 +08:00
|
|
|
buf = ERR_PTR(-EIO);
|
2009-05-22 17:48:59 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = gfs2_meta_inode_buffer(ip, &dibh);
|
2010-01-14 13:59:16 +08:00
|
|
|
if (error) {
|
|
|
|
buf = ERR_PTR(error);
|
2009-05-22 17:48:59 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2011-05-13 17:34:59 +08:00
|
|
|
buf = kzalloc(size + 1, GFP_NOFS);
|
2010-01-14 13:59:16 +08:00
|
|
|
if (!buf)
|
|
|
|
buf = ERR_PTR(-ENOMEM);
|
|
|
|
else
|
2011-05-13 17:34:59 +08:00
|
|
|
memcpy(buf, dibh->b_data + sizeof(struct gfs2_dinode), size);
|
2009-05-22 17:48:59 +08:00
|
|
|
brelse(dibh);
|
|
|
|
out:
|
|
|
|
gfs2_glock_dq_uninit(&i_gh);
|
2015-05-03 01:32:22 +08:00
|
|
|
if (!IS_ERR(buf))
|
2015-12-30 04:58:39 +08:00
|
|
|
set_delayed_call(done, kfree_link, buf);
|
2015-05-03 01:32:22 +08:00
|
|
|
return buf;
|
2006-01-17 00:50:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gfs2_permission -
|
2011-01-19 17:42:40 +08:00
|
|
|
* @inode: The inode
|
|
|
|
* @mask: The mask to be tested
|
|
|
|
* @flags: Indicates whether this is an RCU path walk or not
|
2006-01-17 00:50:04 +08:00
|
|
|
*
|
2006-11-27 22:55:28 +08:00
|
|
|
* This may be called from the VFS directly, or from within GFS2 with the
|
|
|
|
* inode locked, so we look to see if the glock is already locked and only
|
|
|
|
* lock the glock if its not already been done.
|
|
|
|
*
|
2006-01-17 00:50:04 +08:00
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
2011-06-21 07:28:19 +08:00
|
|
|
int gfs2_permission(struct inode *inode, int mask)
|
2006-01-17 00:50:04 +08:00
|
|
|
{
|
2011-01-07 14:49:58 +08:00
|
|
|
struct gfs2_inode *ip;
|
2006-01-17 00:50:04 +08:00
|
|
|
struct gfs2_holder i_gh;
|
|
|
|
int error;
|
2011-01-07 14:49:58 +08:00
|
|
|
|
2016-06-17 20:31:27 +08:00
|
|
|
gfs2_holder_mark_uninitialized(&i_gh);
|
2011-01-07 14:49:58 +08:00
|
|
|
ip = GFS2_I(inode);
|
2008-02-23 00:07:18 +08:00
|
|
|
if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
|
2014-11-14 10:42:04 +08:00
|
|
|
if (mask & MAY_NOT_BLOCK)
|
|
|
|
return -ECHILD;
|
|
|
|
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2006-11-27 22:55:28 +08:00
|
|
|
}
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2008-07-03 03:12:01 +08:00
|
|
|
if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
|
2016-08-02 19:58:28 +08:00
|
|
|
error = -EPERM;
|
2008-07-03 03:12:01 +08:00
|
|
|
else
|
2011-06-21 07:16:29 +08:00
|
|
|
error = generic_permission(inode, mask);
|
2016-06-17 20:31:27 +08:00
|
|
|
if (gfs2_holder_initialized(&i_gh))
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_glock_dq_uninit(&i_gh);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2011-08-15 21:20:36 +08:00
|
|
|
static int __gfs2_setattr_simple(struct inode *inode, struct iattr *attr)
|
2011-05-09 21:06:38 +08:00
|
|
|
{
|
|
|
|
setattr_copy(inode, attr);
|
|
|
|
mark_inode_dirty(inode);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gfs2_setattr_simple -
|
|
|
|
* @ip:
|
|
|
|
* @attr:
|
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
2011-08-15 21:20:36 +08:00
|
|
|
int gfs2_setattr_simple(struct inode *inode, struct iattr *attr)
|
2011-05-09 21:06:38 +08:00
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (current->journal_info)
|
2011-08-15 21:20:36 +08:00
|
|
|
return __gfs2_setattr_simple(inode, attr);
|
2011-05-09 21:06:38 +08:00
|
|
|
|
2011-08-15 21:20:36 +08:00
|
|
|
error = gfs2_trans_begin(GFS2_SB(inode), RES_DINODE, 0);
|
2011-05-09 21:06:38 +08:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2011-08-15 21:20:36 +08:00
|
|
|
error = __gfs2_setattr_simple(inode, attr);
|
|
|
|
gfs2_trans_end(GFS2_SB(inode));
|
2011-05-09 21:06:38 +08:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2006-01-17 00:50:04 +08:00
|
|
|
static int setattr_chown(struct inode *inode, struct iattr *attr)
|
|
|
|
{
|
2006-06-15 03:32:57 +08:00
|
|
|
struct gfs2_inode *ip = GFS2_I(inode);
|
|
|
|
struct gfs2_sbd *sdp = GFS2_SB(inode);
|
2013-02-01 12:27:54 +08:00
|
|
|
kuid_t ouid, nuid;
|
|
|
|
kgid_t ogid, ngid;
|
2006-01-17 00:50:04 +08:00
|
|
|
int error;
|
2015-03-19 01:03:41 +08:00
|
|
|
struct gfs2_alloc_parms ap;
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2006-11-02 02:23:29 +08:00
|
|
|
ouid = inode->i_uid;
|
|
|
|
ogid = inode->i_gid;
|
2006-01-17 00:50:04 +08:00
|
|
|
nuid = attr->ia_uid;
|
|
|
|
ngid = attr->ia_gid;
|
|
|
|
|
2013-02-01 13:56:13 +08:00
|
|
|
if (!(attr->ia_valid & ATTR_UID) || uid_eq(ouid, nuid))
|
2013-02-01 09:49:26 +08:00
|
|
|
ouid = nuid = NO_UID_QUOTA_CHANGE;
|
2013-02-01 13:56:13 +08:00
|
|
|
if (!(attr->ia_valid & ATTR_GID) || gid_eq(ogid, ngid))
|
2013-02-01 09:49:26 +08:00
|
|
|
ogid = ngid = NO_GID_QUOTA_CHANGE;
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2015-10-26 23:40:28 +08:00
|
|
|
error = gfs2_rsqa_alloc(ip);
|
2014-01-07 06:16:01 +08:00
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
error = gfs2_rindex_update(sdp);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
error = gfs2_quota_lock(ip, nuid, ngid);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
|
2015-03-19 01:03:41 +08:00
|
|
|
ap.target = gfs2_get_inode_blocks(&ip->i_inode);
|
|
|
|
|
2013-02-01 13:56:13 +08:00
|
|
|
if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) ||
|
|
|
|
!gid_eq(ogid, NO_GID_QUOTA_CHANGE)) {
|
2015-03-19 01:03:41 +08:00
|
|
|
error = gfs2_quota_check(ip, nuid, ngid, &ap);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out_gunlock_q;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = gfs2_trans_begin(sdp, RES_DINODE + 2 * RES_QUOTA, 0);
|
|
|
|
if (error)
|
|
|
|
goto out_gunlock_q;
|
|
|
|
|
2011-08-15 21:20:36 +08:00
|
|
|
error = gfs2_setattr_simple(inode, attr);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out_end_trans;
|
|
|
|
|
2013-02-01 13:56:13 +08:00
|
|
|
if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) ||
|
|
|
|
!gid_eq(ogid, NO_GID_QUOTA_CHANGE)) {
|
2015-06-03 00:02:24 +08:00
|
|
|
gfs2_quota_change(ip, -(s64)ap.target, ouid, ogid);
|
2015-03-19 01:03:41 +08:00
|
|
|
gfs2_quota_change(ip, ap.target, nuid, ngid);
|
2006-01-17 00:50:04 +08:00
|
|
|
}
|
|
|
|
|
2006-09-05 00:04:26 +08:00
|
|
|
out_end_trans:
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_trans_end(sdp);
|
2006-09-05 00:04:26 +08:00
|
|
|
out_gunlock_q:
|
2006-01-17 00:50:04 +08:00
|
|
|
gfs2_quota_unlock(ip);
|
2014-01-07 06:16:01 +08:00
|
|
|
out:
|
2006-01-17 00:50:04 +08:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gfs2_setattr - Change attributes on an inode
|
|
|
|
* @dentry: The dentry which is changing
|
|
|
|
* @attr: The structure describing the change
|
|
|
|
*
|
|
|
|
* The VFS layer wants to change one or more of an inodes attributes. Write
|
|
|
|
* that change out to disk.
|
|
|
|
*
|
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int gfs2_setattr(struct dentry *dentry, struct iattr *attr)
|
|
|
|
{
|
2015-03-18 06:25:59 +08:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2006-06-15 03:32:57 +08:00
|
|
|
struct gfs2_inode *ip = GFS2_I(inode);
|
2006-01-17 00:50:04 +08:00
|
|
|
struct gfs2_holder i_gh;
|
|
|
|
int error;
|
|
|
|
|
2015-10-26 23:40:28 +08:00
|
|
|
error = gfs2_rsqa_alloc(ip);
|
2012-06-06 18:17:59 +08:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2006-01-17 00:50:04 +08:00
|
|
|
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
error = -EPERM;
|
|
|
|
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
|
|
|
|
goto out;
|
|
|
|
|
2016-05-26 22:55:18 +08:00
|
|
|
error = setattr_prepare(dentry, attr);
|
2006-01-17 00:50:04 +08:00
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (attr->ia_valid & ATTR_SIZE)
|
2010-08-11 16:37:53 +08:00
|
|
|
error = gfs2_setattr_size(inode, attr->ia_size);
|
2006-01-17 00:50:04 +08:00
|
|
|
else if (attr->ia_valid & (ATTR_UID | ATTR_GID))
|
|
|
|
error = setattr_chown(inode, attr);
|
2013-12-20 21:16:52 +08:00
|
|
|
else {
|
2011-08-15 21:20:36 +08:00
|
|
|
error = gfs2_setattr_simple(inode, attr);
|
2013-12-20 21:16:52 +08:00
|
|
|
if (!error && attr->ia_valid & ATTR_MODE)
|
|
|
|
error = posix_acl_chmod(inode, inode->i_mode);
|
|
|
|
}
|
2006-01-17 00:50:04 +08:00
|
|
|
|
2006-09-05 00:04:26 +08:00
|
|
|
out:
|
2006-01-17 00:50:04 +08:00
|
|
|
if (!error)
|
|
|
|
mark_inode_dirty(inode);
|
2011-08-15 21:20:36 +08:00
|
|
|
gfs2_glock_dq_uninit(&i_gh);
|
2006-01-17 00:50:04 +08:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gfs2_getattr - Read out an inode's attributes
|
statx: Add a system call to make enhanced file info available
Add a system call to make extended file information available, including
file creation and some attribute flags where available through the
underlying filesystem.
The getattr inode operation is altered to take two additional arguments: a
u32 request_mask and an unsigned int flags that indicate the
synchronisation mode. This change is propagated to the vfs_getattr*()
function.
Functions like vfs_stat() are now inline wrappers around new functions
vfs_statx() and vfs_statx_fd() to reduce stack usage.
========
OVERVIEW
========
The idea was initially proposed as a set of xattrs that could be retrieved
with getxattr(), but the general preference proved to be for a new syscall
with an extended stat structure.
A number of requests were gathered for features to be included. The
following have been included:
(1) Make the fields a consistent size on all arches and make them large.
(2) Spare space, request flags and information flags are provided for
future expansion.
(3) Better support for the y2038 problem [Arnd Bergmann] (tv_sec is an
__s64).
(4) Creation time: The SMB protocol carries the creation time, which could
be exported by Samba, which will in turn help CIFS make use of
FS-Cache as that can be used for coherency data (stx_btime).
This is also specified in NFSv4 as a recommended attribute and could
be exported by NFSD [Steve French].
(5) Lightweight stat: Ask for just those details of interest, and allow a
netfs (such as NFS) to approximate anything not of interest, possibly
without going to the server [Trond Myklebust, Ulrich Drepper, Andreas
Dilger] (AT_STATX_DONT_SYNC).
(6) Heavyweight stat: Force a netfs to go to the server, even if it thinks
its cached attributes are up to date [Trond Myklebust]
(AT_STATX_FORCE_SYNC).
And the following have been left out for future extension:
(7) Data version number: Could be used by userspace NFS servers [Aneesh
Kumar].
Can also be used to modify fill_post_wcc() in NFSD which retrieves
i_version directly, but has just called vfs_getattr(). It could get
it from the kstat struct if it used vfs_xgetattr() instead.
(There's disagreement on the exact semantics of a single field, since
not all filesystems do this the same way).
(8) BSD stat compatibility: Including more fields from the BSD stat such
as creation time (st_btime) and inode generation number (st_gen)
[Jeremy Allison, Bernd Schubert].
(9) Inode generation number: Useful for FUSE and userspace NFS servers
[Bernd Schubert].
(This was asked for but later deemed unnecessary with the
open-by-handle capability available and caused disagreement as to
whether it's a security hole or not).
(10) Extra coherency data may be useful in making backups [Andreas Dilger].
(No particular data were offered, but things like last backup
timestamp, the data version number and the DOS archive bit would come
into this category).
(11) Allow the filesystem to indicate what it can/cannot provide: A
filesystem can now say it doesn't support a standard stat feature if
that isn't available, so if, for instance, inode numbers or UIDs don't
exist or are fabricated locally...
(This requires a separate system call - I have an fsinfo() call idea
for this).
(12) Store a 16-byte volume ID in the superblock that can be returned in
struct xstat [Steve French].
(Deferred to fsinfo).
(13) Include granularity fields in the time data to indicate the
granularity of each of the times (NFSv4 time_delta) [Steve French].
(Deferred to fsinfo).
(14) FS_IOC_GETFLAGS value. These could be translated to BSD's st_flags.
Note that the Linux IOC flags are a mess and filesystems such as Ext4
define flags that aren't in linux/fs.h, so translation in the kernel
may be a necessity (or, possibly, we provide the filesystem type too).
(Some attributes are made available in stx_attributes, but the general
feeling was that the IOC flags were to ext[234]-specific and shouldn't
be exposed through statx this way).
(15) Mask of features available on file (eg: ACLs, seclabel) [Brad Boyer,
Michael Kerrisk].
(Deferred, probably to fsinfo. Finding out if there's an ACL or
seclabal might require extra filesystem operations).
(16) Femtosecond-resolution timestamps [Dave Chinner].
(A __reserved field has been left in the statx_timestamp struct for
this - if there proves to be a need).
(17) A set multiple attributes syscall to go with this.
===============
NEW SYSTEM CALL
===============
The new system call is:
int ret = statx(int dfd,
const char *filename,
unsigned int flags,
unsigned int mask,
struct statx *buffer);
The dfd, filename and flags parameters indicate the file to query, in a
similar way to fstatat(). There is no equivalent of lstat() as that can be
emulated with statx() by passing AT_SYMLINK_NOFOLLOW in flags. There is
also no equivalent of fstat() as that can be emulated by passing a NULL
filename to statx() with the fd of interest in dfd.
Whether or not statx() synchronises the attributes with the backing store
can be controlled by OR'ing a value into the flags argument (this typically
only affects network filesystems):
(1) AT_STATX_SYNC_AS_STAT tells statx() to behave as stat() does in this
respect.
(2) AT_STATX_FORCE_SYNC will require a network filesystem to synchronise
its attributes with the server - which might require data writeback to
occur to get the timestamps correct.
(3) AT_STATX_DONT_SYNC will suppress synchronisation with the server in a
network filesystem. The resulting values should be considered
approximate.
mask is a bitmask indicating the fields in struct statx that are of
interest to the caller. The user should set this to STATX_BASIC_STATS to
get the basic set returned by stat(). It should be noted that asking for
more information may entail extra I/O operations.
buffer points to the destination for the data. This must be 256 bytes in
size.
======================
MAIN ATTRIBUTES RECORD
======================
The following structures are defined in which to return the main attribute
set:
struct statx_timestamp {
__s64 tv_sec;
__s32 tv_nsec;
__s32 __reserved;
};
struct statx {
__u32 stx_mask;
__u32 stx_blksize;
__u64 stx_attributes;
__u32 stx_nlink;
__u32 stx_uid;
__u32 stx_gid;
__u16 stx_mode;
__u16 __spare0[1];
__u64 stx_ino;
__u64 stx_size;
__u64 stx_blocks;
__u64 __spare1[1];
struct statx_timestamp stx_atime;
struct statx_timestamp stx_btime;
struct statx_timestamp stx_ctime;
struct statx_timestamp stx_mtime;
__u32 stx_rdev_major;
__u32 stx_rdev_minor;
__u32 stx_dev_major;
__u32 stx_dev_minor;
__u64 __spare2[14];
};
The defined bits in request_mask and stx_mask are:
STATX_TYPE Want/got stx_mode & S_IFMT
STATX_MODE Want/got stx_mode & ~S_IFMT
STATX_NLINK Want/got stx_nlink
STATX_UID Want/got stx_uid
STATX_GID Want/got stx_gid
STATX_ATIME Want/got stx_atime{,_ns}
STATX_MTIME Want/got stx_mtime{,_ns}
STATX_CTIME Want/got stx_ctime{,_ns}
STATX_INO Want/got stx_ino
STATX_SIZE Want/got stx_size
STATX_BLOCKS Want/got stx_blocks
STATX_BASIC_STATS [The stuff in the normal stat struct]
STATX_BTIME Want/got stx_btime{,_ns}
STATX_ALL [All currently available stuff]
stx_btime is the file creation time, stx_mask is a bitmask indicating the
data provided and __spares*[] are where as-yet undefined fields can be
placed.
Time fields are structures with separate seconds and nanoseconds fields
plus a reserved field in case we want to add even finer resolution. Note
that times will be negative if before 1970; in such a case, the nanosecond
fields will also be negative if not zero.
The bits defined in the stx_attributes field convey information about a
file, how it is accessed, where it is and what it does. The following
attributes map to FS_*_FL flags and are the same numerical value:
STATX_ATTR_COMPRESSED File is compressed by the fs
STATX_ATTR_IMMUTABLE File is marked immutable
STATX_ATTR_APPEND File is append-only
STATX_ATTR_NODUMP File is not to be dumped
STATX_ATTR_ENCRYPTED File requires key to decrypt in fs
Within the kernel, the supported flags are listed by:
KSTAT_ATTR_FS_IOC_FLAGS
[Are any other IOC flags of sufficient general interest to be exposed
through this interface?]
New flags include:
STATX_ATTR_AUTOMOUNT Object is an automount trigger
These are for the use of GUI tools that might want to mark files specially,
depending on what they are.
Fields in struct statx come in a number of classes:
(0) stx_dev_*, stx_blksize.
These are local system information and are always available.
(1) stx_mode, stx_nlinks, stx_uid, stx_gid, stx_[amc]time, stx_ino,
stx_size, stx_blocks.
These will be returned whether the caller asks for them or not. The
corresponding bits in stx_mask will be set to indicate whether they
actually have valid values.
If the caller didn't ask for them, then they may be approximated. For
example, NFS won't waste any time updating them from the server,
unless as a byproduct of updating something requested.
If the values don't actually exist for the underlying object (such as
UID or GID on a DOS file), then the bit won't be set in the stx_mask,
even if the caller asked for the value. In such a case, the returned
value will be a fabrication.
Note that there are instances where the type might not be valid, for
instance Windows reparse points.
(2) stx_rdev_*.
This will be set only if stx_mode indicates we're looking at a
blockdev or a chardev, otherwise will be 0.
(3) stx_btime.
Similar to (1), except this will be set to 0 if it doesn't exist.
=======
TESTING
=======
The following test program can be used to test the statx system call:
samples/statx/test-statx.c
Just compile and run, passing it paths to the files you want to examine.
The file is built automatically if CONFIG_SAMPLES is enabled.
Here's some example output. Firstly, an NFS directory that crosses to
another FSID. Note that the AUTOMOUNT attribute is set because transiting
this directory will cause d_automount to be invoked by the VFS.
[root@andromeda ~]# /tmp/test-statx -A /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:26 Inode: 1703937 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Attributes: 0000000000001000 (-------- -------- -------- -------- -------- -------- ---m---- --------)
Secondly, the result of automounting on that directory.
[root@andromeda ~]# /tmp/test-statx /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:27 Inode: 2 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2017-02-01 00:46:22 +08:00
|
|
|
* @path: Object to query
|
2006-01-17 00:50:04 +08:00
|
|
|
* @stat: The inode's stats
|
statx: Add a system call to make enhanced file info available
Add a system call to make extended file information available, including
file creation and some attribute flags where available through the
underlying filesystem.
The getattr inode operation is altered to take two additional arguments: a
u32 request_mask and an unsigned int flags that indicate the
synchronisation mode. This change is propagated to the vfs_getattr*()
function.
Functions like vfs_stat() are now inline wrappers around new functions
vfs_statx() and vfs_statx_fd() to reduce stack usage.
========
OVERVIEW
========
The idea was initially proposed as a set of xattrs that could be retrieved
with getxattr(), but the general preference proved to be for a new syscall
with an extended stat structure.
A number of requests were gathered for features to be included. The
following have been included:
(1) Make the fields a consistent size on all arches and make them large.
(2) Spare space, request flags and information flags are provided for
future expansion.
(3) Better support for the y2038 problem [Arnd Bergmann] (tv_sec is an
__s64).
(4) Creation time: The SMB protocol carries the creation time, which could
be exported by Samba, which will in turn help CIFS make use of
FS-Cache as that can be used for coherency data (stx_btime).
This is also specified in NFSv4 as a recommended attribute and could
be exported by NFSD [Steve French].
(5) Lightweight stat: Ask for just those details of interest, and allow a
netfs (such as NFS) to approximate anything not of interest, possibly
without going to the server [Trond Myklebust, Ulrich Drepper, Andreas
Dilger] (AT_STATX_DONT_SYNC).
(6) Heavyweight stat: Force a netfs to go to the server, even if it thinks
its cached attributes are up to date [Trond Myklebust]
(AT_STATX_FORCE_SYNC).
And the following have been left out for future extension:
(7) Data version number: Could be used by userspace NFS servers [Aneesh
Kumar].
Can also be used to modify fill_post_wcc() in NFSD which retrieves
i_version directly, but has just called vfs_getattr(). It could get
it from the kstat struct if it used vfs_xgetattr() instead.
(There's disagreement on the exact semantics of a single field, since
not all filesystems do this the same way).
(8) BSD stat compatibility: Including more fields from the BSD stat such
as creation time (st_btime) and inode generation number (st_gen)
[Jeremy Allison, Bernd Schubert].
(9) Inode generation number: Useful for FUSE and userspace NFS servers
[Bernd Schubert].
(This was asked for but later deemed unnecessary with the
open-by-handle capability available and caused disagreement as to
whether it's a security hole or not).
(10) Extra coherency data may be useful in making backups [Andreas Dilger].
(No particular data were offered, but things like last backup
timestamp, the data version number and the DOS archive bit would come
into this category).
(11) Allow the filesystem to indicate what it can/cannot provide: A
filesystem can now say it doesn't support a standard stat feature if
that isn't available, so if, for instance, inode numbers or UIDs don't
exist or are fabricated locally...
(This requires a separate system call - I have an fsinfo() call idea
for this).
(12) Store a 16-byte volume ID in the superblock that can be returned in
struct xstat [Steve French].
(Deferred to fsinfo).
(13) Include granularity fields in the time data to indicate the
granularity of each of the times (NFSv4 time_delta) [Steve French].
(Deferred to fsinfo).
(14) FS_IOC_GETFLAGS value. These could be translated to BSD's st_flags.
Note that the Linux IOC flags are a mess and filesystems such as Ext4
define flags that aren't in linux/fs.h, so translation in the kernel
may be a necessity (or, possibly, we provide the filesystem type too).
(Some attributes are made available in stx_attributes, but the general
feeling was that the IOC flags were to ext[234]-specific and shouldn't
be exposed through statx this way).
(15) Mask of features available on file (eg: ACLs, seclabel) [Brad Boyer,
Michael Kerrisk].
(Deferred, probably to fsinfo. Finding out if there's an ACL or
seclabal might require extra filesystem operations).
(16) Femtosecond-resolution timestamps [Dave Chinner].
(A __reserved field has been left in the statx_timestamp struct for
this - if there proves to be a need).
(17) A set multiple attributes syscall to go with this.
===============
NEW SYSTEM CALL
===============
The new system call is:
int ret = statx(int dfd,
const char *filename,
unsigned int flags,
unsigned int mask,
struct statx *buffer);
The dfd, filename and flags parameters indicate the file to query, in a
similar way to fstatat(). There is no equivalent of lstat() as that can be
emulated with statx() by passing AT_SYMLINK_NOFOLLOW in flags. There is
also no equivalent of fstat() as that can be emulated by passing a NULL
filename to statx() with the fd of interest in dfd.
Whether or not statx() synchronises the attributes with the backing store
can be controlled by OR'ing a value into the flags argument (this typically
only affects network filesystems):
(1) AT_STATX_SYNC_AS_STAT tells statx() to behave as stat() does in this
respect.
(2) AT_STATX_FORCE_SYNC will require a network filesystem to synchronise
its attributes with the server - which might require data writeback to
occur to get the timestamps correct.
(3) AT_STATX_DONT_SYNC will suppress synchronisation with the server in a
network filesystem. The resulting values should be considered
approximate.
mask is a bitmask indicating the fields in struct statx that are of
interest to the caller. The user should set this to STATX_BASIC_STATS to
get the basic set returned by stat(). It should be noted that asking for
more information may entail extra I/O operations.
buffer points to the destination for the data. This must be 256 bytes in
size.
======================
MAIN ATTRIBUTES RECORD
======================
The following structures are defined in which to return the main attribute
set:
struct statx_timestamp {
__s64 tv_sec;
__s32 tv_nsec;
__s32 __reserved;
};
struct statx {
__u32 stx_mask;
__u32 stx_blksize;
__u64 stx_attributes;
__u32 stx_nlink;
__u32 stx_uid;
__u32 stx_gid;
__u16 stx_mode;
__u16 __spare0[1];
__u64 stx_ino;
__u64 stx_size;
__u64 stx_blocks;
__u64 __spare1[1];
struct statx_timestamp stx_atime;
struct statx_timestamp stx_btime;
struct statx_timestamp stx_ctime;
struct statx_timestamp stx_mtime;
__u32 stx_rdev_major;
__u32 stx_rdev_minor;
__u32 stx_dev_major;
__u32 stx_dev_minor;
__u64 __spare2[14];
};
The defined bits in request_mask and stx_mask are:
STATX_TYPE Want/got stx_mode & S_IFMT
STATX_MODE Want/got stx_mode & ~S_IFMT
STATX_NLINK Want/got stx_nlink
STATX_UID Want/got stx_uid
STATX_GID Want/got stx_gid
STATX_ATIME Want/got stx_atime{,_ns}
STATX_MTIME Want/got stx_mtime{,_ns}
STATX_CTIME Want/got stx_ctime{,_ns}
STATX_INO Want/got stx_ino
STATX_SIZE Want/got stx_size
STATX_BLOCKS Want/got stx_blocks
STATX_BASIC_STATS [The stuff in the normal stat struct]
STATX_BTIME Want/got stx_btime{,_ns}
STATX_ALL [All currently available stuff]
stx_btime is the file creation time, stx_mask is a bitmask indicating the
data provided and __spares*[] are where as-yet undefined fields can be
placed.
Time fields are structures with separate seconds and nanoseconds fields
plus a reserved field in case we want to add even finer resolution. Note
that times will be negative if before 1970; in such a case, the nanosecond
fields will also be negative if not zero.
The bits defined in the stx_attributes field convey information about a
file, how it is accessed, where it is and what it does. The following
attributes map to FS_*_FL flags and are the same numerical value:
STATX_ATTR_COMPRESSED File is compressed by the fs
STATX_ATTR_IMMUTABLE File is marked immutable
STATX_ATTR_APPEND File is append-only
STATX_ATTR_NODUMP File is not to be dumped
STATX_ATTR_ENCRYPTED File requires key to decrypt in fs
Within the kernel, the supported flags are listed by:
KSTAT_ATTR_FS_IOC_FLAGS
[Are any other IOC flags of sufficient general interest to be exposed
through this interface?]
New flags include:
STATX_ATTR_AUTOMOUNT Object is an automount trigger
These are for the use of GUI tools that might want to mark files specially,
depending on what they are.
Fields in struct statx come in a number of classes:
(0) stx_dev_*, stx_blksize.
These are local system information and are always available.
(1) stx_mode, stx_nlinks, stx_uid, stx_gid, stx_[amc]time, stx_ino,
stx_size, stx_blocks.
These will be returned whether the caller asks for them or not. The
corresponding bits in stx_mask will be set to indicate whether they
actually have valid values.
If the caller didn't ask for them, then they may be approximated. For
example, NFS won't waste any time updating them from the server,
unless as a byproduct of updating something requested.
If the values don't actually exist for the underlying object (such as
UID or GID on a DOS file), then the bit won't be set in the stx_mask,
even if the caller asked for the value. In such a case, the returned
value will be a fabrication.
Note that there are instances where the type might not be valid, for
instance Windows reparse points.
(2) stx_rdev_*.
This will be set only if stx_mode indicates we're looking at a
blockdev or a chardev, otherwise will be 0.
(3) stx_btime.
Similar to (1), except this will be set to 0 if it doesn't exist.
=======
TESTING
=======
The following test program can be used to test the statx system call:
samples/statx/test-statx.c
Just compile and run, passing it paths to the files you want to examine.
The file is built automatically if CONFIG_SAMPLES is enabled.
Here's some example output. Firstly, an NFS directory that crosses to
another FSID. Note that the AUTOMOUNT attribute is set because transiting
this directory will cause d_automount to be invoked by the VFS.
[root@andromeda ~]# /tmp/test-statx -A /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:26 Inode: 1703937 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Attributes: 0000000000001000 (-------- -------- -------- -------- -------- -------- ---m---- --------)
Secondly, the result of automounting on that directory.
[root@andromeda ~]# /tmp/test-statx /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:27 Inode: 2 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2017-02-01 00:46:22 +08:00
|
|
|
* @request_mask: Mask of STATX_xxx flags indicating the caller's interests
|
|
|
|
* @flags: AT_STATX_xxx setting
|
2006-01-17 00:50:04 +08:00
|
|
|
*
|
2006-11-27 23:12:05 +08:00
|
|
|
* This may be called from the VFS directly, or from within GFS2 with the
|
|
|
|
* inode locked, so we look to see if the glock is already locked and only
|
|
|
|
* lock the glock if its not already been done. Note that its the NFS
|
|
|
|
* readdirplus operation which causes this to be called (from filldir)
|
|
|
|
* with the glock already held.
|
|
|
|
*
|
2006-01-17 00:50:04 +08:00
|
|
|
* Returns: errno
|
|
|
|
*/
|
|
|
|
|
statx: Add a system call to make enhanced file info available
Add a system call to make extended file information available, including
file creation and some attribute flags where available through the
underlying filesystem.
The getattr inode operation is altered to take two additional arguments: a
u32 request_mask and an unsigned int flags that indicate the
synchronisation mode. This change is propagated to the vfs_getattr*()
function.
Functions like vfs_stat() are now inline wrappers around new functions
vfs_statx() and vfs_statx_fd() to reduce stack usage.
========
OVERVIEW
========
The idea was initially proposed as a set of xattrs that could be retrieved
with getxattr(), but the general preference proved to be for a new syscall
with an extended stat structure.
A number of requests were gathered for features to be included. The
following have been included:
(1) Make the fields a consistent size on all arches and make them large.
(2) Spare space, request flags and information flags are provided for
future expansion.
(3) Better support for the y2038 problem [Arnd Bergmann] (tv_sec is an
__s64).
(4) Creation time: The SMB protocol carries the creation time, which could
be exported by Samba, which will in turn help CIFS make use of
FS-Cache as that can be used for coherency data (stx_btime).
This is also specified in NFSv4 as a recommended attribute and could
be exported by NFSD [Steve French].
(5) Lightweight stat: Ask for just those details of interest, and allow a
netfs (such as NFS) to approximate anything not of interest, possibly
without going to the server [Trond Myklebust, Ulrich Drepper, Andreas
Dilger] (AT_STATX_DONT_SYNC).
(6) Heavyweight stat: Force a netfs to go to the server, even if it thinks
its cached attributes are up to date [Trond Myklebust]
(AT_STATX_FORCE_SYNC).
And the following have been left out for future extension:
(7) Data version number: Could be used by userspace NFS servers [Aneesh
Kumar].
Can also be used to modify fill_post_wcc() in NFSD which retrieves
i_version directly, but has just called vfs_getattr(). It could get
it from the kstat struct if it used vfs_xgetattr() instead.
(There's disagreement on the exact semantics of a single field, since
not all filesystems do this the same way).
(8) BSD stat compatibility: Including more fields from the BSD stat such
as creation time (st_btime) and inode generation number (st_gen)
[Jeremy Allison, Bernd Schubert].
(9) Inode generation number: Useful for FUSE and userspace NFS servers
[Bernd Schubert].
(This was asked for but later deemed unnecessary with the
open-by-handle capability available and caused disagreement as to
whether it's a security hole or not).
(10) Extra coherency data may be useful in making backups [Andreas Dilger].
(No particular data were offered, but things like last backup
timestamp, the data version number and the DOS archive bit would come
into this category).
(11) Allow the filesystem to indicate what it can/cannot provide: A
filesystem can now say it doesn't support a standard stat feature if
that isn't available, so if, for instance, inode numbers or UIDs don't
exist or are fabricated locally...
(This requires a separate system call - I have an fsinfo() call idea
for this).
(12) Store a 16-byte volume ID in the superblock that can be returned in
struct xstat [Steve French].
(Deferred to fsinfo).
(13) Include granularity fields in the time data to indicate the
granularity of each of the times (NFSv4 time_delta) [Steve French].
(Deferred to fsinfo).
(14) FS_IOC_GETFLAGS value. These could be translated to BSD's st_flags.
Note that the Linux IOC flags are a mess and filesystems such as Ext4
define flags that aren't in linux/fs.h, so translation in the kernel
may be a necessity (or, possibly, we provide the filesystem type too).
(Some attributes are made available in stx_attributes, but the general
feeling was that the IOC flags were to ext[234]-specific and shouldn't
be exposed through statx this way).
(15) Mask of features available on file (eg: ACLs, seclabel) [Brad Boyer,
Michael Kerrisk].
(Deferred, probably to fsinfo. Finding out if there's an ACL or
seclabal might require extra filesystem operations).
(16) Femtosecond-resolution timestamps [Dave Chinner].
(A __reserved field has been left in the statx_timestamp struct for
this - if there proves to be a need).
(17) A set multiple attributes syscall to go with this.
===============
NEW SYSTEM CALL
===============
The new system call is:
int ret = statx(int dfd,
const char *filename,
unsigned int flags,
unsigned int mask,
struct statx *buffer);
The dfd, filename and flags parameters indicate the file to query, in a
similar way to fstatat(). There is no equivalent of lstat() as that can be
emulated with statx() by passing AT_SYMLINK_NOFOLLOW in flags. There is
also no equivalent of fstat() as that can be emulated by passing a NULL
filename to statx() with the fd of interest in dfd.
Whether or not statx() synchronises the attributes with the backing store
can be controlled by OR'ing a value into the flags argument (this typically
only affects network filesystems):
(1) AT_STATX_SYNC_AS_STAT tells statx() to behave as stat() does in this
respect.
(2) AT_STATX_FORCE_SYNC will require a network filesystem to synchronise
its attributes with the server - which might require data writeback to
occur to get the timestamps correct.
(3) AT_STATX_DONT_SYNC will suppress synchronisation with the server in a
network filesystem. The resulting values should be considered
approximate.
mask is a bitmask indicating the fields in struct statx that are of
interest to the caller. The user should set this to STATX_BASIC_STATS to
get the basic set returned by stat(). It should be noted that asking for
more information may entail extra I/O operations.
buffer points to the destination for the data. This must be 256 bytes in
size.
======================
MAIN ATTRIBUTES RECORD
======================
The following structures are defined in which to return the main attribute
set:
struct statx_timestamp {
__s64 tv_sec;
__s32 tv_nsec;
__s32 __reserved;
};
struct statx {
__u32 stx_mask;
__u32 stx_blksize;
__u64 stx_attributes;
__u32 stx_nlink;
__u32 stx_uid;
__u32 stx_gid;
__u16 stx_mode;
__u16 __spare0[1];
__u64 stx_ino;
__u64 stx_size;
__u64 stx_blocks;
__u64 __spare1[1];
struct statx_timestamp stx_atime;
struct statx_timestamp stx_btime;
struct statx_timestamp stx_ctime;
struct statx_timestamp stx_mtime;
__u32 stx_rdev_major;
__u32 stx_rdev_minor;
__u32 stx_dev_major;
__u32 stx_dev_minor;
__u64 __spare2[14];
};
The defined bits in request_mask and stx_mask are:
STATX_TYPE Want/got stx_mode & S_IFMT
STATX_MODE Want/got stx_mode & ~S_IFMT
STATX_NLINK Want/got stx_nlink
STATX_UID Want/got stx_uid
STATX_GID Want/got stx_gid
STATX_ATIME Want/got stx_atime{,_ns}
STATX_MTIME Want/got stx_mtime{,_ns}
STATX_CTIME Want/got stx_ctime{,_ns}
STATX_INO Want/got stx_ino
STATX_SIZE Want/got stx_size
STATX_BLOCKS Want/got stx_blocks
STATX_BASIC_STATS [The stuff in the normal stat struct]
STATX_BTIME Want/got stx_btime{,_ns}
STATX_ALL [All currently available stuff]
stx_btime is the file creation time, stx_mask is a bitmask indicating the
data provided and __spares*[] are where as-yet undefined fields can be
placed.
Time fields are structures with separate seconds and nanoseconds fields
plus a reserved field in case we want to add even finer resolution. Note
that times will be negative if before 1970; in such a case, the nanosecond
fields will also be negative if not zero.
The bits defined in the stx_attributes field convey information about a
file, how it is accessed, where it is and what it does. The following
attributes map to FS_*_FL flags and are the same numerical value:
STATX_ATTR_COMPRESSED File is compressed by the fs
STATX_ATTR_IMMUTABLE File is marked immutable
STATX_ATTR_APPEND File is append-only
STATX_ATTR_NODUMP File is not to be dumped
STATX_ATTR_ENCRYPTED File requires key to decrypt in fs
Within the kernel, the supported flags are listed by:
KSTAT_ATTR_FS_IOC_FLAGS
[Are any other IOC flags of sufficient general interest to be exposed
through this interface?]
New flags include:
STATX_ATTR_AUTOMOUNT Object is an automount trigger
These are for the use of GUI tools that might want to mark files specially,
depending on what they are.
Fields in struct statx come in a number of classes:
(0) stx_dev_*, stx_blksize.
These are local system information and are always available.
(1) stx_mode, stx_nlinks, stx_uid, stx_gid, stx_[amc]time, stx_ino,
stx_size, stx_blocks.
These will be returned whether the caller asks for them or not. The
corresponding bits in stx_mask will be set to indicate whether they
actually have valid values.
If the caller didn't ask for them, then they may be approximated. For
example, NFS won't waste any time updating them from the server,
unless as a byproduct of updating something requested.
If the values don't actually exist for the underlying object (such as
UID or GID on a DOS file), then the bit won't be set in the stx_mask,
even if the caller asked for the value. In such a case, the returned
value will be a fabrication.
Note that there are instances where the type might not be valid, for
instance Windows reparse points.
(2) stx_rdev_*.
This will be set only if stx_mode indicates we're looking at a
blockdev or a chardev, otherwise will be 0.
(3) stx_btime.
Similar to (1), except this will be set to 0 if it doesn't exist.
=======
TESTING
=======
The following test program can be used to test the statx system call:
samples/statx/test-statx.c
Just compile and run, passing it paths to the files you want to examine.
The file is built automatically if CONFIG_SAMPLES is enabled.
Here's some example output. Firstly, an NFS directory that crosses to
another FSID. Note that the AUTOMOUNT attribute is set because transiting
this directory will cause d_automount to be invoked by the VFS.
[root@andromeda ~]# /tmp/test-statx -A /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:26 Inode: 1703937 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Attributes: 0000000000001000 (-------- -------- -------- -------- -------- -------- ---m---- --------)
Secondly, the result of automounting on that directory.
[root@andromeda ~]# /tmp/test-statx /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:27 Inode: 2 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2017-02-01 00:46:22 +08:00
|
|
|
static int gfs2_getattr(const struct path *path, struct kstat *stat,
|
|
|
|
u32 request_mask, unsigned int flags)
|
2006-01-17 00:50:04 +08:00
|
|
|
{
|
statx: Add a system call to make enhanced file info available
Add a system call to make extended file information available, including
file creation and some attribute flags where available through the
underlying filesystem.
The getattr inode operation is altered to take two additional arguments: a
u32 request_mask and an unsigned int flags that indicate the
synchronisation mode. This change is propagated to the vfs_getattr*()
function.
Functions like vfs_stat() are now inline wrappers around new functions
vfs_statx() and vfs_statx_fd() to reduce stack usage.
========
OVERVIEW
========
The idea was initially proposed as a set of xattrs that could be retrieved
with getxattr(), but the general preference proved to be for a new syscall
with an extended stat structure.
A number of requests were gathered for features to be included. The
following have been included:
(1) Make the fields a consistent size on all arches and make them large.
(2) Spare space, request flags and information flags are provided for
future expansion.
(3) Better support for the y2038 problem [Arnd Bergmann] (tv_sec is an
__s64).
(4) Creation time: The SMB protocol carries the creation time, which could
be exported by Samba, which will in turn help CIFS make use of
FS-Cache as that can be used for coherency data (stx_btime).
This is also specified in NFSv4 as a recommended attribute and could
be exported by NFSD [Steve French].
(5) Lightweight stat: Ask for just those details of interest, and allow a
netfs (such as NFS) to approximate anything not of interest, possibly
without going to the server [Trond Myklebust, Ulrich Drepper, Andreas
Dilger] (AT_STATX_DONT_SYNC).
(6) Heavyweight stat: Force a netfs to go to the server, even if it thinks
its cached attributes are up to date [Trond Myklebust]
(AT_STATX_FORCE_SYNC).
And the following have been left out for future extension:
(7) Data version number: Could be used by userspace NFS servers [Aneesh
Kumar].
Can also be used to modify fill_post_wcc() in NFSD which retrieves
i_version directly, but has just called vfs_getattr(). It could get
it from the kstat struct if it used vfs_xgetattr() instead.
(There's disagreement on the exact semantics of a single field, since
not all filesystems do this the same way).
(8) BSD stat compatibility: Including more fields from the BSD stat such
as creation time (st_btime) and inode generation number (st_gen)
[Jeremy Allison, Bernd Schubert].
(9) Inode generation number: Useful for FUSE and userspace NFS servers
[Bernd Schubert].
(This was asked for but later deemed unnecessary with the
open-by-handle capability available and caused disagreement as to
whether it's a security hole or not).
(10) Extra coherency data may be useful in making backups [Andreas Dilger].
(No particular data were offered, but things like last backup
timestamp, the data version number and the DOS archive bit would come
into this category).
(11) Allow the filesystem to indicate what it can/cannot provide: A
filesystem can now say it doesn't support a standard stat feature if
that isn't available, so if, for instance, inode numbers or UIDs don't
exist or are fabricated locally...
(This requires a separate system call - I have an fsinfo() call idea
for this).
(12) Store a 16-byte volume ID in the superblock that can be returned in
struct xstat [Steve French].
(Deferred to fsinfo).
(13) Include granularity fields in the time data to indicate the
granularity of each of the times (NFSv4 time_delta) [Steve French].
(Deferred to fsinfo).
(14) FS_IOC_GETFLAGS value. These could be translated to BSD's st_flags.
Note that the Linux IOC flags are a mess and filesystems such as Ext4
define flags that aren't in linux/fs.h, so translation in the kernel
may be a necessity (or, possibly, we provide the filesystem type too).
(Some attributes are made available in stx_attributes, but the general
feeling was that the IOC flags were to ext[234]-specific and shouldn't
be exposed through statx this way).
(15) Mask of features available on file (eg: ACLs, seclabel) [Brad Boyer,
Michael Kerrisk].
(Deferred, probably to fsinfo. Finding out if there's an ACL or
seclabal might require extra filesystem operations).
(16) Femtosecond-resolution timestamps [Dave Chinner].
(A __reserved field has been left in the statx_timestamp struct for
this - if there proves to be a need).
(17) A set multiple attributes syscall to go with this.
===============
NEW SYSTEM CALL
===============
The new system call is:
int ret = statx(int dfd,
const char *filename,
unsigned int flags,
unsigned int mask,
struct statx *buffer);
The dfd, filename and flags parameters indicate the file to query, in a
similar way to fstatat(). There is no equivalent of lstat() as that can be
emulated with statx() by passing AT_SYMLINK_NOFOLLOW in flags. There is
also no equivalent of fstat() as that can be emulated by passing a NULL
filename to statx() with the fd of interest in dfd.
Whether or not statx() synchronises the attributes with the backing store
can be controlled by OR'ing a value into the flags argument (this typically
only affects network filesystems):
(1) AT_STATX_SYNC_AS_STAT tells statx() to behave as stat() does in this
respect.
(2) AT_STATX_FORCE_SYNC will require a network filesystem to synchronise
its attributes with the server - which might require data writeback to
occur to get the timestamps correct.
(3) AT_STATX_DONT_SYNC will suppress synchronisation with the server in a
network filesystem. The resulting values should be considered
approximate.
mask is a bitmask indicating the fields in struct statx that are of
interest to the caller. The user should set this to STATX_BASIC_STATS to
get the basic set returned by stat(). It should be noted that asking for
more information may entail extra I/O operations.
buffer points to the destination for the data. This must be 256 bytes in
size.
======================
MAIN ATTRIBUTES RECORD
======================
The following structures are defined in which to return the main attribute
set:
struct statx_timestamp {
__s64 tv_sec;
__s32 tv_nsec;
__s32 __reserved;
};
struct statx {
__u32 stx_mask;
__u32 stx_blksize;
__u64 stx_attributes;
__u32 stx_nlink;
__u32 stx_uid;
__u32 stx_gid;
__u16 stx_mode;
__u16 __spare0[1];
__u64 stx_ino;
__u64 stx_size;
__u64 stx_blocks;
__u64 __spare1[1];
struct statx_timestamp stx_atime;
struct statx_timestamp stx_btime;
struct statx_timestamp stx_ctime;
struct statx_timestamp stx_mtime;
__u32 stx_rdev_major;
__u32 stx_rdev_minor;
__u32 stx_dev_major;
__u32 stx_dev_minor;
__u64 __spare2[14];
};
The defined bits in request_mask and stx_mask are:
STATX_TYPE Want/got stx_mode & S_IFMT
STATX_MODE Want/got stx_mode & ~S_IFMT
STATX_NLINK Want/got stx_nlink
STATX_UID Want/got stx_uid
STATX_GID Want/got stx_gid
STATX_ATIME Want/got stx_atime{,_ns}
STATX_MTIME Want/got stx_mtime{,_ns}
STATX_CTIME Want/got stx_ctime{,_ns}
STATX_INO Want/got stx_ino
STATX_SIZE Want/got stx_size
STATX_BLOCKS Want/got stx_blocks
STATX_BASIC_STATS [The stuff in the normal stat struct]
STATX_BTIME Want/got stx_btime{,_ns}
STATX_ALL [All currently available stuff]
stx_btime is the file creation time, stx_mask is a bitmask indicating the
data provided and __spares*[] are where as-yet undefined fields can be
placed.
Time fields are structures with separate seconds and nanoseconds fields
plus a reserved field in case we want to add even finer resolution. Note
that times will be negative if before 1970; in such a case, the nanosecond
fields will also be negative if not zero.
The bits defined in the stx_attributes field convey information about a
file, how it is accessed, where it is and what it does. The following
attributes map to FS_*_FL flags and are the same numerical value:
STATX_ATTR_COMPRESSED File is compressed by the fs
STATX_ATTR_IMMUTABLE File is marked immutable
STATX_ATTR_APPEND File is append-only
STATX_ATTR_NODUMP File is not to be dumped
STATX_ATTR_ENCRYPTED File requires key to decrypt in fs
Within the kernel, the supported flags are listed by:
KSTAT_ATTR_FS_IOC_FLAGS
[Are any other IOC flags of sufficient general interest to be exposed
through this interface?]
New flags include:
STATX_ATTR_AUTOMOUNT Object is an automount trigger
These are for the use of GUI tools that might want to mark files specially,
depending on what they are.
Fields in struct statx come in a number of classes:
(0) stx_dev_*, stx_blksize.
These are local system information and are always available.
(1) stx_mode, stx_nlinks, stx_uid, stx_gid, stx_[amc]time, stx_ino,
stx_size, stx_blocks.
These will be returned whether the caller asks for them or not. The
corresponding bits in stx_mask will be set to indicate whether they
actually have valid values.
If the caller didn't ask for them, then they may be approximated. For
example, NFS won't waste any time updating them from the server,
unless as a byproduct of updating something requested.
If the values don't actually exist for the underlying object (such as
UID or GID on a DOS file), then the bit won't be set in the stx_mask,
even if the caller asked for the value. In such a case, the returned
value will be a fabrication.
Note that there are instances where the type might not be valid, for
instance Windows reparse points.
(2) stx_rdev_*.
This will be set only if stx_mode indicates we're looking at a
blockdev or a chardev, otherwise will be 0.
(3) stx_btime.
Similar to (1), except this will be set to 0 if it doesn't exist.
=======
TESTING
=======
The following test program can be used to test the statx system call:
samples/statx/test-statx.c
Just compile and run, passing it paths to the files you want to examine.
The file is built automatically if CONFIG_SAMPLES is enabled.
Here's some example output. Firstly, an NFS directory that crosses to
another FSID. Note that the AUTOMOUNT attribute is set because transiting
this directory will cause d_automount to be invoked by the VFS.
[root@andromeda ~]# /tmp/test-statx -A /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:26 Inode: 1703937 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Attributes: 0000000000001000 (-------- -------- -------- -------- -------- -------- ---m---- --------)
Secondly, the result of automounting on that directory.
[root@andromeda ~]# /tmp/test-statx /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:27 Inode: 2 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2017-02-01 00:46:22 +08:00
|
|
|
struct inode *inode = d_inode(path->dentry);
|
2006-06-15 03:32:57 +08:00
|
|
|
struct gfs2_inode *ip = GFS2_I(inode);
|
2006-01-17 00:50:04 +08:00
|
|
|
struct gfs2_holder gh;
|
|
|
|
int error;
|
|
|
|
|
2016-06-17 20:31:27 +08:00
|
|
|
gfs2_holder_mark_uninitialized(&gh);
|
2008-02-23 00:07:18 +08:00
|
|
|
if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
|
2014-11-14 10:42:04 +08:00
|
|
|
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2006-01-17 00:50:04 +08:00
|
|
|
}
|
|
|
|
|
2006-11-27 23:12:05 +08:00
|
|
|
generic_fillattr(inode, stat);
|
2016-06-17 20:31:27 +08:00
|
|
|
if (gfs2_holder_initialized(&gh))
|
2006-11-27 23:12:05 +08:00
|
|
|
gfs2_glock_dq_uninit(&gh);
|
|
|
|
|
|
|
|
return 0;
|
2006-01-17 00:50:04 +08:00
|
|
|
}
|
|
|
|
|
2008-10-14 21:43:29 +08:00
|
|
|
static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
|
|
|
u64 start, u64 len)
|
|
|
|
{
|
|
|
|
struct gfs2_inode *ip = GFS2_I(inode);
|
|
|
|
struct gfs2_holder gh;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_lock(inode);
|
2008-10-14 21:43:29 +08:00
|
|
|
|
|
|
|
ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (gfs2_is_stuffed(ip)) {
|
|
|
|
u64 phys = ip->i_no_addr << inode->i_blkbits;
|
|
|
|
u64 size = i_size_read(inode);
|
|
|
|
u32 flags = FIEMAP_EXTENT_LAST|FIEMAP_EXTENT_NOT_ALIGNED|
|
|
|
|
FIEMAP_EXTENT_DATA_INLINE;
|
|
|
|
phys += sizeof(struct gfs2_dinode);
|
|
|
|
phys += start;
|
|
|
|
if (start + len > size)
|
|
|
|
len = size - start;
|
|
|
|
if (start < size)
|
|
|
|
ret = fiemap_fill_next_extent(fieinfo, start, phys,
|
|
|
|
len, flags);
|
|
|
|
if (ret == 1)
|
|
|
|
ret = 0;
|
|
|
|
} else {
|
|
|
|
ret = __generic_block_fiemap(inode, fieinfo, start, len,
|
|
|
|
gfs2_block_map);
|
|
|
|
}
|
|
|
|
|
|
|
|
gfs2_glock_dq_uninit(&gh);
|
|
|
|
out:
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_unlock(inode);
|
2008-10-14 21:43:29 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-02-12 16:55:39 +08:00
|
|
|
const struct inode_operations gfs2_file_iops = {
|
2008-07-16 09:03:57 +08:00
|
|
|
.permission = gfs2_permission,
|
2006-01-17 00:50:04 +08:00
|
|
|
.setattr = gfs2_setattr,
|
|
|
|
.getattr = gfs2_getattr,
|
|
|
|
.listxattr = gfs2_listxattr,
|
2008-10-14 21:43:29 +08:00
|
|
|
.fiemap = gfs2_fiemap,
|
2011-07-23 23:37:31 +08:00
|
|
|
.get_acl = gfs2_get_acl,
|
2013-12-20 21:16:52 +08:00
|
|
|
.set_acl = gfs2_set_acl,
|
2006-01-17 00:50:04 +08:00
|
|
|
};
|
|
|
|
|
2007-02-12 16:55:39 +08:00
|
|
|
const struct inode_operations gfs2_dir_iops = {
|
2006-01-17 00:50:04 +08:00
|
|
|
.create = gfs2_create,
|
|
|
|
.lookup = gfs2_lookup,
|
|
|
|
.link = gfs2_link,
|
|
|
|
.unlink = gfs2_unlink,
|
|
|
|
.symlink = gfs2_symlink,
|
|
|
|
.mkdir = gfs2_mkdir,
|
2011-05-09 23:42:37 +08:00
|
|
|
.rmdir = gfs2_unlink,
|
2006-01-17 00:50:04 +08:00
|
|
|
.mknod = gfs2_mknod,
|
2016-09-27 17:03:58 +08:00
|
|
|
.rename = gfs2_rename2,
|
2008-07-16 09:03:57 +08:00
|
|
|
.permission = gfs2_permission,
|
2006-01-17 00:50:04 +08:00
|
|
|
.setattr = gfs2_setattr,
|
|
|
|
.getattr = gfs2_getattr,
|
|
|
|
.listxattr = gfs2_listxattr,
|
2008-10-14 21:43:29 +08:00
|
|
|
.fiemap = gfs2_fiemap,
|
2011-07-23 23:37:31 +08:00
|
|
|
.get_acl = gfs2_get_acl,
|
2013-12-20 21:16:52 +08:00
|
|
|
.set_acl = gfs2_set_acl,
|
2013-06-14 18:17:15 +08:00
|
|
|
.atomic_open = gfs2_atomic_open,
|
2006-01-17 00:50:04 +08:00
|
|
|
};
|
|
|
|
|
2007-02-12 16:55:39 +08:00
|
|
|
const struct inode_operations gfs2_symlink_iops = {
|
2015-11-17 23:20:54 +08:00
|
|
|
.get_link = gfs2_get_link,
|
2008-07-16 09:03:57 +08:00
|
|
|
.permission = gfs2_permission,
|
2006-01-17 00:50:04 +08:00
|
|
|
.setattr = gfs2_setattr,
|
|
|
|
.getattr = gfs2_getattr,
|
|
|
|
.listxattr = gfs2_listxattr,
|
2008-10-14 21:43:29 +08:00
|
|
|
.fiemap = gfs2_fiemap,
|
2006-01-17 00:50:04 +08:00
|
|
|
};
|
|
|
|
|