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