Пример #1
0
/*
 * NAME:	jfs_mknod(dvp, name, mode, dev, crp)
 *
 * FUNCTION:	make a new object in directory <dvp> with mode = <mode>,
 *		name = <pname>, and rdev = <dev>.
 *
 * PARAMETER:	dvp 	- directory vnode
 *		name	- name of new object
 *		mode	- create mode (rwxrwxrwx).
 *		dev	- new device number if block/character-special file
 *		crp	- credential
 *
 * RETURN:	Errors from subroutines
 *
 * note: JFS allows mknod() to create a special file.
 * XPG4.2: the only portable use of mknod() is to create a FIFO-special file
 * with mode = S_IFIFO and dev = 0.
 */
jfs_mknod(
	struct vnode	*dvp,
	caddr_t		name,
	mode_t		mode,
	dev_t		dev,
	struct ucred	*crp)
{
	int32	rc;
	int32	tid;
	inode_t	*dip = VP2IP(dvp);
	inode_t *ip;
	ino_t	ino;
	component_t	dname;
	struct vfs	*vfsp = dvp->v_vfsp;
	btstack_t	btstack;
	inode_t		*iplist[2];

	/* JFS does NOT support mknod() of directory */
	if ((mode & IFMT) == IFDIR)
		return EISDIR;
		
	if ((mode & IFMT) != IFIFO && (mode & IFMT) != IFSOCK)
		if (rc = privcheck_cr(DEV_CONFIG, crp))
			return rc;

	IWRITE_LOCK(dip);

	txBegin(dip->i_ipmnt, &tid, 0);

	/* validate search+write permission on parent directory */
	if (rc = iAccess(dip, IEXEC|IWRITE, crp))
		goto out1;

	/*
	 * scan parent directory for entry/freespace
	 * (dtSearch() returns parent directory page pinned)
	 */
	dname.name = name;
	dname.namlen = strlen(name);
	if (rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE))
		goto out1;

	/*
	 * allocate in-memory+on-disk inode:
	 * (iAlloc() returns new, locked inode)
	 */
	if (rc = iAlloc(vfsp, dip, mode, &ip, crp))
	{	
		/* release parent directory page */
		BT_PUTSEARCH(&btstack);

		goto out1;
	}

	/*
	 * create entry in parent directory
	 * (dtInsert() releases parent directory page)
	 */
	ino = ip->i_number;
	if (rc = dtInsert(tid, dip, &dname, &ino, &btstack))
	{
		/* discard the new inode */
		ip->i_nlink = 0;

		goto out2;
	}

	/*
	 * fo a block or character special file, the passed-in device number
	 * needs to be set into the inode's device field and the gnode's
	 * "real device" field.
	 */
	if ((ip->i_mode & IFMT) == IFBLK || (ip->i_mode & IFMT) == IFCHR)
	{
		ip->i_rdev = dev;
		IP2GP(ip)->gn_rdev = dev;
	}

	imark(ip, IACC|ICHG|IUPD|IFSYNC);	

	/* update parent directory inode */
	imark(dip, ICHG|IUPD|IFSYNC);

	/*
	 * insert entry for the new file to dnlc
	 */
	ncEnter(dip->i_ipimap, dip->i_number, &dname, ino, NULL);

	iplist[0] = dip;
	iplist[1] = ip;
	rc = txCommit(tid, 2, &iplist[0], 0);

out2:
	IWRITE_UNLOCK(ip);

	ICACHE_LOCK();
	iput(ip, vfsp);
	ICACHE_UNLOCK();

out1:
	IWRITE_UNLOCK(dip);

	txEnd(tid);

	return rc;
}
Пример #2
0
/*
 * NAME:	jfs_link(vp, dvp, name, crp)
 *
 * FUNCTION:	create a link to <vp> by the name = <name>
 *		in the parent directory <dvp>
 *
 * PARAMETER:	vp	- target object
 *		dvp	- parent directory of new link
 *		name	- name of new link to target object
 *		crp	- credential
 *
 * RETURN:	Errors from subroutines
 *
 * note:
 * JFS does NOT support link() on directories (to prevent circular
 * path in the directory hierarchy);
 * EPERM: the target object is a directory, and either the caller
 * does not have appropriate privileges or the implementation prohibits
 * using link() on directories [XPG4.2].
 *
 * JFS does NOT support links between file systems:
 * EXDEV: target object and new link are on different file systems and
 * implementation does not support links between file systems [XPG4.2].
 */
static int jfs_link(struct dentry *old_dentry,
                    struct inode *dir, struct dentry *dentry)
{
    int rc;
    tid_t tid;
    struct inode *ip = old_dentry->d_inode;
    ino_t ino;
    struct component_name dname;
    struct btstack btstack;
    struct inode *iplist[2];

    jfs_info("jfs_link: %s %s", old_dentry->d_name.name,
             dentry->d_name.name);

    if (ip->i_nlink == JFS_LINK_MAX)
        return -EMLINK;

    dquot_initialize(dir);

    tid = txBegin(ip->i_sb, 0);

    mutex_lock_nested(&JFS_IP(dir)->commit_mutex, COMMIT_MUTEX_PARENT);
    mutex_lock_nested(&JFS_IP(ip)->commit_mutex, COMMIT_MUTEX_CHILD);

    /*
     * scan parent directory for entry/freespace
     */
    if ((rc = get_UCSname(&dname, dentry)))
        goto out;

    if ((rc = dtSearch(dir, &dname, &ino, &btstack, JFS_CREATE)))
        goto free_dname;

    /*
     * create entry for new link in parent directory
     */
    ino = ip->i_ino;
    if ((rc = dtInsert(tid, dir, &dname, &ino, &btstack)))
        goto free_dname;

    /* update object inode */
    inc_nlink(ip);		/* for new link */
    ip->i_ctime = CURRENT_TIME;
    dir->i_ctime = dir->i_mtime = CURRENT_TIME;
    mark_inode_dirty(dir);
    ihold(ip);

    iplist[0] = ip;
    iplist[1] = dir;
    rc = txCommit(tid, 2, &iplist[0], 0);

    if (rc) {
        ip->i_nlink--; /* never instantiated */
        iput(ip);
    } else
        d_instantiate(dentry, ip);

free_dname:
    free_UCSname(&dname);

out:
    txEnd(tid);

    mutex_unlock(&JFS_IP(ip)->commit_mutex);
    mutex_unlock(&JFS_IP(dir)->commit_mutex);

    jfs_info("jfs_link: rc:%d", rc);
    return rc;
}
Пример #3
0
static int jfs_symlink(struct inode *dip, struct dentry *dentry,
                       const char *name)
{
    int rc;
    tid_t tid;
    ino_t ino = 0;
    struct component_name dname;
    int ssize;		/* source pathname size */
    struct btstack btstack;
    struct inode *ip = dentry->d_inode;
    unchar *i_fastsymlink;
    s64 xlen = 0;
    int bmask = 0, xsize;
    s64 extent = 0, xaddr;
    struct metapage *mp;
    struct super_block *sb;
    struct tblock *tblk;

    struct inode *iplist[2];

    jfs_info("jfs_symlink: dip:0x%p name:%s", dip, name);

    dquot_initialize(dip);

    ssize = strlen(name) + 1;

    /*
     * search parent directory for entry/freespace
     * (dtSearch() returns parent directory page pinned)
     */

    if ((rc = get_UCSname(&dname, dentry)))
        goto out1;

    /*
     * allocate on-disk/in-memory inode for symbolic link:
     * (iAlloc() returns new, locked inode)
     */
    ip = ialloc(dip, S_IFLNK | 0777);
    if (IS_ERR(ip)) {
        rc = PTR_ERR(ip);
        goto out2;
    }

    tid = txBegin(dip->i_sb, 0);

    mutex_lock_nested(&JFS_IP(dip)->commit_mutex, COMMIT_MUTEX_PARENT);
    mutex_lock_nested(&JFS_IP(ip)->commit_mutex, COMMIT_MUTEX_CHILD);

    rc = jfs_init_security(tid, ip, dip, &dentry->d_name);
    if (rc)
        goto out3;

    tblk = tid_to_tblock(tid);
    tblk->xflag |= COMMIT_CREATE;
    tblk->ino = ip->i_ino;
    tblk->u.ixpxd = JFS_IP(ip)->ixpxd;

    /* fix symlink access permission
     * (dir_create() ANDs in the u.u_cmask,
     * but symlinks really need to be 777 access)
     */
    ip->i_mode |= 0777;

    /*
     * write symbolic link target path name
     */
    xtInitRoot(tid, ip);

    /*
     * write source path name inline in on-disk inode (fast symbolic link)
     */

    if (ssize <= IDATASIZE) {
        ip->i_op = &jfs_fast_symlink_inode_operations;

        i_fastsymlink = JFS_IP(ip)->i_inline;
        memcpy(i_fastsymlink, name, ssize);
        ip->i_size = ssize - 1;

        /*
         * if symlink is > 128 bytes, we don't have the space to
         * store inline extended attributes
         */
        if (ssize > sizeof (JFS_IP(ip)->i_inline))
            JFS_IP(ip)->mode2 &= ~INLINEEA;

        jfs_info("jfs_symlink: fast symlink added  ssize:%d name:%s ",
                 ssize, name);
    }
    /*
     * write source path name in a single extent
     */
    else {
        jfs_info("jfs_symlink: allocate extent ip:0x%p", ip);

        ip->i_op = &jfs_symlink_inode_operations;
        ip->i_mapping->a_ops = &jfs_aops;

        /*
         * even though the data of symlink object (source
         * path name) is treated as non-journaled user data,
         * it is read/written thru buffer cache for performance.
         */
        sb = ip->i_sb;
        bmask = JFS_SBI(sb)->bsize - 1;
        xsize = (ssize + bmask) & ~bmask;
        xaddr = 0;
        xlen = xsize >> JFS_SBI(sb)->l2bsize;
        if ((rc = xtInsert(tid, ip, 0, 0, xlen, &xaddr, 0))) {
            txAbort(tid, 0);
            goto out3;
        }
        extent = xaddr;
        ip->i_size = ssize - 1;
        while (ssize) {
            /* This is kind of silly since PATH_MAX == 4K */
            int copy_size = min(ssize, PSIZE);

            mp = get_metapage(ip, xaddr, PSIZE, 1);

            if (mp == NULL) {
                xtTruncate(tid, ip, 0, COMMIT_PWMAP);
                rc = -EIO;
                txAbort(tid, 0);
                goto out3;
            }
            memcpy(mp->data, name, copy_size);
            flush_metapage(mp);
            ssize -= copy_size;
            name += copy_size;
            xaddr += JFS_SBI(sb)->nbperpage;
        }
    }

    /*
     * create entry for symbolic link in parent directory
     */
    rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE);
    if (rc == 0) {
        ino = ip->i_ino;
        rc = dtInsert(tid, dip, &dname, &ino, &btstack);
    }
    if (rc) {
        if (xlen)
            xtTruncate(tid, ip, 0, COMMIT_PWMAP);
        txAbort(tid, 0);
        /* discard new inode */
        goto out3;
    }

    mark_inode_dirty(ip);

    dip->i_ctime = dip->i_mtime = CURRENT_TIME;
    mark_inode_dirty(dip);
    /*
     * commit update of parent directory and link object
     */

    iplist[0] = dip;
    iplist[1] = ip;
    rc = txCommit(tid, 2, &iplist[0], 0);

out3:
    txEnd(tid);
    mutex_unlock(&JFS_IP(ip)->commit_mutex);
    mutex_unlock(&JFS_IP(dip)->commit_mutex);
    if (rc) {
        free_ea_wmap(ip);
        ip->i_nlink = 0;
        unlock_new_inode(ip);
        iput(ip);
    } else {
        d_instantiate(dentry, ip);
        unlock_new_inode(ip);
    }

out2:
    free_UCSname(&dname);

out1:
    jfs_info("jfs_symlink: rc:%d", rc);
    return rc;
}
Пример #4
0
/*
 * NAME:	jfs_mknod
 *
 * FUNCTION:	Create a special file (device)
 */
static int jfs_mknod(struct inode *dir, struct dentry *dentry,
                     int mode, dev_t rdev)
{
    struct jfs_inode_info *jfs_ip;
    struct btstack btstack;
    struct component_name dname;
    ino_t ino;
    struct inode *ip;
    struct inode *iplist[2];
    int rc;
    tid_t tid;
    struct tblock *tblk;

    if (!new_valid_dev(rdev))
        return -EINVAL;

    jfs_info("jfs_mknod: %s", dentry->d_name.name);

    dquot_initialize(dir);

    if ((rc = get_UCSname(&dname, dentry)))
        goto out;

    ip = ialloc(dir, mode);
    if (IS_ERR(ip)) {
        rc = PTR_ERR(ip);
        goto out1;
    }
    jfs_ip = JFS_IP(ip);

    tid = txBegin(dir->i_sb, 0);

    mutex_lock_nested(&JFS_IP(dir)->commit_mutex, COMMIT_MUTEX_PARENT);
    mutex_lock_nested(&JFS_IP(ip)->commit_mutex, COMMIT_MUTEX_CHILD);

    rc = jfs_init_acl(tid, ip, dir);
    if (rc)
        goto out3;

    rc = jfs_init_security(tid, ip, dir, &dentry->d_name);
    if (rc) {
        txAbort(tid, 0);
        goto out3;
    }

    if ((rc = dtSearch(dir, &dname, &ino, &btstack, JFS_CREATE))) {
        txAbort(tid, 0);
        goto out3;
    }

    tblk = tid_to_tblock(tid);
    tblk->xflag |= COMMIT_CREATE;
    tblk->ino = ip->i_ino;
    tblk->u.ixpxd = JFS_IP(ip)->ixpxd;

    ino = ip->i_ino;
    if ((rc = dtInsert(tid, dir, &dname, &ino, &btstack))) {
        txAbort(tid, 0);
        goto out3;
    }

    ip->i_op = &jfs_file_inode_operations;
    jfs_ip->dev = new_encode_dev(rdev);
    init_special_inode(ip, ip->i_mode, rdev);

    mark_inode_dirty(ip);

    dir->i_ctime = dir->i_mtime = CURRENT_TIME;

    mark_inode_dirty(dir);

    iplist[0] = dir;
    iplist[1] = ip;
    rc = txCommit(tid, 2, iplist, 0);

out3:
    txEnd(tid);
    mutex_unlock(&JFS_IP(ip)->commit_mutex);
    mutex_unlock(&JFS_IP(dir)->commit_mutex);
    if (rc) {
        free_ea_wmap(ip);
        ip->i_nlink = 0;
        unlock_new_inode(ip);
        iput(ip);
    } else {
        d_instantiate(dentry, ip);
        unlock_new_inode(ip);
    }

out1:
    free_UCSname(&dname);

out:
    jfs_info("jfs_mknod: returning %d", rc);
    return rc;
}
Пример #5
0
/*
 * NAME:	jfs_create(dip, dentry, mode)
 *
 * FUNCTION:	create a regular file in the parent directory <dip>
 *		with name = <from dentry> and mode = <mode>
 *
 * PARAMETER:	dip	- parent directory vnode
 *		dentry	- dentry of new file
 *		mode	- create mode (rwxrwxrwx).
 *		nd- nd struct
 *
 * RETURN:	Errors from subroutines
 *
 */
static int jfs_create(struct inode *dip, struct dentry *dentry, int mode,
                      struct nameidata *nd)
{
    int rc = 0;
    tid_t tid;		/* transaction id */
    struct inode *ip = NULL;	/* child directory inode */
    ino_t ino;
    struct component_name dname;	/* child directory name */
    struct btstack btstack;
    struct inode *iplist[2];
    struct tblock *tblk;

    jfs_info("jfs_create: dip:0x%p name:%s", dip, dentry->d_name.name);

    dquot_initialize(dip);

    /*
     * search parent directory for entry/freespace
     * (dtSearch() returns parent directory page pinned)
     */
    if ((rc = get_UCSname(&dname, dentry)))
        goto out1;

    /*
     * Either iAlloc() or txBegin() may block.  Deadlock can occur if we
     * block there while holding dtree page, so we allocate the inode &
     * begin the transaction before we search the directory.
     */
    ip = ialloc(dip, mode);
    if (IS_ERR(ip)) {
        rc = PTR_ERR(ip);
        goto out2;
    }

    tid = txBegin(dip->i_sb, 0);

    mutex_lock_nested(&JFS_IP(dip)->commit_mutex, COMMIT_MUTEX_PARENT);
    mutex_lock_nested(&JFS_IP(ip)->commit_mutex, COMMIT_MUTEX_CHILD);

    rc = jfs_init_acl(tid, ip, dip);
    if (rc)
        goto out3;

    rc = jfs_init_security(tid, ip, dip, &dentry->d_name);
    if (rc) {
        txAbort(tid, 0);
        goto out3;
    }

    if ((rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE))) {
        jfs_err("jfs_create: dtSearch returned %d", rc);
        txAbort(tid, 0);
        goto out3;
    }

    tblk = tid_to_tblock(tid);
    tblk->xflag |= COMMIT_CREATE;
    tblk->ino = ip->i_ino;
    tblk->u.ixpxd = JFS_IP(ip)->ixpxd;

    iplist[0] = dip;
    iplist[1] = ip;

    /*
     * initialize the child XAD tree root in-line in inode
     */
    xtInitRoot(tid, ip);

    /*
     * create entry in parent directory for child directory
     * (dtInsert() releases parent directory page)
     */
    ino = ip->i_ino;
    if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) {
        if (rc == -EIO) {
            jfs_err("jfs_create: dtInsert returned -EIO");
            txAbort(tid, 1);	/* Marks Filesystem dirty */
        } else
            txAbort(tid, 0);	/* Filesystem full */
        goto out3;
    }

    ip->i_op = &jfs_file_inode_operations;
    ip->i_fop = &jfs_file_operations;
    ip->i_mapping->a_ops = &jfs_aops;

    mark_inode_dirty(ip);

    dip->i_ctime = dip->i_mtime = CURRENT_TIME;

    mark_inode_dirty(dip);

    rc = txCommit(tid, 2, &iplist[0], 0);

out3:
    txEnd(tid);
    mutex_unlock(&JFS_IP(ip)->commit_mutex);
    mutex_unlock(&JFS_IP(dip)->commit_mutex);
    if (rc) {
        free_ea_wmap(ip);
        ip->i_nlink = 0;
        unlock_new_inode(ip);
        iput(ip);
    } else {
        d_instantiate(dentry, ip);
        unlock_new_inode(ip);
    }

out2:
    free_UCSname(&dname);

out1:

    jfs_info("jfs_create: rc:%d", rc);
    return rc;
}
Пример #6
0
/*
 * NAME:        jfs_rename
 *
 * FUNCTION:    rename a file or directory
 *
 * PARAMETER:   from_vp         - pointer to source vnode
 *              frompar_vp      - pointer to source parent vnode
 *              from_name       - original name
 *              to_vp           - pointer to target vnode (if it exists)
 *              topar_vp        - pointer to target parent vnode
 *              to_name         - new name
 *              crp             - pointer to caller's credentials
 *
 * RETURN:      errors from subroutines
 *
 * JFS supports sticky bit permission.
 *
 * normally, to rename an object, user needs execute and write
 * permission of the directory containing the object as well as
 * on the target directory.
 * EACCESS: if S_ISVTX (aka sticky) bit is set for a directory,
 * a file in the directory can be renamed only if
 * the user has write permission for the directory, and
 * either owns the file, owns the directory, or
 * is the superuser (or have appropriate privileges).
 * (e.g., /tmp in which any user can create a file but
 * should not be able to delete or rename files owned
 * by others) [XPG4.2]
 *
 * Basic algorithm is:
 *
 * 1) Check validity of parameters and then obtain locks on the source parent
 *    directory, destination parent directory, source, and the destination
 *    inode.  This ensures neither the source or destination will be deleted out
 *    from underneath us.  It also ensures the source or destination won't be
 *    modified before we finish.
 * 2) Link source to destination.  If destination already exists,
 *    delete it first.
 * 3) Unlink source reference to inode if still around. If a
 *    directory was moved and the parent of the destination
 *    is different from the source, patch the ".." entry in the
 *    directory.
 *
 * The transaction log needs to have the changes journalled in such a
 * way that a crash meets the requirement that the destination always
 * exists (if it existed before).  For directory objects it is critical
 * that the changes in i-numbers occur in such a way that loops and
 * orphans aren't created by a crash.
 */
int32
jfs_rename(
        struct vnode    *from_vp,       /* source vnode */
        struct vnode    *frompar_vp,    /* source parent vnode */
	UniChar		*from_name,     /* source name */
        struct vnode    *to_vp,         /* destination vnode */
        struct vnode    *topar_vp,      /* destination parent vnode */
	UniChar		*to_name,       /* destination name */
        struct dasd_usage  *from_dlim,	/* dasd usage structure for source  F226941 */
        struct dasd_usage  *to_dlim,	/* dasd usage structure for dest  F226941 */
	uint32		flags)		/* INEWNAME if new pathname not 8.3 */
{
	inode_t	*ipmnt;
        int32   tid = -1;               /* Transaction ID */
        int32   txCount = 0;            /* Count of inodes in txList */
        inode_t *txList[4];             /* List of modified inodes */
        inode_t *from_ip = VP2IP (from_vp);     /* Source inode */
        inode_t *frompar_ip = VP2IP (frompar_vp); /* Source parent inode */
        inode_t *to_ip = to_vp ? VP2IP (to_vp) : 0;
                                        /* Target inode (if it exists) */
        inode_t *topar_ip = VP2IP (topar_vp);   /* Target parent inode */
        struct vfs      *vfsf = frompar_vp->v_vfsp;
        struct vfs      *vfst = topar_vp->v_vfsp;
        int32   doingdirectory = 0,     /* Source inode is a directory */
                newparent = 0;          /* New i-number of parent */
        int32   got_from = 0, got_to = 0;
        btstack_t btstack;              /* Temporary stack for B-tree struct */
        component_t     dname;          /* Directory name structure */
        ino_t   ino;                    /* I-number for directory searches */
        int32   error = 0, rc;
	tlock_t		*tlck;		/* Transaction lock */
	dtlock_t	*dtlck;		/* dtree line lock */
	lv_t		*lv;		/* line lock vector */

// BEGIN F226941

#ifdef	_JFS_OS2
	int64		blocks_moving;	/* Number of blocks being moved */
	int64		orig_fromblocks; /* original dasd usage of from dir */
	int64		orig_toblocks;	/* original dasd usage of to dir */
	uint32		start_here;	/* index in limits array */
	int32		i;
	inode_t		*lock_arr[64];	/* Array of inodes to lock */
	inode_t		**lock_list;	/* List of inodes to lock */
	int32		num_locks;	/* Number of inodes to lock */
	int32		upper_limit;	/* Upper limit to number of inodes
					 * we need to lock */
#ifdef _JFS_FASTDASD
	int32		first_locked;	/* index in lock_list array of first
					 * inode still locked.		D233382
					 */
#endif /* _JFS_FASTDASD */
#endif	/* _JFS_OS2 */

// BEGIN F226941

        assert( to_name != NULL && from_name != NULL);

	ipmnt = frompar_ip->i_ipmnt;
	RENAME_LOCK(ipmnt);

        /*
         * Make sure that we are not renaming the "." or ".." entries in
         * a directory.
         */

        if (from_name[0] == '.' &&
                (!from_name[1] || (from_name[1] == '.' && !from_name[2])))
        {
                error = EINVAL;
                goto abortit;
        }

        /*
         * Now we want to verify the proper realationship between the inodes.
         * If everything checks out we need to obtain locks on the source parent
         * directory, destination parent directory, source, and destination
         * inodes.  This allows us to proceed knowing that noone can delete
         * either the source or destination from underneath us.  It also allows
         * us to know noone will be modifying the destination so we can delete
         * it safely.  We need the lock on the source since we might need to
         * modify its .. entry.
         */

	/*
	 * In OS/2, the destination cannot exist unless it is the same as
	 * the source.  This is to allow DosMove to change the case of a
	 * filename.
	 */
	if (to_ip && (from_ip != to_ip))
	{
		error = EINVAL;
		goto abortit;
	}

	/*
	 * Check for cross-device rename.
	 */
	if (frompar_ip->i_dev != topar_ip->i_dev)
	{
		error = EXDEV;
		goto abortit;
	}

	/*
	 * Check if source is the destination parent.  Can't do this
	 * since this would orphan everything under the source.
	 */
	if (from_ip == topar_ip)
	{
		error = EINVAL;
		goto abortit;
	}

	/*
	 * Now we know both the parents are okay.  We will lock both the
	 * parent inodes so we can guarantee the children will not be
	 * deleted underneath us.  We will also lock the source.
	 * When locking the inodes we will use the protocol of locking
	 * regular files first, then directories.  When locking more than
	 * one of each type, we do it in descending inode order.  We also need
	 * to make sure we don't attempt to lock one inode twice if the
	 * two parents are the same.
	 */
	if ((from_ip->i_mode & IFMT) == IFDIR)
		doingdirectory = 1;

// BEGIN F226941
	if (from_dlim->num_limits)
	{
		/* We need to lock all directories from the root to each of
		 * the from and to parent directories.  The DASD usage lists
		 * will contain at least one common entry (the root).  We need
		 * to compile a list of all unique inodes to lock.  The upper
		 * limit will be the sum of both num_limits fields.
		 */
		if (frompar_ip == topar_ip)
			upper_limit = from_dlim->num_limits + 1;
		else
			upper_limit = from_dlim->num_limits +
				       to_dlim->num_limits;
		if (upper_limit > 64)
			lock_list = (inode_t **)
					xmalloc(upper_limit* sizeof(inode_t *),
						0, kernel_heap);
		else
			lock_list = lock_arr;

		/* Put from parents dasd usage list into lock_list */
		for (num_locks = 0; num_locks < from_dlim->num_limits;
		     num_locks++)
			lock_list[num_locks] = from_dlim->pLimits[num_locks];

		/*
		 * We need to lock regular files before directories.  If
		 * we are moving a regular file, lock it now, otherwise add
		 * it to the lock list.
		 */
		if (doingdirectory)
			lock_list[num_locks++] = from_ip;
		else
			IWRITE_LOCK(from_ip);

		if (frompar_ip != topar_ip)
		{
			/* Add unique members of to parent's dasd usage list */
			for (i = 0; i < to_dlim->num_limits; i++)
				if (to_dlim->pLimits[i] != lock_list[i])
					break;
			start_here = i;		/* Needed by over_limit() */
			while (i < to_dlim->num_limits)
				lock_list[num_locks++] = to_dlim->pLimits[i++];
		}

		/* Lock them! */
		sort_and_lock(0, num_locks, lock_list);

		/*
		 * If this was a regular file, add the inode to the lock list
		 * now, so we include it in the txCommit, and unlock it
		 */
		if (!doingdirectory)
			lock_list[num_locks++] = from_ip;

		from_dlim->flag |= DLIM_DIRS_LOCKED;

		frompar_ip->i_dasdlim = from_dlim;
		if (frompar_ip != topar_ip)			// D230860
		{
			to_dlim->flag |= DLIM_DIRS_LOCKED;
			topar_ip->i_dasdlim = to_dlim;
		}
	}
	else
// END F226941

	{
		if (doingdirectory)
		{
			if (frompar_ip == topar_ip)
				iwritelocklist(2, frompar_ip, from_ip);
			else
				iwritelocklist(3, frompar_ip, topar_ip,
					       from_ip);
		}
		else
		{
			if (frompar_ip == topar_ip)
			{
				IWRITE_LOCK(from_ip);
				IWRITE_LOCK(frompar_ip);
			}
			else
			{
				IWRITE_LOCK(from_ip);
				iwritelocklist(2, frompar_ip, topar_ip);
			}
		}
	}
	/*
	 * Now that we have the source parent locked we can lookup the
	 * source inode to make sure it hasn't changed since we did our
	 * initial verification.  If it has we will need to release our
	 * locks and repeat our verification with the new version.
	 * Otherwise we know the source is valid and we can continue.
	 */
	dname.name = from_name;
	dname.namlen = UniStrlen (from_name);

	rc = dtSearch(frompar_ip, &dname, &ino, &btstack, JFS_LOOKUP);
	switch (rc)
	{
		case 0:
			/*
			 * An entry was found, need to see if it is the
			 * same inode as we had before.
			 */
			if (ino != from_ip->i_number)
			{
					rc = ENOENT;
					goto cleanup;
			}
			break;
		default:
			/*
			 * Either the source cannot be found or
			 * something went wrong in the search
			 */
			error = rc;
			goto cleanup;
	}

	/*
	 * If changing case, no need to search for destination inode
	 */
	if (!to_ip)
	{
		/*
		 * Now we can lookup the destination inode to make sure it
		 * hasn't been created since we did our verifications.  If it
		 * has we abort.
		 */
		dname.name = to_name;
		dname.namlen = UniStrlen (to_name);
		if (dname.namlen > JFS_NAME_MAX-1)
		{
			error = ERROR_FILENAME_EXCED_RANGE;
			goto cleanup;
		}
		rc = dtSearch(topar_ip, &dname, &ino, &btstack, JFS_LOOKUP);

		switch (rc)
		{
			case 0:
				/*
				 * We found an entry; fail with EEXIST
				 */
				rc = EEXIST;
				goto cleanup;
			case ENOENT:
				break;
			default:
				/*
				 * Some error from dtSearch().  cleanup and
				 * return
				 */
				error = rc;
				goto cleanup;
		}
	}

	/*
	 * Now we need to validate parameters and check for proper inodes
	 */

	/*
	 * Make sure the source inode has a positive link count
	 */
	if (from_ip->i_nlink <= 0)
	{
		error = EINVAL;
		goto cleanup;
	}

	if ((topar_ip->i_mode & IFMT) != IFDIR)
	{
		error = EINVAL;
		goto cleanup;
	}

	/* Check for the appropriate permissions on the source */

	if (doingdirectory && (frompar_ip->i_number != topar_ip->i_number))
	{
		/*
		 * Account for ".." in new directory.  When source and
		 * destination have the same parent we don't fool with the link
		 * count.
		 */
		if ((nlink_t)topar_ip->i_nlink >= LINK_MAX)
		{
			error = EMLINK;
			goto cleanup;
		}

		/*
		 * If ".." must be changed (i.e. the directory gets a new
		 * parent) then the source directory must not be in the
		 * directory hierarchy above the target, as this would orphan
		 * everything below the source directory.
		 */

		newparent = topar_ip->i_number;
		/* RENAME_LOCK(topar_ip->i_ipmnt); */
		if (error = jfs_checkpath(from_ip, frompar_ip, topar_ip, vfst))
			goto cleanup;
	}

	/*
	 * Check for write-protected media
	 */
	if (isReadOnly(topar_ip))
	{
		error = EROFS;
		goto cleanup;
	}

// BEGIN F226941
	/*
	 * Check to see if there is room in the destination directory
	 */
	blocks_moving = doingdirectory ? DASDUSED(&from_ip->i_DASD) :
					 from_ip->i_nblocks;

	if ((topar_ip != frompar_ip) && (topar_ip->i_dasdlim) &&
	    over_limit(topar_ip->i_dasdlim, blocks_moving, start_here))
	{
		error = ERROR_DISK_FULL;
		goto cleanup;
	}

	orig_fromblocks = frompar_ip->i_nblocks;
	if (topar_ip != frompar_ip)
		orig_toblocks = topar_ip->i_nblocks;
// END F226941

	/*
	 * Perform rename
	 */
	txBegin(topar_ip->i_ipmnt, &tid, 0);

	dname.name = to_name;
	dname.namlen = UniStrlen (to_name);

	if (to_ip != NULL)
	{
		/*
		 * We change the case of the name in place.
		 */
		ino = to_ip->i_number;

		if (error = dtChangeCase(tid, frompar_ip, &dname, &ino,
					 JFS_RENAME))
			goto cleanup;

		imark(topar_ip, ICHG|IUPD|IFSYNC);
	}
	else
	{
		/*
		 * We already know the destination does not exist, so link
		 * source inode as destination.
		 */
		if (error = dtSearch(topar_ip, &dname, &ino, &btstack,
				JFS_CREATE))
		{
			/*
			 * Problem, can't find where to put this guy, or it
			 * already exists!!
			 */
			goto cleanup;
		}

		ino = from_ip->i_number;
		if (error = dtInsert(tid, topar_ip, &dname, &ino, &btstack))
		{
			/*
			 * Failed adding source to destination parent
			 */
			goto cleanup;
		}

		imark(topar_ip, ICHG|IUPD|IFSYNC);

		/* Insert entry for the new file to name cache */
		ncEnter(topar_ip->i_ipimap, topar_ip->i_number, &dname,
			from_ip->i_number, NULL);

		/* Remove source name from source parent and name cache */
		dname.name = from_name;
		dname.namlen = UniStrlen (from_name);
		ino = from_ip->i_number;

		ncDelete(frompar_ip->i_ipimap, frompar_ip->i_number, &dname);

		if (error = dtDelete(tid, frompar_ip, &dname, &ino, JFS_REMOVE))
		{
			/*
		 	* Another unexpected error -- the original file does not
		 	* appear to exist ...
		 	*/
			assert(0);
		}
		imark(frompar_ip, ICHG|IUPD|IFSYNC);

		/*
		 * If this is a directory we need to update the ".." i-number
		 * in the inode.  Also, there is now one fewer ".." referencing
		 * the source parent directory.  Decrement the link count to
		 * the source parent directory for this.
		 */
		if (doingdirectory && newparent)
		{
			/* linelock header of dtree root (containing idotdot)
		 	*/
			tlck = txLock(tid, from_ip,
				      (jbuf_t *)&from_ip->i_bxflag,
				      tlckDTREE|tlckBTROOT);
			dtlck = (dtlock_t *)&tlck->lock;
			ASSERT(dtlck->index == 0);
			lv = (lv_t *)&dtlck->lv[0];
			lv->offset = 0;
			lv->length = 1;
			dtlck->index++;

			((dtroot_t *) &from_ip->i_btroot)->header.idotdot =
					newparent;

			frompar_ip->i_nlink--;
			topar_ip->i_nlink++;
			/* RENAME_UNLOCK(topar_ip->i_ipmnt); */
		}

		/* Set or reset INEWNAME flag as appropriate */
		from_ip->i_mode = (from_ip->i_mode & ~INEWNAME) |
				  (flags & INEWNAME);
		imark(from_ip, ICHG|IFSYNC);			// D231252
		txList[txCount++] = from_ip;			// D231252
	}

// BEGIN F226941
	/*
	 * Modify dasd usage for source and destination directories.
	 * Number of blocks for object being moved didn't change.
	 */
	if (frompar_ip == topar_ip)
	{
		DLIM_UPDATE(tid, frompar_ip, frompar_ip->i_nblocks
			    - orig_fromblocks);
	}
	else
	{
		DLIM_UPDATE(tid, topar_ip, topar_ip->i_nblocks +
			    blocks_moving - orig_toblocks);
		DLIM_UPDATE(tid, frompar_ip, frompar_ip->i_nblocks -
			    (orig_fromblocks + blocks_moving));
	}

// BEGIN D233382
#ifdef _JFS_FASTDASD
	if (from_dlim->num_limits)
	{
		/*
		 * Let's remove any ancestor directories at the beginning of
		 * the lock list that aren't directly involved in the rename.
		 * i_dasdlim will be non-zero for frompar_ip & topar_ip
		 */
		for(i = 0; i < num_locks; i++)
		{
			if (lock_list[i]->i_dasdlim)
			{
				first_locked = i;
				break;
			}
			IWRITE_UNLOCK(lock_list[i]);
		}
		ASSERT(i < num_locks);	// We shouldn't have unlocked them all.
	}
#endif /* _JFS_FASTDASD */
// END D233382

#ifndef _JFS_FASTDASD						// D233382
	if ((from_dlim->flag & DLIM_LOGGED) || (to_dlim->flag & DLIM_LOGGED))
	{
		error = txCommit(tid, num_locks, lock_list, 0);
		from_dlim->flag &= ~DLIM_LOGGED;
		to_dlim->flag &= ~DLIM_LOGGED;
	}
	else
#endif /* _JFS_FASTDASD */					// D233382
// END F226941

	{
		if (frompar_ip != topar_ip)
		{
			/*
			 * Different parent, so we need to add the other parent
			 * to our transaction list
			 */
			txList[txCount++] = topar_ip;
		}

		/* Add source parent directory to transaction list */
		txList[txCount++] = frompar_ip;

		assert(txCount <= 4);
		error = txCommit(tid, txCount, txList, 0);
	}

cleanup:
	/*
	 * Come to this label when all locks have been taken and ready to leave.
	 * error should be set to return value.
	 */
	if (tid != -1) txEnd(tid);

// BEGIN F226941
	if (from_dlim->num_limits)
	{
		frompar_ip->i_dasdlim->flag &= ~DLIM_DIRS_LOCKED;
		frompar_ip->i_dasdlim = 0;
		if (frompar_ip != topar_ip)
		{
			topar_ip->i_dasdlim->flag &= ~DLIM_DIRS_LOCKED;
			topar_ip->i_dasdlim = 0;
		}

#ifdef _JFS_FASTDASD
		for(i = first_locked; i < num_locks; i++)	// D233382
#else
		for(i = 0; i < num_locks; i++)
#endif
			IWRITE_UNLOCK(lock_list[i]);

		if (lock_list != lock_arr)
			xmfree((void *)lock_list, kernel_heap);
	}
	else
// END F226941
	{
		IWRITE_UNLOCK(from_ip);

		IWRITE_UNLOCK(frompar_ip);
		if (frompar_ip != topar_ip)
		{
			IWRITE_UNLOCK(topar_ip);
		}
	}


abortit:
	/*
	 * Come to this label when none of the locks have been taken and ready
	 * to leave.  error should be set to return value.
	 */
	if (to_ip && got_to)
	{
		ICACHE_LOCK();
		iput(to_ip, vfst);
		ICACHE_UNLOCK();
	}
	if (got_from)
	{
		ICACHE_LOCK();
		iput(from_ip, vfsf);
		ICACHE_UNLOCK();
	}

	RENAME_UNLOCK(ipmnt);

	return error;
}
Пример #7
0
/*
 * NAME:	jfs_rename
 *
 * FUNCTION:	rename a file or directory
 */
static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                      struct inode *new_dir, struct dentry *new_dentry)
{
    struct btstack btstack;
    ino_t ino;
    struct component_name new_dname;
    struct inode *new_ip;
    struct component_name old_dname;
    struct inode *old_ip;
    int rc;
    tid_t tid;
    struct tlock *tlck;
    struct dt_lock *dtlck;
    struct lv *lv;
    int ipcount;
    struct inode *iplist[4];
    struct tblock *tblk;
    s64 new_size = 0;
    int commit_flag;


    jfs_info("jfs_rename: %s %s", old_dentry->d_name.name,
             new_dentry->d_name.name);

    dquot_initialize(old_dir);
    dquot_initialize(new_dir);

    old_ip = old_dentry->d_inode;
    new_ip = new_dentry->d_inode;

    if ((rc = get_UCSname(&old_dname, old_dentry)))
        goto out1;

    if ((rc = get_UCSname(&new_dname, new_dentry)))
        goto out2;

    /*
     * Make sure source inode number is what we think it is
     */
    rc = dtSearch(old_dir, &old_dname, &ino, &btstack, JFS_LOOKUP);
    if (rc || (ino != old_ip->i_ino)) {
        rc = -ENOENT;
        goto out3;
    }

    /*
     * Make sure dest inode number (if any) is what we think it is
     */
    rc = dtSearch(new_dir, &new_dname, &ino, &btstack, JFS_LOOKUP);
    if (!rc) {
        if ((!new_ip) || (ino != new_ip->i_ino)) {
            rc = -ESTALE;
            goto out3;
        }
    } else if (rc != -ENOENT)
        goto out3;
    else if (new_ip) {
        /* no entry exists, but one was expected */
        rc = -ESTALE;
        goto out3;
    }

    if (S_ISDIR(old_ip->i_mode)) {
        if (new_ip) {
            if (!dtEmpty(new_ip)) {
                rc = -ENOTEMPTY;
                goto out3;
            }
        } else if ((new_dir != old_dir) &&
                   (new_dir->i_nlink == JFS_LINK_MAX)) {
            rc = -EMLINK;
            goto out3;
        }
    } else if (new_ip) {
        IWRITE_LOCK(new_ip, RDWRLOCK_NORMAL);
        /* Init inode for quota operations. */
        dquot_initialize(new_ip);
    }

    /*
     * The real work starts here
     */
    tid = txBegin(new_dir->i_sb, 0);

    /*
     * How do we know the locking is safe from deadlocks?
     * The vfs does the hard part for us.  Any time we are taking nested
     * commit_mutexes, the vfs already has i_mutex held on the parent.
     * Here, the vfs has already taken i_mutex on both old_dir and new_dir.
     */
    mutex_lock_nested(&JFS_IP(new_dir)->commit_mutex, COMMIT_MUTEX_PARENT);
    mutex_lock_nested(&JFS_IP(old_ip)->commit_mutex, COMMIT_MUTEX_CHILD);
    if (old_dir != new_dir)
        mutex_lock_nested(&JFS_IP(old_dir)->commit_mutex,
                          COMMIT_MUTEX_SECOND_PARENT);

    if (new_ip) {
        mutex_lock_nested(&JFS_IP(new_ip)->commit_mutex,
                          COMMIT_MUTEX_VICTIM);
        /*
         * Change existing directory entry to new inode number
         */
        ino = new_ip->i_ino;
        rc = dtModify(tid, new_dir, &new_dname, &ino,
                      old_ip->i_ino, JFS_RENAME);
        if (rc)
            goto out4;
        drop_nlink(new_ip);
        if (S_ISDIR(new_ip->i_mode)) {
            drop_nlink(new_ip);
            if (new_ip->i_nlink) {
                mutex_unlock(&JFS_IP(new_ip)->commit_mutex);
                if (old_dir != new_dir)
                    mutex_unlock(&JFS_IP(old_dir)->commit_mutex);
                mutex_unlock(&JFS_IP(old_ip)->commit_mutex);
                mutex_unlock(&JFS_IP(new_dir)->commit_mutex);
                if (!S_ISDIR(old_ip->i_mode) && new_ip)
                    IWRITE_UNLOCK(new_ip);
                jfs_error(new_ip->i_sb,
                          "jfs_rename: new_ip->i_nlink != 0");
                return -EIO;
            }
            tblk = tid_to_tblock(tid);
            tblk->xflag |= COMMIT_DELETE;
            tblk->u.ip = new_ip;
        } else if (new_ip->i_nlink == 0) {
            assert(!test_cflag(COMMIT_Nolink, new_ip));
            /* free block resources */
            if ((new_size = commitZeroLink(tid, new_ip)) < 0) {
                txAbort(tid, 1);	/* Marks FS Dirty */
                rc = new_size;
                goto out4;
            }
            tblk = tid_to_tblock(tid);
            tblk->xflag |= COMMIT_DELETE;
            tblk->u.ip = new_ip;
        } else {
            new_ip->i_ctime = CURRENT_TIME;
            mark_inode_dirty(new_ip);
        }
    } else {
        /*
         * Add new directory entry
         */
        rc = dtSearch(new_dir, &new_dname, &ino, &btstack,
                      JFS_CREATE);
        if (rc) {
            jfs_err("jfs_rename didn't expect dtSearch to fail "
                    "w/rc = %d", rc);
            goto out4;
        }

        ino = old_ip->i_ino;
        rc = dtInsert(tid, new_dir, &new_dname, &ino, &btstack);
        if (rc) {
            if (rc == -EIO)
                jfs_err("jfs_rename: dtInsert returned -EIO");
            goto out4;
        }
        if (S_ISDIR(old_ip->i_mode))
            inc_nlink(new_dir);
    }
    /*
     * Remove old directory entry
     */

    ino = old_ip->i_ino;
    rc = dtDelete(tid, old_dir, &old_dname, &ino, JFS_REMOVE);
    if (rc) {
        jfs_err("jfs_rename did not expect dtDelete to return rc = %d",
                rc);
        txAbort(tid, 1);	/* Marks Filesystem dirty */
        goto out4;
    }
    if (S_ISDIR(old_ip->i_mode)) {
        drop_nlink(old_dir);
        if (old_dir != new_dir) {
            /*
             * Change inode number of parent for moved directory
             */

            JFS_IP(old_ip)->i_dtroot.header.idotdot =
                cpu_to_le32(new_dir->i_ino);

            /* Linelock header of dtree */
            tlck = txLock(tid, old_ip,
                          (struct metapage *) &JFS_IP(old_ip)->bxflag,
                          tlckDTREE | tlckBTROOT | tlckRELINK);
            dtlck = (struct dt_lock *) & tlck->lock;
            ASSERT(dtlck->index == 0);
            lv = & dtlck->lv[0];
            lv->offset = 0;
            lv->length = 1;
            dtlck->index++;
        }
    }

    /*
     * Update ctime on changed/moved inodes & mark dirty
     */
    old_ip->i_ctime = CURRENT_TIME;
    mark_inode_dirty(old_ip);

    new_dir->i_ctime = new_dir->i_mtime = current_fs_time(new_dir->i_sb);
    mark_inode_dirty(new_dir);

    /* Build list of inodes modified by this transaction */
    ipcount = 0;
    iplist[ipcount++] = old_ip;
    if (new_ip)
        iplist[ipcount++] = new_ip;
    iplist[ipcount++] = old_dir;

    if (old_dir != new_dir) {
        iplist[ipcount++] = new_dir;
        old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
        mark_inode_dirty(old_dir);
    }

    /*
     * Incomplete truncate of file data can
     * result in timing problems unless we synchronously commit the
     * transaction.
     */
    if (new_size)
        commit_flag = COMMIT_SYNC;
    else
        commit_flag = 0;

    rc = txCommit(tid, ipcount, iplist, commit_flag);

out4:
    txEnd(tid);
    if (new_ip)
        mutex_unlock(&JFS_IP(new_ip)->commit_mutex);
    if (old_dir != new_dir)
        mutex_unlock(&JFS_IP(old_dir)->commit_mutex);
    mutex_unlock(&JFS_IP(old_ip)->commit_mutex);
    mutex_unlock(&JFS_IP(new_dir)->commit_mutex);

    while (new_size && (rc == 0)) {
        tid = txBegin(new_ip->i_sb, 0);
        mutex_lock(&JFS_IP(new_ip)->commit_mutex);
        new_size = xtTruncate_pmap(tid, new_ip, new_size);
        if (new_size < 0) {
            txAbort(tid, 1);
            rc = new_size;
        } else
            rc = txCommit(tid, 1, &new_ip, COMMIT_SYNC);
        txEnd(tid);
        mutex_unlock(&JFS_IP(new_ip)->commit_mutex);
    }
    if (new_ip && (new_ip->i_nlink == 0))
        set_cflag(COMMIT_Nolink, new_ip);
out3:
    free_UCSname(&new_dname);
out2:
    free_UCSname(&old_dname);
out1:
    if (new_ip && !S_ISDIR(new_ip->i_mode))
        IWRITE_UNLOCK(new_ip);
    /*
     * Truncating the directory index table is not guaranteed.  It
     * may need to be done iteratively
     */
    if (test_cflag(COMMIT_Stale, old_dir)) {
        if (old_dir->i_size > 1)
            jfs_truncate_nolock(old_dir, 0);

        clear_cflag(COMMIT_Stale, old_dir);
    }

    jfs_info("jfs_rename: returning %d", rc);
    return rc;
}
Пример #8
0
/*
 * NAME:	jfs_mkdir(dip, dentry, mode)
 *
 * FUNCTION:	create a child directory in the parent directory <dip>
 *		with name = <from dentry> and mode = <mode>
 *
 * PARAMETER:	dip 	- parent directory vnode
 *		dentry	- dentry of child directory
 *		mode	- create mode (rwxrwxrwx).
 *
 * RETURN:	Errors from subroutines
 *
 * note:
 * EACCESS: user needs search+write permission on the parent directory
 */
static int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode)
{
	int rc = 0;
	tid_t tid;		/* transaction id */
	struct inode *ip = NULL;	/* child directory inode */
	ino_t ino;
	struct component_name dname;	/* child directory name */
	struct btstack btstack;
	struct inode *iplist[2];
	struct tblock *tblk;

	jfs_info("jfs_mkdir: dip:0x%p name:%s", dip, dentry->d_name.name);

	/* link count overflow on parent directory ? */
	if (dip->i_nlink == JFS_LINK_MAX) {
		rc = -EMLINK;
		goto out1;
	}

	/*
	 * search parent directory for entry/freespace
	 * (dtSearch() returns parent directory page pinned)
	 */
	if ((rc = get_UCSname(&dname, dentry)))
		goto out1;

	/*
	 * Either iAlloc() or txBegin() may block.  Deadlock can occur if we
	 * block there while holding dtree page, so we allocate the inode &
	 * begin the transaction before we search the directory.
	 */
	ip = ialloc(dip, S_IFDIR | mode);
	if (ip == NULL) {
		rc = -ENOSPC;
		goto out2;
	}

	tid = txBegin(dip->i_sb, 0);

	mutex_lock(&JFS_IP(dip)->commit_mutex);
	mutex_lock(&JFS_IP(ip)->commit_mutex);

	rc = jfs_init_acl(tid, ip, dip);
	if (rc)
		goto out3;

	rc = jfs_init_security(tid, ip, dip);
	if (rc) {
		txAbort(tid, 0);
		goto out3;
	}

	if ((rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE))) {
		jfs_err("jfs_mkdir: dtSearch returned %d", rc);
		txAbort(tid, 0);
		goto out3;
	}

	tblk = tid_to_tblock(tid);
	tblk->xflag |= COMMIT_CREATE;
	tblk->ino = ip->i_ino;
	tblk->u.ixpxd = JFS_IP(ip)->ixpxd;

	iplist[0] = dip;
	iplist[1] = ip;

	/*
	 * initialize the child directory in-line in inode
	 */
	dtInitRoot(tid, ip, dip->i_ino);

	/*
	 * create entry in parent directory for child directory
	 * (dtInsert() releases parent directory page)
	 */
	ino = ip->i_ino;
	if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) {
		if (rc == -EIO) {
			jfs_err("jfs_mkdir: dtInsert returned -EIO");
			txAbort(tid, 1);	/* Marks Filesystem dirty */
		} else
			txAbort(tid, 0);	/* Filesystem full */
		goto out3;
	}

	ip->i_nlink = 2;	/* for '.' */
	ip->i_op = &jfs_dir_inode_operations;
	ip->i_fop = &jfs_dir_operations;

	insert_inode_hash(ip);
	mark_inode_dirty(ip);

	/* update parent directory inode */
	dip->i_nlink++;		/* for '..' from child directory */
	dip->i_ctime = dip->i_mtime = CURRENT_TIME;
	mark_inode_dirty(dip);

	rc = txCommit(tid, 2, &iplist[0], 0);

      out3:
	txEnd(tid);
	mutex_unlock(&JFS_IP(ip)->commit_mutex);
	mutex_unlock(&JFS_IP(dip)->commit_mutex);
	if (rc) {
		free_ea_wmap(ip);
		ip->i_nlink = 0;
		iput(ip);
	} else
		d_instantiate(dentry, ip);

      out2:
	free_UCSname(&dname);


      out1:

	jfs_info("jfs_mkdir: rc:%d", rc);
	return rc;
}
Пример #9
0
/*
 * NAME:	jfs_mkdir(dvp, name, mode, crp)
 *
 * FUNCTION:	create a child directory in the parent directory <dvp>
 *		with name = <name> and mode = <mode>
 *
 * PARAMETER:	dvp 	- parent directory vnode
 *		name	- name of child directory
 *		mode	- create mode (rwxrwxrwx).
 *		crp	- credential
 *
 * RETURN:	Errors from subroutines
 *
 * note:
 * EACCESS: user needs search+write permission on the parent directory
 */
jfs_mkdir(
	struct vnode	*dvp,
	UniChar		*name,
	mode_t		mode,
#ifdef _JFS_OS2
        EAOP		*pEABuf,
#endif	/* _JFS_OS2 */
	struct ucred	*crp)
{
	int32 	rc = 0, rc1 = 0;
	int32	tid;		/* transaction id */
	struct vfs	*vfsp = dvp->v_vfsp;
	inode_t	*dip = VP2IP(dvp); /* parent directory inode */
	struct dasd_usage	*du;				// F226941
	inode_t *ip = NULL;	/* child directory inode */
	ino_t	ino;
	component_t dname;	/* child directory name */
	btstack_t	btstack;
	inode_t	*iplist[2];
	int64	orig_nblocks;					// F226941
#ifdef _JFS_LAZYCOMMIT
	tblock_t *tblk;						// D230860
#endif
#ifdef _JFS_OS2
	FEALIST		*pfealist = NULL;
#endif	/* _JFS_OS2 */

NOISE(1,("jfs_mkdir: dip:0x%08x name:%s\n", dip, name));

	/*
	 * the named file exists for "." or ".."
	 */
	if (name[0] == '.')
	{
		if ((name[1] == '.' && name[2] == '\0') ||
		    name[1] == '\0')
			return EEXIST;
	}

#ifdef _JFS_OS2
	/* validate the EAOP buffer, FEALIST size storage location and the
	 * entire FEALIST storage area.  Once all the storage has been
	 * validated, the entire FEALIST is validated for format and the size is
	 * computed and compared against the limit.
	 */
	if (pEABuf)
	{
		if (rc = jfs_ValidateUserFEAList(pEABuf, &pfealist,
				&pEABuf->oError))
		{
			/* something failed -- bail out */
			return rc;
		}
	}
#endif	/* _JFS_OS2 */

	if (dip->i_nlink == 0)
	{
		rc = ENOENT;
		goto out1;
	}

	/* link count overflow on parent directory ? */
	if (dip->i_nlink >= LINK_MAX)
	{
		rc = EMLINK;
		goto out1;
	}

	/*
	 * search parent directory for entry/freespace
	 * (dtSearch() returns parent directory page pinned)
	 */
	dname.name = name;
	dname.namlen = UniStrlen(name);
	if (dname.namlen > JFS_NAME_MAX-1)
	{
		rc = ERROR_FILENAME_EXCED_RANGE;
		goto out1;
	}

// BEGIN D233382
#ifdef _JFS_LAZYCOMMIT
	if (isReadOnly(dip))
	{
		rc = EROFS;
		goto out1;
	}

	/*
	 * Either iAlloc() or txBegin() may block.  Deadlock can occur if we
	 * block there while holding dtree page, so we allocate the inode &
	 * begin the transaction before we search the directory.
	 */
	if (rc = iAlloc(vfsp, dip, IFDIR|mode|IDIRECTORY, &ip, crp))
		goto out1;

	txBegin(dip->i_ipmnt, &tid, 0);

	if (rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE))
	{
		ip->i_nlink = 0;
		ICACHE_LOCK();
		iput(ip, vfsp);
		ICACHE_UNLOCK();
		txEnd(tid);
		goto out1;
	}

	tblk = &TxBlock[tid];					// D230860
	tblk->xflag |= COMMIT_CREATE;				// D230860
	tblk->ip = ip;						// D230860

#else /* ! _JFS_LAZYCOMMIT */
// END D233382

	if (rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE))
		goto out1;

	if (isReadOnly(dip))
	{
		rc = EROFS;

		/* release parent directory page */
		BT_PUTSEARCH(&btstack);
		goto out1;
	}

	/*
	 * allocate on-disk/in-memory inode for child directory:
	 * (iAlloc() returns new, locked inode)
	 */
	if (rc = iAlloc(vfsp, dip, IFDIR|mode|IDIRECTORY, &ip, crp))
	{
		/* release parent directory page */
		BT_PUTSEARCH(&btstack);

jEVENT(0,("jfs_mkdir: iAlloc error(%d)\n", rc));
		goto out1;
	}

	txBegin(dip->i_ipmnt, &tid, 0);
#endif /* _JFS_LAZYCOMMIT */

	du = ip->i_dasdlim = dip->i_dasdlim;			// F226941

	orig_nblocks = dip->i_nblocks;				// F226941

	iplist[0] = dip;
	iplist[1] = ip;
	
	/*
	 * initialize the child directory in-line in inode
	 */
	dtInitRoot(tid, ip, dip->i_number);

	/*
	 * create entry in parent directory for child directory
	 * (dtInsert() releases parent directory page)
	 */
	ino = ip->i_number;
	if (rc = dtInsert(tid, dip, &dname, &ino, &btstack))
	{	
		/* discard new directory inode */
		ip->i_nlink = 0;

		if (rc == EIO)
			txAbort(tid, 1);	/* Marks Filesystem dirty */
		else
			txAbort(tid, 0);	/* Filesystem full */
		txEnd(tid);
		goto out3;
	}

	/* update child directory inode */
// BEGIN F226941
	setDASDLIMIT(&ip->i_DASD, 0);
	setDASDUSED(&ip->i_DASD, 0);
// END F226941

	ip->i_nlink++;	/* for '.' */ 
	imark(ip, IACC|ICHG|IUPD|IFSYNC);	

	/* update parent directory inode */
	dip->i_nlink++; /* for '..' from child directory */
	imark(dip, ICHG|IUPD|IFSYNC);	

	/*
	 * insert entry for the new file to dnlc
	 */
	ncEnter(dip->i_ipimap, dip->i_number, &dname, ino, NULL);

#ifdef _JFS_OS2

	if ((rc1 == 0) && pfealist)				// F226941
		rc1 = jfs_InitializeFEAList(ip, tid, pfealist);

	if ((rc1 == 0) && (dip->i_acl.flag) && (dip->i_acl.size))
		rc1 = jfs_CopyACL(ip, dip, tid);

#endif	/* _JFS_OS2 */

// BEGIN FF226941
	DLIM_UPDATE(tid, dip, dip->i_nblocks + ip->i_nblocks - orig_nblocks);
	setDASDUSED(&ip->i_DASD, ip->i_nblocks);		// D233382
#ifndef _JFS_FASTDASD						// D233382
	/*
	 * If the transaction modified the ancestors of the inode, the
	 * parent will be in the dasd usage list.  Otherwise, transaction
	 * will only change new directory and parent.
	 */
	if (du && (du->flag & DLIM_LOGGED))
		rc = dasd_commit(tid, ip, 0);
	else
#endif /* _JFS_FASTDASD */					// D233382
// END FF226941

		rc = txCommit(tid, 2, &iplist[0], 0);
	txEnd(tid);

#ifdef _JFS_OS2

	/* If we successfully added the directory, but failed adding the EA or
	 * ACL, we must cleanup the created directory entry and return the
	 * error.
	 */
	if (rc1)
	{
// BEGIN D230860
		if ((du) && (du->first_locked))
		{
			/*
			 * txCommit unlocked one or more inodes.
			 * We need to unlock all the directories &
			 * relock them.
			 */
			dip->i_dasdlim = 0;
			DLIM_WRITE_UNLOCK_DETACH(ip, du);
			DLIM_WRITE_LOCK_ATTACH(ip, du);
			dip->i_dasdlim = du;
		}
// END D230860
		jfs_xrmdir(dip, ip, name);
		rc = rc1;
	}

#endif	/* _JFS_OS2 */

out3:
	ip->i_dasdlim = 0;					// F226941
	IWRITE_UNLOCK(ip);

	ICACHE_LOCK();
	iput(ip, vfsp);
	ICACHE_UNLOCK();

out1:

#ifdef _JFS_OS2
		/*
		 * this buffer was allocated by
		 * jfs_ValidateUserFEAList() at twice the
		 * size of the given list to provide buffer
		 * space for eliminating duplicate names
		 */
	if (pfealist)
		jfs_EABufFree((char *)pfealist, (pfealist->cbList << 1));
#endif	/* _JFS_OS2 */

NOISE(1,("jfs_mkdir: rc:%d\n", rc));
	return rc;
}
Пример #10
0
/*
 * NAME:	jfs_link(vp, dvp, name, crp)
 *
 * FUNCTION:	create a link to <vp> by the name = <name>
 *		in the parent directory <dvp>
 *
 * PARAMETER:	vp 	- target object
 *		dvp	- parent directory of new link
 *		name	- name of new link to target object
 *		crp	- credential
 *
 * RETURN:	Errors from subroutines
 *
 * note:
 * JFS does NOT support link() on directories (to prevent circular
 * path in the directory hierarchy);
 * EPERM: the target object is a directory, and either the caller
 * does not have appropriate privileges or the implementation prohibits
 * using link() on directories [XPG4.2].
 *
 * JFS does NOT support links between file systems:
 * EXDEV: target object and new link are on different file systems and
 * implementation does not support links between file systems [XPG4.2].
 */
int jfs_link(struct dentry *old_dentry,
	     struct inode *dir, struct dentry *dentry)
{
	int rc;
	tid_t tid;
	struct inode *ip = old_dentry->d_inode;
	ino_t ino;
	struct component_name dname;
	struct btstack btstack;
	struct inode *iplist[2];

	jFYI(1,
	     ("jfs_link: %s %s\n", old_dentry->d_name.name,
	      dentry->d_name.name));

	/* JFS does NOT support link() on directories */
	if (S_ISDIR(ip->i_mode))
		return -EPERM;

	tid = txBegin(ip->i_sb, 0);

	down(&JFS_IP(dir)->commit_sem);
	down(&JFS_IP(ip)->commit_sem);

	if (ip->i_nlink == JFS_LINK_MAX) {
		rc = EMLINK;
		goto out;
	}

	/*
	 * scan parent directory for entry/freespace
	 */
	if ((rc = get_UCSname(&dname, dentry, JFS_SBI(ip->i_sb)->nls_tab)))
		goto out;

	if ((rc = dtSearch(dir, &dname, &ino, &btstack, JFS_CREATE)))
		goto out;

	/*
	 * create entry for new link in parent directory
	 */
	ino = ip->i_ino;
	if ((rc = dtInsert(tid, dir, &dname, &ino, &btstack)))
		goto out;

	/* update object inode */
	ip->i_nlink++;		/* for new link */
	ip->i_ctime = CURRENT_TIME;
	mark_inode_dirty(dir);
	atomic_inc(&ip->i_count);
	d_instantiate(dentry, ip);

	iplist[0] = ip;
	iplist[1] = dir;
	rc = txCommit(tid, 2, &iplist[0], 0);

      out:
	txEnd(tid);

	up(&JFS_IP(dir)->commit_sem);
	up(&JFS_IP(ip)->commit_sem);

	jFYI(1, ("jfs_link: rc:%d\n", rc));
	return -rc;
}
Пример #11
0
int jfs_symlink(struct inode *dip, struct dentry *dentry, const char *name)
{
	int rc;
	tid_t tid;
	ino_t ino = 0;
	struct component_name dname;
	int ssize;		/* source pathname size */
	struct btstack btstack;
	struct inode *ip = dentry->d_inode;
	unchar *i_fastsymlink;
	s64 xlen = 0;
	int bmask = 0, xsize;
	s64 xaddr;
	struct metapage *mp;
	struct super_block *sb;
	struct tblock *tblk;

	struct inode *iplist[2];

	jFYI(1, ("jfs_symlink: dip:0x%p name:%s\n", dip, name));

	ssize = strlen(name) + 1;

	/*
	 * search parent directory for entry/freespace
	 * (dtSearch() returns parent directory page pinned)
	 */

	if ((rc = get_UCSname(&dname, dentry, JFS_SBI(dip->i_sb)->nls_tab)))
		goto out1;

	/*
	 * allocate on-disk/in-memory inode for symbolic link:
	 * (iAlloc() returns new, locked inode)
	 */
	ip = ialloc(dip, S_IFLNK | 0777);
	if (ip == NULL) {
		rc = ENOSPC;
		goto out2;
	}

	tid = txBegin(dip->i_sb, 0);

	down(&JFS_IP(dip)->commit_sem);
	down(&JFS_IP(ip)->commit_sem);

	if ((rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE)))
		goto out3;

	tblk = tid_to_tblock(tid);
	tblk->xflag |= COMMIT_CREATE;
	tblk->ip = ip;

	/*
	 * create entry for symbolic link in parent directory
	 */

	ino = ip->i_ino;



	if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) {
		jERROR(1, ("jfs_symlink: dtInsert returned %d\n", rc));
		/* discard ne inode */
		goto out3;

	}

	/* fix symlink access permission
	 * (dir_create() ANDs in the u.u_cmask, 
	 * but symlinks really need to be 777 access)
	 */
	ip->i_mode |= 0777;

	/*
	   *       write symbolic link target path name
	 */
	xtInitRoot(tid, ip);

	/*
	 * write source path name inline in on-disk inode (fast symbolic link)
	 */

	if (ssize <= IDATASIZE) {
		ip->i_op = &jfs_symlink_inode_operations;

		i_fastsymlink = JFS_IP(ip)->i_inline;
		memcpy(i_fastsymlink, name, ssize);
		ip->i_size = ssize - 1;

		/*
		 * if symlink is > 128 bytes, we don't have the space to
		 * store inline extended attributes
		 */
		if (ssize > sizeof (JFS_IP(ip)->i_inline))
			JFS_IP(ip)->mode2 &= ~INLINEEA;

		jFYI(1,
		     ("jfs_symlink: fast symlink added  ssize:%d name:%s \n",
		      ssize, name));
	}
	/*
	 * write source path name in a single extent
	 */
	else {
		jFYI(1, ("jfs_symlink: allocate extent ip:0x%p\n", ip));

		ip->i_op = &page_symlink_inode_operations;
		ip->i_mapping->a_ops = &jfs_aops;

		/*
		 * even though the data of symlink object (source 
		 * path name) is treated as non-journaled user data,
		 * it is read/written thru buffer cache for performance.
		 */
		sb = ip->i_sb;
		bmask = JFS_SBI(sb)->bsize - 1;
		xsize = (ssize + bmask) & ~bmask;
		xaddr = 0;
		xlen = xsize >> JFS_SBI(sb)->l2bsize;
		if ((rc = xtInsert(tid, ip, 0, 0, xlen, &xaddr, 0)) == 0) {
			ip->i_size = ssize - 1;
			while (ssize) {
				int copy_size = min(ssize, PSIZE);

				mp = get_metapage(ip, xaddr, PSIZE, 1);

				if (mp == NULL) {
					dtDelete(tid, dip, &dname, &ino,
						 JFS_REMOVE);
					rc = EIO;
					goto out3;
				}
				memcpy(mp->data, name, copy_size);
				flush_metapage(mp);
#if 0
				mark_buffer_uptodate(bp, 1);
				mark_buffer_dirty(bp, 1);
				if (IS_SYNC(dip)) {
					ll_rw_block(WRITE, 1, &bp);
					wait_on_buffer(bp);
				}
				brelse(bp);
#endif				/* 0 */
				ssize -= copy_size;
				xaddr += JFS_SBI(sb)->nbperpage;
			}
			ip->i_blocks = LBLK2PBLK(sb, xlen);
		} else {
			dtDelete(tid, dip, &dname, &ino, JFS_REMOVE);
			rc = ENOSPC;
			goto out3;
		}
	}

	insert_inode_hash(ip);
	mark_inode_dirty(ip);
	d_instantiate(dentry, ip);

	/*
	 * commit update of parent directory and link object
	 *
	 * if extent allocation failed (ENOSPC),
	 * the parent inode is committed regardless to avoid
	 * backing out parent directory update (by dtInsert())
	 * and subsequent dtDelete() which is harmless wrt 
	 * integrity concern.  
	 * the symlink inode will be freed by iput() at exit
	 * as it has a zero link count (by dtDelete()) and 
	 * no permanant resources. 
	 */

	iplist[0] = dip;
	if (rc == 0) {
		iplist[1] = ip;
		rc = txCommit(tid, 2, &iplist[0], 0);
	} else
		rc = txCommit(tid, 1, &iplist[0], 0);

      out3:
	txEnd(tid);
	up(&JFS_IP(dip)->commit_sem);
	up(&JFS_IP(ip)->commit_sem);
	if (rc) {
		ip->i_nlink = 0;
		iput(ip);
	}

      out2:
	free_UCSname(&dname);

      out1:
	jFYI(1, ("jfs_symlink: rc:%d\n", -rc));
	return -rc;
}
Пример #12
0
/*
 * NAME:	jfs_create(dip, dentry, mode)
 *
 * FUNCTION:	create a regular file in the parent directory <dip>
 *		with name = <from dentry> and mode = <mode>
 *
 * PARAMETER:	dip 	- parent directory vnode
 *		dentry	- dentry of new file
 *		mode	- create mode (rwxrwxrwx).
 *
 * RETURN:	Errors from subroutines
 *
 */
int jfs_create(struct inode *dip, struct dentry *dentry, int mode)
{
	int rc = 0;
	tid_t tid;		/* transaction id */
	struct inode *ip = NULL;	/* child directory inode */
	ino_t ino;
	struct component_name dname;	/* child directory name */
	struct btstack btstack;
	struct inode *iplist[2];
	struct tblock *tblk;

	jFYI(1, ("jfs_create: dip:0x%p name:%s\n", dip, dentry->d_name.name));

	/*
	 * search parent directory for entry/freespace
	 * (dtSearch() returns parent directory page pinned)
	 */
	if ((rc = get_UCSname(&dname, dentry, JFS_SBI(dip->i_sb)->nls_tab)))
		goto out1;

	/*
	 * Either iAlloc() or txBegin() may block.  Deadlock can occur if we
	 * block there while holding dtree page, so we allocate the inode &
	 * begin the transaction before we search the directory.
	 */
	ip = ialloc(dip, mode);
	if (ip == NULL) {
		rc = ENOSPC;
		goto out2;
	}

	tid = txBegin(dip->i_sb, 0);

	down(&JFS_IP(dip)->commit_sem);
	down(&JFS_IP(ip)->commit_sem);

	if ((rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE))) {
		jERROR(1, ("jfs_create: dtSearch returned %d\n", rc));
		goto out3;
	}

	tblk = tid_to_tblock(tid);
	tblk->xflag |= COMMIT_CREATE;
	tblk->ip = ip;

	iplist[0] = dip;
	iplist[1] = ip;

	/*
	 * initialize the child XAD tree root in-line in inode
	 */
	xtInitRoot(tid, ip);

	/*
	 * create entry in parent directory for child directory
	 * (dtInsert() releases parent directory page)
	 */
	ino = ip->i_ino;
	if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) {
		jERROR(1, ("jfs_create: dtInsert returned %d\n", rc));
		if (rc == EIO)
			txAbort(tid, 1);	/* Marks Filesystem dirty */
		else
			txAbort(tid, 0);	/* Filesystem full */
		goto out3;
	}

	ip->i_op = &jfs_file_inode_operations;
	ip->i_fop = &jfs_file_operations;
	ip->i_mapping->a_ops = &jfs_aops;

	insert_inode_hash(ip);
	mark_inode_dirty(ip);
	d_instantiate(dentry, ip);

	dip->i_ctime = dip->i_mtime = CURRENT_TIME;

	mark_inode_dirty(dip);

	rc = txCommit(tid, 2, &iplist[0], 0);

      out3:
	txEnd(tid);
	up(&JFS_IP(dip)->commit_sem);
	up(&JFS_IP(ip)->commit_sem);
	if (rc) {
		ip->i_nlink = 0;
		iput(ip);
	}

      out2:
	free_UCSname(&dname);

      out1:

	jFYI(1, ("jfs_create: rc:%d\n", -rc));
	return -rc;
}
Пример #13
0
/*
 * NAME:        jfs_mknod
 *
 * FUNCTION:    Create a special file (device)
 */
int jfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
{
	struct btstack btstack;
	struct component_name dname;
	ino_t ino;
	struct inode *ip;
	struct inode *iplist[2];
	int rc;
	tid_t tid;
	struct tblock *tblk;

	jFYI(1, ("jfs_mknod: %s\n", dentry->d_name.name));

	if ((rc = get_UCSname(&dname, dentry, JFS_SBI(dir->i_sb)->nls_tab)))
		goto out;

	ip = ialloc(dir, mode);
	if (ip == NULL) {
		rc = ENOSPC;
		goto out1;
	}

	tid = txBegin(dir->i_sb, 0);

	down(&JFS_IP(dir)->commit_sem);
	down(&JFS_IP(ip)->commit_sem);

	if ((rc = dtSearch(dir, &dname, &ino, &btstack, JFS_CREATE)))
		goto out3;

	tblk = tid_to_tblock(tid);
	tblk->xflag |= COMMIT_CREATE;
	tblk->ip = ip;

	ino = ip->i_ino;
	if ((rc = dtInsert(tid, dir, &dname, &ino, &btstack)))
		goto out3;

	ip->i_op = &jfs_file_inode_operations;
	init_special_inode(ip, ip->i_mode, rdev);

	insert_inode_hash(ip);
	mark_inode_dirty(ip);
	d_instantiate(dentry, ip);

	dir->i_ctime = dir->i_mtime = CURRENT_TIME;

	mark_inode_dirty(dir);

	iplist[0] = dir;
	iplist[1] = ip;
	rc = txCommit(tid, 2, iplist, 0);

      out3:
	txEnd(tid);
	up(&JFS_IP(ip)->commit_sem);
	up(&JFS_IP(dir)->commit_sem);
	if (rc) {
		ip->i_nlink = 0;
		iput(ip);
	}

      out1:
	free_UCSname(&dname);

      out:
	jFYI(1, ("jfs_mknod: returning %d\n", rc));
	return -rc;
}
Пример #14
0
/*
 * NAME:        jfs_rename
 *
 * FUNCTION:    rename a file or directory
 */
int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
	       struct inode *new_dir, struct dentry *new_dentry)
{
	struct btstack btstack;
	ino_t ino;
	struct component_name new_dname;
	struct inode *new_ip;
	struct component_name old_dname;
	struct inode *old_ip;
	int rc;
	tid_t tid;
	struct tlock *tlck;
	struct dt_lock *dtlck;
	struct lv *lv;
	int ipcount;
	struct inode *iplist[4];
	struct tblock *tblk;
	s64 new_size = 0;
	int commit_flag;


	jFYI(1,
	     ("jfs_rename: %s %s\n", old_dentry->d_name.name,
	      new_dentry->d_name.name));

	old_ip = old_dentry->d_inode;
	new_ip = new_dentry->d_inode;

	if ((rc = get_UCSname(&old_dname, old_dentry,
			      JFS_SBI(old_dir->i_sb)->nls_tab)))
		goto out1;

	if ((rc = get_UCSname(&new_dname, new_dentry,
			      JFS_SBI(old_dir->i_sb)->nls_tab)))
		goto out2;

	/*
	 * Make sure source inode number is what we think it is
	 */
	rc = dtSearch(old_dir, &old_dname, &ino, &btstack, JFS_LOOKUP);
	if (rc || (ino != old_ip->i_ino)) {
		rc = ENOENT;
		goto out3;
	}

	/*
	 * Make sure dest inode number (if any) is what we think it is
	 */
	rc = dtSearch(new_dir, &new_dname, &ino, &btstack, JFS_LOOKUP);
	if (rc == 0) {
		if ((new_ip == 0) || (ino != new_ip->i_ino)) {
			rc = ESTALE;
			goto out3;
		}
	} else if (rc != ENOENT)
		goto out3;
	else if (new_ip) {
		/* no entry exists, but one was expected */
		rc = ESTALE;
		goto out3;
	}

	if (S_ISDIR(old_ip->i_mode)) {
		if (new_ip) {
			if (!dtEmpty(new_ip)) {
				rc = ENOTEMPTY;
				goto out3;
			}
		} else if ((new_dir != old_dir) &&
			   (new_dir->i_nlink == JFS_LINK_MAX)) {
			rc = EMLINK;
			goto out3;
		}
	} else if (new_ip)
		IWRITE_LOCK(new_ip);

	/*
	 * The real work starts here
	 */
	tid = txBegin(new_dir->i_sb, 0);

	down(&JFS_IP(new_dir)->commit_sem);
	down(&JFS_IP(old_ip)->commit_sem);
	if (old_dir != new_dir)
		down(&JFS_IP(old_dir)->commit_sem);

	if (new_ip) {
		down(&JFS_IP(new_ip)->commit_sem);
		/*
		 * Change existing directory entry to new inode number
		 */
		ino = new_ip->i_ino;
		rc = dtModify(tid, new_dir, &new_dname, &ino,
			      old_ip->i_ino, JFS_RENAME);
		if (rc)
			goto out4;
		new_ip->i_nlink--;
		if (S_ISDIR(new_ip->i_mode)) {
			new_ip->i_nlink--;
			assert(new_ip->i_nlink == 0);
			tblk = tid_to_tblock(tid);
			tblk->xflag |= COMMIT_DELETE;
			tblk->ip = new_ip;
		} else if (new_ip->i_nlink == 0) {
			assert(!test_cflag(COMMIT_Nolink, new_ip));
			/* free block resources */
			if ((new_size = commitZeroLink(tid, new_ip)) < 0) {
				txAbort(tid, 1);	/* Marks FS Dirty */
				rc = -new_size;		/* We return -rc */
				goto out4;
			}
			tblk = tid_to_tblock(tid);
			tblk->xflag |= COMMIT_DELETE;
			tblk->ip = new_ip;
		} else {
			new_ip->i_ctime = CURRENT_TIME;
			mark_inode_dirty(new_ip);
		}
	} else {
		/*
		 * Add new directory entry
		 */
		rc = dtSearch(new_dir, &new_dname, &ino, &btstack,
			      JFS_CREATE);
		if (rc) {
			jERROR(1,
			       ("jfs_rename didn't expect dtSearch to fail w/rc = %d\n",
				rc));
			goto out4;
		}

		ino = old_ip->i_ino;
		rc = dtInsert(tid, new_dir, &new_dname, &ino, &btstack);
		if (rc) {
			jERROR(1,
			       ("jfs_rename: dtInsert failed w/rc = %d\n",
				rc));
			goto out4;
		}
		if (S_ISDIR(old_ip->i_mode))
			new_dir->i_nlink++;
	}
	/*
	 * Remove old directory entry
	 */

	ino = old_ip->i_ino;
	rc = dtDelete(tid, old_dir, &old_dname, &ino, JFS_REMOVE);
	if (rc) {
		jERROR(1,
		       ("jfs_rename did not expect dtDelete to return rc = %d\n",
			rc));
		txAbort(tid, 1);	/* Marks Filesystem dirty */
		goto out4;
	}
	if (S_ISDIR(old_ip->i_mode)) {
		old_dir->i_nlink--;
		if (old_dir != new_dir) {
			/*
			 * Change inode number of parent for moved directory
			 */

			JFS_IP(old_ip)->i_dtroot.header.idotdot =
				cpu_to_le32(new_dir->i_ino);

			/* Linelock header of dtree */
			tlck = txLock(tid, old_ip,
				    (struct metapage *) &JFS_IP(old_ip)->bxflag,
				      tlckDTREE | tlckBTROOT);
			dtlck = (struct dt_lock *) & tlck->lock;
			ASSERT(dtlck->index == 0);
			lv = & dtlck->lv[0];
			lv->offset = 0;
			lv->length = 1;
			dtlck->index++;
		}
	}

	/*
	 * Update ctime on changed/moved inodes & mark dirty
	 */
	old_ip->i_ctime = CURRENT_TIME;
	mark_inode_dirty(old_ip);

	new_dir->i_ctime = CURRENT_TIME;
	mark_inode_dirty(new_dir);

	/* Build list of inodes modified by this transaction */
	ipcount = 0;
	iplist[ipcount++] = old_ip;
	if (new_ip)
		iplist[ipcount++] = new_ip;
	iplist[ipcount++] = old_dir;

	if (old_dir != new_dir) {
		iplist[ipcount++] = new_dir;
		old_dir->i_ctime = CURRENT_TIME;
		mark_inode_dirty(old_dir);
	}

	/*
	 * Incomplete truncate of file data can
	 * result in timing problems unless we synchronously commit the
	 * transaction.
	 */
	if (new_size)
		commit_flag = COMMIT_SYNC;
	else
		commit_flag = 0;

	rc = txCommit(tid, ipcount, iplist, commit_flag);

	/*
	 * Don't unlock new_ip if COMMIT_HOLDLOCK is set
	 */
	if (new_ip && test_cflag(COMMIT_Holdlock, new_ip)) {
		up(&JFS_IP(new_ip)->commit_sem);
		new_ip = 0;
	}

      out4:
	txEnd(tid);

	up(&JFS_IP(new_dir)->commit_sem);
	up(&JFS_IP(old_ip)->commit_sem);
	if (old_dir != new_dir)
		up(&JFS_IP(old_dir)->commit_sem);
	if (new_ip)
		up(&JFS_IP(new_ip)->commit_sem);

	while (new_size && (rc == 0)) {
		tid = txBegin(new_ip->i_sb, 0);
		down(&JFS_IP(new_ip)->commit_sem);
		new_size = xtTruncate_pmap(tid, new_ip, new_size);
		if (new_size < 0) {
			txAbort(tid, 1);
			rc = -new_size;		/* We return -rc */
		} else
			rc = txCommit(tid, 1, &new_ip, COMMIT_SYNC);
		txEnd(tid);
		up(&JFS_IP(new_ip)->commit_sem);
	}
	if (new_ip && (new_ip->i_nlink == 0))
		set_cflag(COMMIT_Nolink, new_ip);
      out3:
	free_UCSname(&new_dname);
      out2:
	free_UCSname(&old_dname);
      out1:
	if (new_ip && !S_ISDIR(new_ip->i_mode))
		IWRITE_UNLOCK(new_ip);
	/*
	 * Truncating the directory index table is not guaranteed.  It
	 * may need to be done iteratively
	 */
	if (test_cflag(COMMIT_Stale, old_dir)) {
		if (old_dir->i_size > 1)
			jfs_truncate_nolock(old_dir, 0);

		clear_cflag(COMMIT_Stale, old_dir);
	}

	jFYI(1, ("jfs_rename: returning %d\n", rc));
	return -rc;
}
Пример #15
0
/*
 * NAME:	jfs_link(vp, dvp, name, crp)
 *
 * FUNCTION:	create a link to <vp> by the name = <name>
 *		in the parent directory <dvp>
 *
 * PARAMETER:	vp 	- target object
 *		dvp	- parent directory of new link
 *		name	- name of new link to target object
 *		crp	- credential
 *
 * RETURN:	Errors from subroutines
 *
 * note:
 * JFS does NOT support link() on directories (to prevent circular
 * path in the directory hierarchy);
 * EPERM: the target object is a directory, and either the caller
 * does not have appropriate privileges or the implementation prohibits
 * using link() on directories [XPG4.2].
 *
 * JFS does NOT support links between file systems:
 * EXDEV: target object and new link are on different file systems and
 * implementation does not support links between file systems [XPG4.2].
 */
static int jfs_link(struct dentry *old_dentry,
	     struct inode *dir, struct dentry *dentry)
{
	int rc;
	tid_t tid;
	struct inode *ip = old_dentry->d_inode;
	ino_t ino;
	struct component_name dname;
	struct btstack btstack;
	struct inode *iplist[2];

	jfs_info("jfs_link: %s %s", old_dentry->d_name.name,
		 dentry->d_name.name);

	if (ip->i_nlink == JFS_LINK_MAX)
		return -EMLINK;

	if (ip->i_nlink == 0)
		return -ENOENT;

	tid = txBegin(ip->i_sb, 0);

	down(&JFS_IP(dir)->commit_sem);
	down(&JFS_IP(ip)->commit_sem);

	/*
	 * scan parent directory for entry/freespace
	 */
	if ((rc = get_UCSname(&dname, dentry)))
		goto out;

	if ((rc = dtSearch(dir, &dname, &ino, &btstack, JFS_CREATE)))
		goto free_dname;

	/*
	 * create entry for new link in parent directory
	 */
	ino = ip->i_ino;
	if ((rc = dtInsert(tid, dir, &dname, &ino, &btstack)))
		goto free_dname;

	/* update object inode */
	ip->i_nlink++;		/* for new link */
	ip->i_ctime = CURRENT_TIME;
	mark_inode_dirty(dir);
	atomic_inc(&ip->i_count);

	iplist[0] = ip;
	iplist[1] = dir;
	rc = txCommit(tid, 2, &iplist[0], 0);

	if (rc) {
		ip->i_nlink--;
		iput(ip);
	} else
		d_instantiate(dentry, ip);

      free_dname:
	free_UCSname(&dname);

      out:
	txEnd(tid);

	up(&JFS_IP(dir)->commit_sem);
	up(&JFS_IP(ip)->commit_sem);

	jfs_info("jfs_link: rc:%d", rc);
	return rc;
}
Пример #16
0
/*
 * NAME:        jfs_mknod
 *
 * FUNCTION:    Create a special file (device)
 */
static int jfs_mknod(struct inode *dir, struct dentry *dentry,
		int mode, dev_t rdev)
{
	struct jfs_inode_info *jfs_ip;
	struct btstack btstack;
	struct component_name dname;
	ino_t ino;
	struct inode *ip;
	struct inode *iplist[2];
	int rc;
	tid_t tid;
	struct tblock *tblk;

	if (!new_valid_dev(rdev))
		return -EINVAL;

	jfs_info("jfs_mknod: %s", dentry->d_name.name);

	if ((rc = get_UCSname(&dname, dentry)))
		goto out;

	ip = ialloc(dir, mode);
	if (ip == NULL) {
		rc = -ENOSPC;
		goto out1;
	}
	jfs_ip = JFS_IP(ip);

	tid = txBegin(dir->i_sb, 0);

	down(&JFS_IP(dir)->commit_sem);
	down(&JFS_IP(ip)->commit_sem);

	if ((rc = dtSearch(dir, &dname, &ino, &btstack, JFS_CREATE)))
		goto out3;

	tblk = tid_to_tblock(tid);
	tblk->xflag |= COMMIT_CREATE;
	tblk->ino = ip->i_ino;
	tblk->u.ixpxd = JFS_IP(ip)->ixpxd;

	ino = ip->i_ino;
	if ((rc = dtInsert(tid, dir, &dname, &ino, &btstack)))
		goto out3;

	ip->i_op = &jfs_file_inode_operations;
	jfs_ip->dev = new_encode_dev(rdev);
	init_special_inode(ip, ip->i_mode, rdev);

	insert_inode_hash(ip);
	mark_inode_dirty(ip);

	dir->i_ctime = dir->i_mtime = CURRENT_TIME;

	mark_inode_dirty(dir);

	iplist[0] = dir;
	iplist[1] = ip;
	rc = txCommit(tid, 2, iplist, 0);

      out3:
	txEnd(tid);
	up(&JFS_IP(ip)->commit_sem);
	up(&JFS_IP(dir)->commit_sem);
	if (rc) {
		ip->i_nlink = 0;
		iput(ip);
	} else
		d_instantiate(dentry, ip);

      out1:
	free_UCSname(&dname);

#ifdef CONFIG_JFS_POSIX_ACL
	if (rc == 0)
		jfs_init_acl(ip, dir);
#endif

      out:
	jfs_info("jfs_mknod: returning %d", rc);
	return rc;
}