Skip to content

Commit 14121bd

Browse files
jtlaytonSteve French
authored andcommitted
cifs: make cifs_rename handle -EACCES errors
cifs: make cifs_rename handle -EACCES errors Some servers seem to return -EACCES when attempting to rename one open file on top of another. Refactor the cifs_rename logic to attempt to rename the target file out of the way in this situation. This also fixes the "unlink_target" logic to be undoable if the subsequent rename fails. Signed-off-by: Jeff Layton <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 4134609 commit 14121bd

File tree

1 file changed

+122
-52
lines changed

1 file changed

+122
-52
lines changed

fs/cifs/inode.c

Lines changed: 122 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,30 +1285,32 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
12851285
return rc;
12861286
}
12871287

1288-
int cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
1289-
struct inode *target_inode, struct dentry *target_direntry)
1288+
int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
1289+
struct inode *target_dir, struct dentry *target_dentry)
12901290
{
12911291
char *fromName = NULL;
12921292
char *toName = NULL;
12931293
struct cifs_sb_info *cifs_sb_source;
12941294
struct cifs_sb_info *cifs_sb_target;
1295-
struct cifsTconInfo *pTcon;
1295+
struct cifsTconInfo *tcon;
1296+
struct cifsInodeInfo *target_cinode;
12961297
FILE_UNIX_BASIC_INFO *info_buf_source = NULL;
12971298
FILE_UNIX_BASIC_INFO *info_buf_target;
1298-
int xid;
1299-
int rc;
1299+
__u16 dstfid;
1300+
int xid, rc, tmprc, oplock = 0;
1301+
bool delete_already_pending;
13001302

1301-
cifs_sb_target = CIFS_SB(target_inode->i_sb);
1302-
cifs_sb_source = CIFS_SB(source_inode->i_sb);
1303-
pTcon = cifs_sb_source->tcon;
1303+
cifs_sb_target = CIFS_SB(target_dir->i_sb);
1304+
cifs_sb_source = CIFS_SB(source_dir->i_sb);
1305+
tcon = cifs_sb_source->tcon;
13041306

13051307
xid = GetXid();
13061308

13071309
/*
13081310
* BB: this might be allowed if same server, but different share.
13091311
* Consider adding support for this
13101312
*/
1311-
if (pTcon != cifs_sb_target->tcon) {
1313+
if (tcon != cifs_sb_target->tcon) {
13121314
rc = -EXDEV;
13131315
goto cifs_rename_exit;
13141316
}
@@ -1317,65 +1319,133 @@ int cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
13171319
* we already have the rename sem so we do not need to
13181320
* grab it again here to protect the path integrity
13191321
*/
1320-
fromName = build_path_from_dentry(source_direntry);
1322+
fromName = build_path_from_dentry(source_dentry);
13211323
if (fromName == NULL) {
13221324
rc = -ENOMEM;
13231325
goto cifs_rename_exit;
13241326
}
13251327

1326-
toName = build_path_from_dentry(target_direntry);
1328+
toName = build_path_from_dentry(target_dentry);
13271329
if (toName == NULL) {
13281330
rc = -ENOMEM;
13291331
goto cifs_rename_exit;
13301332
}
13311333

1332-
rc = cifs_do_rename(xid, source_direntry, fromName,
1333-
target_direntry, toName);
1334+
rc = cifs_do_rename(xid, source_dentry, fromName,
1335+
target_dentry, toName);
13341336

1335-
if (rc == -EEXIST) {
1336-
if (pTcon->unix_ext) {
1337-
/*
1338-
* Are src and dst hardlinks of same inode? We can
1339-
* only tell with unix extensions enabled
1340-
*/
1341-
info_buf_source =
1342-
kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),
1343-
GFP_KERNEL);
1344-
if (info_buf_source == NULL)
1345-
goto unlink_target;
1346-
1347-
info_buf_target = info_buf_source + 1;
1348-
rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName,
1349-
info_buf_source,
1350-
cifs_sb_source->local_nls,
1351-
cifs_sb_source->mnt_cifs_flags &
1352-
CIFS_MOUNT_MAP_SPECIAL_CHR);
1353-
if (rc != 0)
1354-
goto unlink_target;
1355-
1356-
rc = CIFSSMBUnixQPathInfo(xid, pTcon,
1357-
toName, info_buf_target,
1358-
cifs_sb_target->local_nls,
1359-
/* remap based on source sb */
1360-
cifs_sb_source->mnt_cifs_flags &
1361-
CIFS_MOUNT_MAP_SPECIAL_CHR);
1337+
if (rc == -EEXIST && tcon->unix_ext) {
1338+
/*
1339+
* Are src and dst hardlinks of same inode? We can
1340+
* only tell with unix extensions enabled
1341+
*/
1342+
info_buf_source =
1343+
kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),
1344+
GFP_KERNEL);
1345+
if (info_buf_source == NULL) {
1346+
rc = -ENOMEM;
1347+
goto cifs_rename_exit;
1348+
}
1349+
1350+
info_buf_target = info_buf_source + 1;
1351+
rc = CIFSSMBUnixQPathInfo(xid, tcon, fromName,
1352+
info_buf_source,
1353+
cifs_sb_source->local_nls,
1354+
cifs_sb_source->mnt_cifs_flags &
1355+
CIFS_MOUNT_MAP_SPECIAL_CHR);
1356+
if (rc != 0)
1357+
goto unlink_target;
13621358

1363-
if (rc == 0 && (info_buf_source->UniqueId ==
1364-
info_buf_target->UniqueId))
1365-
/* same file, POSIX says that this is a noop */
1366-
goto cifs_rename_exit;
1367-
} /* else ... BB we could add the same check for Windows by
1359+
rc = CIFSSMBUnixQPathInfo(xid, tcon,
1360+
toName, info_buf_target,
1361+
cifs_sb_target->local_nls,
1362+
/* remap based on source sb */
1363+
cifs_sb_source->mnt_cifs_flags &
1364+
CIFS_MOUNT_MAP_SPECIAL_CHR);
1365+
1366+
if (rc == 0 && (info_buf_source->UniqueId ==
1367+
info_buf_target->UniqueId))
1368+
/* same file, POSIX says that this is a noop */
1369+
goto cifs_rename_exit;
1370+
1371+
rc = -EEXIST;
1372+
} /* else ... BB we could add the same check for Windows by
13681373
checking the UniqueId via FILE_INTERNAL_INFO */
1374+
1375+
if ((rc == -EACCES) || (rc == -EEXIST)) {
13691376
unlink_target:
1377+
/* don't bother if this is a negative dentry */
1378+
if (!target_dentry->d_inode)
1379+
goto cifs_rename_exit;
1380+
1381+
target_cinode = CIFS_I(target_dentry->d_inode);
1382+
1383+
/* try to move the target out of the way */
1384+
tmprc = CIFSSMBOpen(xid, tcon, toName, FILE_OPEN, DELETE,
1385+
CREATE_NOT_DIR, &dstfid, &oplock, NULL,
1386+
cifs_sb_target->local_nls,
1387+
cifs_sb_target->mnt_cifs_flags &
1388+
CIFS_MOUNT_MAP_SPECIAL_CHR);
1389+
if (tmprc)
1390+
goto cifs_rename_exit;
1391+
1392+
/* rename the file to random name */
1393+
tmprc = CIFSSMBRenameOpenFile(xid, tcon, dstfid, NULL,
1394+
cifs_sb_target->local_nls,
1395+
cifs_sb_target->mnt_cifs_flags &
1396+
CIFS_MOUNT_MAP_SPECIAL_CHR);
1397+
1398+
if (tmprc)
1399+
goto close_target;
1400+
1401+
delete_already_pending = target_cinode->delete_pending;
1402+
1403+
if (!delete_already_pending) {
1404+
/* set delete on close */
1405+
tmprc = CIFSSMBSetFileDisposition(xid, tcon,
1406+
true, dstfid,
1407+
current->tgid);
1408+
/*
1409+
* This hack is for broken samba servers, remove this
1410+
* once more fixed ones are in the field.
1411+
*/
1412+
if (tmprc == -ENOENT)
1413+
delete_already_pending = false;
1414+
else if (tmprc)
1415+
goto undo_target_rename;
1416+
1417+
target_cinode->delete_pending = true;
1418+
}
1419+
1420+
1421+
rc = cifs_do_rename(xid, source_dentry, fromName,
1422+
target_dentry, toName);
1423+
1424+
if (rc == 0)
1425+
goto close_target;
1426+
13701427
/*
1371-
* we either can not tell the files are hardlinked (as with
1372-
* Windows servers) or files are not hardlinked. Delete the
1373-
* target manually before renaming to follow POSIX rather than
1374-
* Windows semantics
1428+
* after this point, we can't bother with error handling on
1429+
* the undo's. This is best effort since we can't do anything
1430+
* about failures here.
13751431
*/
1376-
cifs_unlink(target_inode, target_direntry);
1377-
rc = cifs_do_rename(xid, source_direntry, fromName,
1378-
target_direntry, toName);
1432+
if (!delete_already_pending) {
1433+
tmprc = CIFSSMBSetFileDisposition(xid, tcon,
1434+
false, dstfid,
1435+
current->tgid);
1436+
if (tmprc == 0)
1437+
target_cinode->delete_pending = false;
1438+
}
1439+
1440+
undo_target_rename:
1441+
/* rename failed: undo target rename */
1442+
CIFSSMBRenameOpenFile(xid, tcon, dstfid,
1443+
target_dentry->d_name.name,
1444+
cifs_sb_target->local_nls,
1445+
cifs_sb_target->mnt_cifs_flags &
1446+
CIFS_MOUNT_MAP_SPECIAL_CHR);
1447+
close_target:
1448+
CIFSSMBClose(xid, tcon, dstfid);
13791449
}
13801450

13811451
cifs_rename_exit:

0 commit comments

Comments
 (0)