aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2008-10-20 14:45:22 -0400
committerSteve French <sfrench@us.ibm.com>2008-10-20 14:44:13 -0400
commit14121bdccc17b8c0e4368a9c0e4f82c3dd47f240 (patch)
tree78489a5a1bd99c542bd5f187c999e6be53691198 /fs
parent413460980ea7796582bce672be85879be0865ada (diff)
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 <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/inode.c174
1 files changed, 122 insertions, 52 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 23cca214ede7..8ac4eabc1f8b 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -1285,22 +1285,24 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
1285 return rc; 1285 return rc;
1286} 1286}
1287 1287
1288int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, 1288int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
1289 struct inode *target_inode, struct dentry *target_direntry) 1289 struct inode *target_dir, struct dentry *target_dentry)
1290{ 1290{
1291 char *fromName = NULL; 1291 char *fromName = NULL;
1292 char *toName = NULL; 1292 char *toName = NULL;
1293 struct cifs_sb_info *cifs_sb_source; 1293 struct cifs_sb_info *cifs_sb_source;
1294 struct cifs_sb_info *cifs_sb_target; 1294 struct cifs_sb_info *cifs_sb_target;
1295 struct cifsTconInfo *pTcon; 1295 struct cifsTconInfo *tcon;
1296 struct cifsInodeInfo *target_cinode;
1296 FILE_UNIX_BASIC_INFO *info_buf_source = NULL; 1297 FILE_UNIX_BASIC_INFO *info_buf_source = NULL;
1297 FILE_UNIX_BASIC_INFO *info_buf_target; 1298 FILE_UNIX_BASIC_INFO *info_buf_target;
1298 int xid; 1299 __u16 dstfid;
1299 int rc; 1300 int xid, rc, tmprc, oplock = 0;
1301 bool delete_already_pending;
1300 1302
1301 cifs_sb_target = CIFS_SB(target_inode->i_sb); 1303 cifs_sb_target = CIFS_SB(target_dir->i_sb);
1302 cifs_sb_source = CIFS_SB(source_inode->i_sb); 1304 cifs_sb_source = CIFS_SB(source_dir->i_sb);
1303 pTcon = cifs_sb_source->tcon; 1305 tcon = cifs_sb_source->tcon;
1304 1306
1305 xid = GetXid(); 1307 xid = GetXid();
1306 1308
@@ -1308,7 +1310,7 @@ int cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
1308 * BB: this might be allowed if same server, but different share. 1310 * BB: this might be allowed if same server, but different share.
1309 * Consider adding support for this 1311 * Consider adding support for this
1310 */ 1312 */
1311 if (pTcon != cifs_sb_target->tcon) { 1313 if (tcon != cifs_sb_target->tcon) {
1312 rc = -EXDEV; 1314 rc = -EXDEV;
1313 goto cifs_rename_exit; 1315 goto cifs_rename_exit;
1314 } 1316 }
@@ -1317,65 +1319,133 @@ int cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
1317 * we already have the rename sem so we do not need to 1319 * we already have the rename sem so we do not need to
1318 * grab it again here to protect the path integrity 1320 * grab it again here to protect the path integrity
1319 */ 1321 */
1320 fromName = build_path_from_dentry(source_direntry); 1322 fromName = build_path_from_dentry(source_dentry);
1321 if (fromName == NULL) { 1323 if (fromName == NULL) {
1322 rc = -ENOMEM; 1324 rc = -ENOMEM;
1323 goto cifs_rename_exit; 1325 goto cifs_rename_exit;
1324 } 1326 }
1325 1327
1326 toName = build_path_from_dentry(target_direntry); 1328 toName = build_path_from_dentry(target_dentry);
1327 if (toName == NULL) { 1329 if (toName == NULL) {
1328 rc = -ENOMEM; 1330 rc = -ENOMEM;
1329 goto cifs_rename_exit; 1331 goto cifs_rename_exit;
1330 } 1332 }
1331 1333
1332 rc = cifs_do_rename(xid, source_direntry, fromName, 1334 rc = cifs_do_rename(xid, source_dentry, fromName,
1333 target_direntry, toName); 1335 target_dentry, toName);
1334 1336
1335 if (rc == -EEXIST) { 1337 if (rc == -EEXIST && tcon->unix_ext) {
1336 if (pTcon->unix_ext) { 1338 /*
1337 /* 1339 * Are src and dst hardlinks of same inode? We can
1338 * Are src and dst hardlinks of same inode? We can 1340 * only tell with unix extensions enabled
1339 * only tell with unix extensions enabled 1341 */
1340 */ 1342 info_buf_source =
1341 info_buf_source = 1343 kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),
1342 kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO), 1344 GFP_KERNEL);
1343 GFP_KERNEL); 1345 if (info_buf_source == NULL) {
1344 if (info_buf_source == NULL) 1346 rc = -ENOMEM;
1345 goto unlink_target; 1347 goto cifs_rename_exit;
1346 1348 }
1347 info_buf_target = info_buf_source + 1; 1349
1348 rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName, 1350 info_buf_target = info_buf_source + 1;
1349 info_buf_source, 1351 rc = CIFSSMBUnixQPathInfo(xid, tcon, fromName,
1350 cifs_sb_source->local_nls, 1352 info_buf_source,
1351 cifs_sb_source->mnt_cifs_flags & 1353 cifs_sb_source->local_nls,
1352 CIFS_MOUNT_MAP_SPECIAL_CHR); 1354 cifs_sb_source->mnt_cifs_flags &
1353 if (rc != 0) 1355 CIFS_MOUNT_MAP_SPECIAL_CHR);
1354 goto unlink_target; 1356 if (rc != 0)
1355 1357 goto unlink_target;
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);
1362 1358
1363 if (rc == 0 && (info_buf_source->UniqueId == 1359 rc = CIFSSMBUnixQPathInfo(xid, tcon,
1364 info_buf_target->UniqueId)) 1360 toName, info_buf_target,
1365 /* same file, POSIX says that this is a noop */ 1361 cifs_sb_target->local_nls,
1366 goto cifs_rename_exit; 1362 /* remap based on source sb */
1367 } /* else ... BB we could add the same check for Windows by 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
1368 checking the UniqueId via FILE_INTERNAL_INFO */ 1373 checking the UniqueId via FILE_INTERNAL_INFO */
1374
1375 if ((rc == -EACCES) || (rc == -EEXIST)) {
1369unlink_target: 1376unlink_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
1370 /* 1427 /*
1371 * we either can not tell the files are hardlinked (as with 1428 * after this point, we can't bother with error handling on
1372 * Windows servers) or files are not hardlinked. Delete the 1429 * the undo's. This is best effort since we can't do anything
1373 * target manually before renaming to follow POSIX rather than 1430 * about failures here.
1374 * Windows semantics
1375 */ 1431 */
1376 cifs_unlink(target_inode, target_direntry); 1432 if (!delete_already_pending) {
1377 rc = cifs_do_rename(xid, source_direntry, fromName, 1433 tmprc = CIFSSMBSetFileDisposition(xid, tcon,
1378 target_direntry, toName); 1434 false, dstfid,
1435 current->tgid);
1436 if (tmprc == 0)
1437 target_cinode->delete_pending = false;
1438 }
1439
1440undo_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);
1447close_target:
1448 CIFSSMBClose(xid, tcon, dstfid);
1379 } 1449 }
1380 1450
1381cifs_rename_exit: 1451cifs_rename_exit: