@@ -26,6 +26,7 @@ static int afs_dir_open(struct inode *inode, struct file *file);
2626static int afs_readdir (struct file * file , struct dir_context * ctx );
2727static int afs_d_revalidate (struct dentry * dentry , unsigned int flags );
2828static int afs_d_delete (const struct dentry * dentry );
29+ static void afs_d_iput (struct dentry * dentry , struct inode * inode );
2930static int afs_lookup_one_filldir (struct dir_context * ctx , const char * name , int nlen ,
3031 loff_t fpos , u64 ino , unsigned dtype );
3132static int afs_lookup_filldir (struct dir_context * ctx , const char * name , int nlen ,
@@ -85,6 +86,7 @@ const struct dentry_operations afs_fs_dentry_operations = {
8586 .d_delete = afs_d_delete ,
8687 .d_release = afs_d_release ,
8788 .d_automount = afs_d_automount ,
89+ .d_iput = afs_d_iput ,
8890};
8991
9092struct afs_lookup_one_cookie {
@@ -1083,6 +1085,16 @@ static int afs_d_delete(const struct dentry *dentry)
10831085 return 1 ;
10841086}
10851087
1088+ /*
1089+ * Clean up sillyrename files on dentry removal.
1090+ */
1091+ static void afs_d_iput (struct dentry * dentry , struct inode * inode )
1092+ {
1093+ if (dentry -> d_flags & DCACHE_NFSFS_RENAMED )
1094+ afs_silly_iput (dentry , inode );
1095+ iput (inode );
1096+ }
1097+
10861098/*
10871099 * handle dentry release
10881100 */
@@ -1225,6 +1237,12 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
12251237 goto error_key ;
12261238 }
12271239
1240+ if (vnode ) {
1241+ ret = down_write_killable (& vnode -> rmdir_lock );
1242+ if (ret < 0 )
1243+ goto error_key ;
1244+ }
1245+
12281246 ret = - ERESTARTSYS ;
12291247 if (afs_begin_vnode_operation (& fc , dvnode , key )) {
12301248 while (afs_select_fileserver (& fc )) {
@@ -1243,6 +1261,8 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
12431261 }
12441262 }
12451263
1264+ if (vnode )
1265+ up_write (& vnode -> rmdir_lock );
12461266error_key :
12471267 key_put (key );
12481268error :
@@ -1259,9 +1279,9 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
12591279 * However, if we didn't have a callback promise outstanding, or it was
12601280 * outstanding on a different server, then it won't break it either...
12611281 */
1262- static int afs_dir_remove_link (struct dentry * dentry , struct key * key ,
1263- unsigned long d_version_before ,
1264- unsigned long d_version_after )
1282+ int afs_dir_remove_link (struct dentry * dentry , struct key * key ,
1283+ unsigned long d_version_before ,
1284+ unsigned long d_version_after )
12651285{
12661286 bool dir_valid ;
12671287 int ret = 0 ;
@@ -1308,6 +1328,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
13081328 struct afs_vnode * dvnode = AFS_FS_I (dir ), * vnode = NULL ;
13091329 struct key * key ;
13101330 unsigned long d_version = (unsigned long )dentry -> d_fsdata ;
1331+ bool need_rehash = false;
13111332 u64 data_version = dvnode -> status .data_version ;
13121333 int ret ;
13131334
@@ -1331,6 +1352,21 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
13311352 goto error_key ;
13321353 }
13331354
1355+ spin_lock (& dentry -> d_lock );
1356+ if (vnode && d_count (dentry ) > 1 ) {
1357+ spin_unlock (& dentry -> d_lock );
1358+ /* Start asynchronous writeout of the inode */
1359+ write_inode_now (d_inode (dentry ), 0 );
1360+ ret = afs_sillyrename (dvnode , vnode , dentry , key );
1361+ goto error_key ;
1362+ }
1363+ if (!d_unhashed (dentry )) {
1364+ /* Prevent a race with RCU lookup. */
1365+ __d_drop (dentry );
1366+ need_rehash = true;
1367+ }
1368+ spin_unlock (& dentry -> d_lock );
1369+
13341370 ret = - ERESTARTSYS ;
13351371 if (afs_begin_vnode_operation (& fc , dvnode , key )) {
13361372 while (afs_select_fileserver (& fc )) {
@@ -1362,6 +1398,9 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
13621398 afs_edit_dir_for_unlink );
13631399 }
13641400
1401+ if (need_rehash && ret < 0 && ret != - ENOENT )
1402+ d_rehash (dentry );
1403+
13651404error_key :
13661405 key_put (key );
13671406error :
@@ -1582,6 +1621,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
15821621{
15831622 struct afs_fs_cursor fc ;
15841623 struct afs_vnode * orig_dvnode , * new_dvnode , * vnode ;
1624+ struct dentry * tmp = NULL , * rehash = NULL ;
1625+ struct inode * new_inode ;
15851626 struct key * key ;
15861627 u64 orig_data_version , new_data_version ;
15871628 bool new_negative = d_is_negative (new_dentry );
@@ -1590,6 +1631,10 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
15901631 if (flags )
15911632 return - EINVAL ;
15921633
1634+ /* Don't allow silly-rename files be moved around. */
1635+ if (old_dentry -> d_flags & DCACHE_NFSFS_RENAMED )
1636+ return - EINVAL ;
1637+
15931638 vnode = AFS_FS_I (d_inode (old_dentry ));
15941639 orig_dvnode = AFS_FS_I (old_dir );
15951640 new_dvnode = AFS_FS_I (new_dir );
@@ -1608,12 +1653,48 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
16081653 goto error ;
16091654 }
16101655
1656+ /* For non-directories, check whether the target is busy and if so,
1657+ * make a copy of the dentry and then do a silly-rename. If the
1658+ * silly-rename succeeds, the copied dentry is hashed and becomes the
1659+ * new target.
1660+ */
1661+ if (d_is_positive (new_dentry ) && !d_is_dir (new_dentry )) {
1662+ /* To prevent any new references to the target during the
1663+ * rename, we unhash the dentry in advance.
1664+ */
1665+ if (!d_unhashed (new_dentry )) {
1666+ d_drop (new_dentry );
1667+ rehash = new_dentry ;
1668+ }
1669+
1670+ if (d_count (new_dentry ) > 2 ) {
1671+ /* copy the target dentry's name */
1672+ ret = - ENOMEM ;
1673+ tmp = d_alloc (new_dentry -> d_parent ,
1674+ & new_dentry -> d_name );
1675+ if (!tmp )
1676+ goto error_rehash ;
1677+
1678+ ret = afs_sillyrename (new_dvnode ,
1679+ AFS_FS_I (d_inode (new_dentry )),
1680+ new_dentry , key );
1681+ if (ret )
1682+ goto error_rehash ;
1683+
1684+ new_dentry = tmp ;
1685+ rehash = NULL ;
1686+ new_negative = true;
1687+ orig_data_version = orig_dvnode -> status .data_version ;
1688+ new_data_version = new_dvnode -> status .data_version ;
1689+ }
1690+ }
1691+
16111692 ret = - ERESTARTSYS ;
16121693 if (afs_begin_vnode_operation (& fc , orig_dvnode , key )) {
16131694 if (orig_dvnode != new_dvnode ) {
16141695 if (mutex_lock_interruptible_nested (& new_dvnode -> io_lock , 1 ) < 0 ) {
16151696 afs_end_vnode_operation (& fc );
1616- goto error_key ;
1697+ goto error_rehash ;
16171698 }
16181699 }
16191700 while (afs_select_fileserver (& fc )) {
@@ -1630,25 +1711,42 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
16301711 mutex_unlock (& new_dvnode -> io_lock );
16311712 ret = afs_end_vnode_operation (& fc );
16321713 if (ret < 0 )
1633- goto error_key ;
1714+ goto error_rehash ;
16341715 }
16351716
16361717 if (ret == 0 ) {
1718+ if (rehash )
1719+ d_rehash (rehash );
16371720 if (test_bit (AFS_VNODE_DIR_VALID , & orig_dvnode -> flags ))
16381721 afs_edit_dir_remove (orig_dvnode , & old_dentry -> d_name ,
1639- afs_edit_dir_for_rename );
1722+ afs_edit_dir_for_rename_0 );
16401723
16411724 if (!new_negative &&
16421725 test_bit (AFS_VNODE_DIR_VALID , & new_dvnode -> flags ))
16431726 afs_edit_dir_remove (new_dvnode , & new_dentry -> d_name ,
1644- afs_edit_dir_for_rename );
1727+ afs_edit_dir_for_rename_1 );
16451728
16461729 if (test_bit (AFS_VNODE_DIR_VALID , & new_dvnode -> flags ))
16471730 afs_edit_dir_add (new_dvnode , & new_dentry -> d_name ,
1648- & vnode -> fid , afs_edit_dir_for_rename );
1731+ & vnode -> fid , afs_edit_dir_for_rename_2 );
1732+
1733+ new_inode = d_inode (new_dentry );
1734+ if (new_inode ) {
1735+ spin_lock (& new_inode -> i_lock );
1736+ if (new_inode -> i_nlink > 0 )
1737+ drop_nlink (new_inode );
1738+ spin_unlock (& new_inode -> i_lock );
1739+ }
1740+ d_move (old_dentry , new_dentry );
1741+ goto error_tmp ;
16491742 }
16501743
1651- error_key :
1744+ error_rehash :
1745+ if (rehash )
1746+ d_rehash (rehash );
1747+ error_tmp :
1748+ if (tmp )
1749+ dput (tmp );
16521750 key_put (key );
16531751error :
16541752 _leave (" = %d" , ret );
0 commit comments