diff options
author | Jeff Layton <jlayton@redhat.com> | 2008-10-20 14:45:22 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2008-10-20 14:44:13 -0400 |
commit | 14121bdccc17b8c0e4368a9c0e4f82c3dd47f240 (patch) | |
tree | 78489a5a1bd99c542bd5f187c999e6be53691198 | |
parent | 413460980ea7796582bce672be85879be0865ada (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>
-rw-r--r-- | fs/cifs/inode.c | 174 |
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 | ||
1288 | int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, | 1288 | int 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)) { | ||
1369 | unlink_target: | 1376 | 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 | |||
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 | |||
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); | ||
1379 | } | 1449 | } |
1380 | 1450 | ||
1381 | cifs_rename_exit: | 1451 | cifs_rename_exit: |