Example #1
0
void jfs_write_inode(struct inode *inode, int wait)
{
	/*
	 * If COMMIT_DIRTY is not set, the inode isn't really dirty.
	 * It has been committed since the last change, but was still
	 * on the dirty inode list
	 */
	if (test_cflag(COMMIT_Nolink, inode) ||
	    !test_cflag(COMMIT_Dirty, inode))
		return;

	if (jfs_commit_inode(inode, wait)) {
		jfs_err("jfs_write_inode: jfs_commit_inode failed!");
	}
}
Example #2
0
void jfs_delete_inode(struct inode *inode)
{
	jfs_info("In jfs_delete_inode, inode = 0x%p", inode);

	if (!is_bad_inode(inode))
		dquot_initialize(inode);

	if (!is_bad_inode(inode) &&
	    (JFS_IP(inode)->fileset == FILESYSTEM_I)) {
		truncate_inode_pages(&inode->i_data, 0);

		if (test_cflag(COMMIT_Freewmap, inode))
			jfs_free_zero_link(inode);

		diFree(inode);

		/*
		 * Free the inode from the quota allocation.
		 */
		dquot_initialize(inode);
		dquot_free_inode(inode);
		dquot_drop(inode);
	}

	clear_inode(inode);
}
Example #3
0
void jfs_evict_inode(struct inode *inode)
{
	jfs_info("In jfs_evict_inode, inode = 0x%p", inode);

	if (!inode->i_nlink && !is_bad_inode(inode)) {
		dquot_initialize(inode);

		if (JFS_IP(inode)->fileset == FILESYSTEM_I) {
			truncate_inode_pages(&inode->i_data, 0);

			if (test_cflag(COMMIT_Freewmap, inode))
				jfs_free_zero_link(inode);

			diFree(inode);

			/*
			 * Free the inode from the quota allocation.
			 */
			dquot_initialize(inode);
			dquot_free_inode(inode);
		}
	} else {
		truncate_inode_pages(&inode->i_data, 0);
	}
	end_writeback(inode);
	dquot_drop(inode);
}
Example #4
0
void jfs_write_inode(struct inode *inode, int wait)
{
	if (test_cflag(COMMIT_Nolink, inode))
		return;
	/*
	 * If COMMIT_DIRTY is not set, the inode isn't really dirty.
	 * It has been committed since the last change, but was still
	 * on the dirty inode list.
	 */
	 if (!test_cflag(COMMIT_Dirty, inode)) {
		/* Make sure committed changes hit the disk */
		jfs_flush_journal(JFS_SBI(inode->i_sb)->log, wait);
		return;
	 }

	if (jfs_commit_inode(inode, wait)) {
		jfs_err("jfs_write_inode: jfs_commit_inode failed!");
	}
}
Example #5
0
void jfs_delete_inode(struct inode *inode)
{
	jfs_info("In jfs_delete_inode, inode = 0x%p", inode);

	if (test_cflag(COMMIT_Freewmap, inode))
		freeZeroLink(inode);

	diFree(inode);

	clear_inode(inode);
}
Example #6
0
/*
 * Workhorse of both fsync & write_inode
 */
int jfs_commit_inode(struct inode *inode, int wait)
{
	int rc = 0;
	tid_t tid;
	static int noisy = 5;

	jfs_info("In jfs_commit_inode, inode = 0x%p", inode);

	/*
	 * Don't commit if inode has been committed since last being
	 * marked dirty, or if it has been deleted.
	 */
	if (inode->i_nlink == 0 || !test_cflag(COMMIT_Dirty, inode))
		return 0;

	if (isReadOnly(inode)) {
		/* kernel allows writes to devices on read-only
		 * partitions and may think inode is dirty
		 */
		if (!special_file(inode->i_mode) && noisy) {
			jfs_err("jfs_commit_inode(0x%p) called on "
				   "read-only volume", inode);
			jfs_err("Is remount racy?");
			noisy--;
		}
		return 0;
	}

	tid = txBegin(inode->i_sb, COMMIT_INODE);
	mutex_lock(&JFS_IP(inode)->commit_mutex);

	/*
	 * Retest inode state after taking commit_mutex
	 */
	if (inode->i_nlink && test_cflag(COMMIT_Dirty, inode))
		rc = txCommit(tid, 1, &inode, wait ? COMMIT_SYNC : 0);

	txEnd(tid);
	mutex_unlock(&JFS_IP(inode)->commit_mutex);
	return rc;
}
Example #7
0
int jfs_write_inode(struct inode *inode, struct writeback_control *wbc)
{
	int wait = 1; /* XXX fix fsync and use wbc->sync_mode == WB_SYNC_ALL; */

	if (test_cflag(COMMIT_Nolink, inode))
		return 0;
	/*
	 * If COMMIT_DIRTY is not set, the inode isn't really dirty.
	 * It has been committed since the last change, but was still
	 * on the dirty inode list.
	 */
	 if (!test_cflag(COMMIT_Dirty, inode)) {
		/* Make sure committed changes hit the disk */
		jfs_flush_journal(JFS_SBI(inode->i_sb)->log, wait);
		return 0;
	 }

	if (jfs_commit_inode(inode, wait)) {
		jfs_err("jfs_write_inode: jfs_commit_inode failed!");
		return -EIO;
	} else
		return 0;
}
Example #8
0
void jfs_delete_inode(struct inode *inode)
{
	jfs_info("In jfs_delete_inode, inode = 0x%p", inode);

	if (test_cflag(COMMIT_Freewmap, inode))
		freeZeroLink(inode);

	diFree(inode);

	/*
	 * Free the inode from the quota allocation.
	 */
	DQUOT_INIT(inode);
	DQUOT_FREE_INODE(inode);
	DQUOT_DROP(inode);

	clear_inode(inode);
}
Example #9
0
/*
 * Guts of jfs_truncate.  Called with locks already held.  Can be called
 * with directory for truncating directory index table.
 */
void jfs_truncate_nolock(struct inode *ip, loff_t length)
{
	loff_t newsize;
	tid_t tid;

	ASSERT(length >= 0);

	if (test_cflag(COMMIT_Nolink, ip)) {
		xtTruncate(0, ip, length, COMMIT_WMAP);
		return;
	}

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

		/*
		 * The commit_mutex cannot be taken before txBegin.
		 * txBegin may block and there is a chance the inode
		 * could be marked dirty and need to be committed
		 * before txBegin unblocks
		 */
		mutex_lock(&JFS_IP(ip)->commit_mutex);

		newsize = xtTruncate(tid, ip, length,
				     COMMIT_TRUNCATE | COMMIT_PWMAP);
		if (newsize < 0) {
			txEnd(tid);
			mutex_unlock(&JFS_IP(ip)->commit_mutex);
			break;
		}

		ip->i_mtime = ip->i_ctime = CURRENT_TIME;
		mark_inode_dirty(ip);

		txCommit(tid, 1, &ip, 0);
		txEnd(tid);
		mutex_unlock(&JFS_IP(ip)->commit_mutex);
	} while (newsize > length);	/* Truncate isn't always atomic */
}
Example #10
0
/*
 * NAME:	jfs_unlink(dip, dentry)
 *
 * FUNCTION:	remove a link to object <vp> named by <name>
 *		from parent directory <dvp>
 *
 * PARAMETER:	dip	- inode of parent directory
 *		dentry	- dentry of object to be removed
 *
 * RETURN:	errors from subroutines
 *
 * note:
 * temporary file: if one or more processes have the file open
 * when the last link is removed, the link will be removed before
 * unlink() returns, but the removal of the file contents will be
 * postponed until all references to the files are closed.
 *
 * JFS does NOT support unlink() on directories.
 *
 */
static int jfs_unlink(struct inode *dip, struct dentry *dentry)
{
    int rc;
    tid_t tid;		/* transaction id */
    struct inode *ip = dentry->d_inode;
    ino_t ino;
    struct component_name dname;	/* object name */
    struct inode *iplist[2];
    struct tblock *tblk;
    s64 new_size = 0;
    int commit_flag;

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

    /* Init inode for quota operations. */
    dquot_initialize(dip);
    dquot_initialize(ip);

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

    IWRITE_LOCK(ip, RDWRLOCK_NORMAL);

    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);

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

    /*
     * delete the entry of target file from parent directory
     */
    ino = ip->i_ino;
    if ((rc = dtDelete(tid, dip, &dname, &ino, JFS_REMOVE))) {
        jfs_err("jfs_unlink: dtDelete returned %d", rc);
        if (rc == -EIO)
            txAbort(tid, 1);	/* Marks FS Dirty */
        txEnd(tid);
        mutex_unlock(&JFS_IP(ip)->commit_mutex);
        mutex_unlock(&JFS_IP(dip)->commit_mutex);
        IWRITE_UNLOCK(ip);
        goto out1;
    }

    ASSERT(ip->i_nlink);

    ip->i_ctime = dip->i_ctime = dip->i_mtime = CURRENT_TIME;
    mark_inode_dirty(dip);

    /* update target's inode */
    inode_dec_link_count(ip);

    /*
     *	commit zero link count object
     */
    if (ip->i_nlink == 0) {
        assert(!test_cflag(COMMIT_Nolink, ip));
        /* free block resources */
        if ((new_size = commitZeroLink(tid, ip)) < 0) {
            txAbort(tid, 1);	/* Marks FS Dirty */
            txEnd(tid);
            mutex_unlock(&JFS_IP(ip)->commit_mutex);
            mutex_unlock(&JFS_IP(dip)->commit_mutex);
            IWRITE_UNLOCK(ip);
            rc = new_size;
            goto out1;
        }
        tblk = tid_to_tblock(tid);
        tblk->xflag |= COMMIT_DELETE;
        tblk->u.ip = ip;
    }

    /*
     * 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;

    /*
     * If xtTruncate was incomplete, commit synchronously to avoid
     * timing complications
     */
    rc = txCommit(tid, 2, &iplist[0], commit_flag);

    txEnd(tid);

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

    while (new_size && (rc == 0)) {
        tid = txBegin(dip->i_sb, 0);
        mutex_lock(&JFS_IP(ip)->commit_mutex);
        new_size = xtTruncate_pmap(tid, ip, new_size);
        if (new_size < 0) {
            txAbort(tid, 1);	/* Marks FS Dirty */
            rc = new_size;
        } else
            rc = txCommit(tid, 2, &iplist[0], COMMIT_SYNC);
        txEnd(tid);
        mutex_unlock(&JFS_IP(ip)->commit_mutex);
    }

    if (ip->i_nlink == 0)
        set_cflag(COMMIT_Nolink, ip);

    IWRITE_UNLOCK(ip);

    /*
     * Truncating the directory index table is not guaranteed.  It
     * may need to be done iteratively
     */
    if (test_cflag(COMMIT_Stale, dip)) {
        if (dip->i_size > 1)
            jfs_truncate_nolock(dip, 0);

        clear_cflag(COMMIT_Stale, dip);
    }

out1:
    free_UCSname(&dname);
out:
    jfs_info("jfs_unlink: rc:%d", rc);
    return rc;
}
Example #11
0
/*
 * NAME:	jfs_rmdir(dip, dentry)
 *
 * FUNCTION:	remove a link to child directory
 *
 * PARAMETER:	dip	- parent inode
 *		dentry	- child directory dentry
 *
 * RETURN:	-EINVAL	- if name is . or ..
 *		-EINVAL - if . or .. exist but are invalid.
 *		errors from subroutines
 *
 * note:
 * if other threads have the directory open when the last link
 * is removed, the "." and ".." entries, if present, are removed before
 * rmdir() returns and no new entries may be created in the directory,
 * but the directory is not removed until the last reference to
 * the directory is released (cf.unlink() of regular file).
 */
static int jfs_rmdir(struct inode *dip, struct dentry *dentry)
{
    int rc;
    tid_t tid;		/* transaction id */
    struct inode *ip = dentry->d_inode;
    ino_t ino;
    struct component_name dname;
    struct inode *iplist[2];
    struct tblock *tblk;

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

    /* Init inode for quota operations. */
    dquot_initialize(dip);
    dquot_initialize(ip);

    /* directory must be empty to be removed */
    if (!dtEmpty(ip)) {
        rc = -ENOTEMPTY;
        goto out;
    }

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

    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);

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

    tblk = tid_to_tblock(tid);
    tblk->xflag |= COMMIT_DELETE;
    tblk->u.ip = ip;

    /*
     * delete the entry of target directory from parent directory
     */
    ino = ip->i_ino;
    if ((rc = dtDelete(tid, dip, &dname, &ino, JFS_REMOVE))) {
        jfs_err("jfs_rmdir: dtDelete returned %d", rc);
        if (rc == -EIO)
            txAbort(tid, 1);
        txEnd(tid);
        mutex_unlock(&JFS_IP(ip)->commit_mutex);
        mutex_unlock(&JFS_IP(dip)->commit_mutex);

        goto out2;
    }

    /* update parent directory's link count corresponding
     * to ".." entry of the target directory deleted
     */
    dip->i_ctime = dip->i_mtime = CURRENT_TIME;
    inode_dec_link_count(dip);

    /*
     * OS/2 could have created EA and/or ACL
     */
    /* free EA from both persistent and working map */
    if (JFS_IP(ip)->ea.flag & DXD_EXTENT) {
        /* free EA pages */
        txEA(tid, ip, &JFS_IP(ip)->ea, NULL);
    }
    JFS_IP(ip)->ea.flag = 0;

    /* free ACL from both persistent and working map */
    if (JFS_IP(ip)->acl.flag & DXD_EXTENT) {
        /* free ACL pages */
        txEA(tid, ip, &JFS_IP(ip)->acl, NULL);
    }
    JFS_IP(ip)->acl.flag = 0;

    /* mark the target directory as deleted */
    clear_nlink(ip);
    mark_inode_dirty(ip);

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

    txEnd(tid);

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

    /*
     * Truncating the directory index table is not guaranteed.  It
     * may need to be done iteratively
     */
    if (test_cflag(COMMIT_Stale, dip)) {
        if (dip->i_size > 1)
            jfs_truncate_nolock(dip, 0);

        clear_cflag(COMMIT_Stale, dip);
    }

out2:
    free_UCSname(&dname);

out:
    jfs_info("jfs_rmdir: rc:%d", rc);
    return rc;
}
Example #12
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;
}
Example #13
0
/*
 * NAME:	jfs_unlink(dip, dentry)
 *
 * FUNCTION:	remove a link to object <vp> named by <name> 
 *		from parent directory <dvp>
 *
 * PARAMETER:	dip 	- inode of parent directory
 *		dentry 	- dentry of object to be removed
 *
 * RETURN:	errors from subroutines
 *
 * note:
 * temporary file: if one or more processes have the file open
 * when the last link is removed, the link will be removed before
 * unlink() returns, but the removal of the file contents will be
 * postponed until all references to the files are closed.
 *
 * JFS does NOT support unlink() on directories.
 *
 */
int jfs_unlink(struct inode *dip, struct dentry *dentry)
{
	int rc;
	tid_t tid;		/* transaction id */
	struct inode *ip = dentry->d_inode;
	ino_t ino;
	struct component_name dname;	/* object name */
	struct inode *iplist[2];
	struct tblock *tblk;
	s64 new_size = 0;
	int commit_flag;

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

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

	IWRITE_LOCK(ip);

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

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

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

	/*
	 * delete the entry of target file from parent directory
	 */
	ino = ip->i_ino;
	if ((rc = dtDelete(tid, dip, &dname, &ino, JFS_REMOVE))) {
		jERROR(1, ("jfs_unlink: dtDelete returned %d\n", rc));
		if (rc == EIO)
			txAbort(tid, 1);	/* Marks FS Dirty */
		txEnd(tid);
		up(&JFS_IP(dip)->commit_sem);
		up(&JFS_IP(ip)->commit_sem);
		IWRITE_UNLOCK(ip);
		goto out1;
	}

	ASSERT(ip->i_nlink);

	ip->i_ctime = dip->i_ctime = dip->i_mtime = CURRENT_TIME;
	mark_inode_dirty(dip);

	/* update target's inode */
	ip->i_nlink--;
	mark_inode_dirty(ip);

	/*
	 *      commit zero link count object
	 */
	if (ip->i_nlink == 0) {
		assert(!test_cflag(COMMIT_Nolink, ip));
		/* free block resources */
		if ((new_size = commitZeroLink(tid, ip)) < 0) {
			txAbort(tid, 1);	/* Marks FS Dirty */
			txEnd(tid);
			up(&JFS_IP(dip)->commit_sem);
			up(&JFS_IP(ip)->commit_sem);
			IWRITE_UNLOCK(ip);
			rc = -new_size;		/* We return -rc */
			goto out1;
		}
		tblk = tid_to_tblock(tid);
		tblk->xflag |= COMMIT_DELETE;
		tblk->ip = ip;
	}

	/*
	 * 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;

	/*
	 * If xtTruncate was incomplete, commit synchronously to avoid
	 * timing complications
	 */
	rc = txCommit(tid, 2, &iplist[0], commit_flag);

	txEnd(tid);

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


	while (new_size && (rc == 0)) {
		tid = txBegin(dip->i_sb, 0);
		down(&JFS_IP(ip)->commit_sem);
		new_size = xtTruncate_pmap(tid, ip, new_size);
		if (new_size < 0) {
			txAbort(tid, 1);	/* Marks FS Dirty */
			rc = -new_size;		/* We return -rc */
		} else
			rc = txCommit(tid, 2, &iplist[0], COMMIT_SYNC);
		txEnd(tid);
		up(&JFS_IP(ip)->commit_sem);
	}

	if (ip->i_nlink == 0)
		set_cflag(COMMIT_Nolink, ip);

	if (!test_cflag(COMMIT_Holdlock, ip))
		IWRITE_UNLOCK(ip);

	/*
	 * Truncating the directory index table is not guaranteed.  It
	 * may need to be done iteratively
	 */
	if (test_cflag(COMMIT_Stale, dip)) {
		if (dip->i_size > 1)
			jfs_truncate_nolock(dip, 0);

		clear_cflag(COMMIT_Stale, dip);
	}

      out1:
	free_UCSname(&dname);
      out:
	jFYI(1, ("jfs_unlink: rc:%d\n", -rc));
	return -rc;
}
Example #14
0
/*
 * NAME:	jfs_rmdir(dip, dentry)
 *
 * FUNCTION:	remove a link to child directory
 *
 * PARAMETER:	dip 	- parent inode
 *		dentry	- child directory dentry
 *
 * RETURN:	EINVAL	- if name is . or ..
 *		EINVAL  - if . or .. exist but are invalid.
 *		errors from subroutines
 *
 * note:
 * if other threads have the directory open when the last link 
 * is removed, the "." and ".." entries, if present, are removed before 
 * rmdir() returns and no new entries may be created in the directory, 
 * but the directory is not removed until the last reference to 
 * the directory is released (cf.unlink() of regular file).
 */
int jfs_rmdir(struct inode *dip, struct dentry *dentry)
{
	int rc;
	tid_t tid;		/* transaction id */
	struct inode *ip = dentry->d_inode;
	ino_t ino;
	struct component_name dname;
	struct inode *iplist[2];
	struct tblock *tblk;

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

	/* directory must be empty to be removed */
	if (!dtEmpty(ip)) {
		rc = ENOTEMPTY;
		goto out;
	}

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

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

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

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

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

	/*
	 * delete the entry of target directory from parent directory
	 */
	ino = ip->i_ino;
	if ((rc = dtDelete(tid, dip, &dname, &ino, JFS_REMOVE))) {
		jERROR(1, ("jfs_rmdir: dtDelete returned %d\n", rc));
		if (rc == EIO)
			txAbort(tid, 1);
		txEnd(tid);
		up(&JFS_IP(dip)->commit_sem);
		up(&JFS_IP(ip)->commit_sem);

		goto out2;
	}

	/* update parent directory's link count corresponding
	 * to ".." entry of the target directory deleted
	 */
	dip->i_nlink--;
	dip->i_ctime = dip->i_mtime = CURRENT_TIME;
	mark_inode_dirty(dip);

	/*
	 * OS/2 could have created EA and/or ACL
	 */
	/* free EA from both persistent and working map */
	if (JFS_IP(ip)->ea.flag & DXD_EXTENT) {
		/* free EA pages */
		txEA(tid, ip, &JFS_IP(ip)->ea, NULL);
	}
	JFS_IP(ip)->ea.flag = 0;

	/* free ACL from both persistent and working map */
	if (JFS_IP(ip)->acl.flag & DXD_EXTENT) {
		/* free ACL pages */
		txEA(tid, ip, &JFS_IP(ip)->acl, NULL);
	}
	JFS_IP(ip)->acl.flag = 0;

	/* mark the target directory as deleted */
	ip->i_nlink = 0;
	mark_inode_dirty(ip);

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

	txEnd(tid);

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

	/*
	 * Truncating the directory index table is not guaranteed.  It
	 * may need to be done iteratively
	 */
	if (test_cflag(COMMIT_Stale, dip)) {
		if (dip->i_size > 1)
			jfs_truncate_nolock(dip, 0);

		clear_cflag(COMMIT_Stale, dip);
	}

      out2:
	free_UCSname(&dname);

      out:
	jFYI(1, ("jfs_rmdir: rc:%d\n", rc));
	return -rc;
}
Example #15
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;
}