diff options
Diffstat (limited to 'fs/cifs/inode.c')
-rw-r--r-- | fs/cifs/inode.c | 211 |
1 files changed, 127 insertions, 84 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index a8c833345fc9..d54fa8aeaea9 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
@@ -506,6 +506,7 @@ int cifs_get_inode_info(struct inode **pinode, | |||
506 | inode = *pinode; | 506 | inode = *pinode; |
507 | cifsInfo = CIFS_I(inode); | 507 | cifsInfo = CIFS_I(inode); |
508 | cifsInfo->cifsAttrs = attr; | 508 | cifsInfo->cifsAttrs = attr; |
509 | cifsInfo->delete_pending = pfindData->DeletePending ? true : false; | ||
509 | cFYI(1, ("Old time %ld", cifsInfo->time)); | 510 | cFYI(1, ("Old time %ld", cifsInfo->time)); |
510 | cifsInfo->time = jiffies; | 511 | cifsInfo->time = jiffies; |
511 | cFYI(1, ("New time %ld", cifsInfo->time)); | 512 | cFYI(1, ("New time %ld", cifsInfo->time)); |
@@ -772,63 +773,106 @@ out: | |||
772 | * anything else. | 773 | * anything else. |
773 | */ | 774 | */ |
774 | static int | 775 | static int |
775 | cifs_rename_pending_delete(char *full_path, struct inode *inode, int xid) | 776 | cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid) |
776 | { | 777 | { |
777 | int oplock = 0; | 778 | int oplock = 0; |
778 | int rc; | 779 | int rc; |
779 | __u16 netfid; | 780 | __u16 netfid; |
781 | struct inode *inode = dentry->d_inode; | ||
780 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); | 782 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); |
781 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | 783 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
782 | struct cifsTconInfo *tcon = cifs_sb->tcon; | 784 | struct cifsTconInfo *tcon = cifs_sb->tcon; |
783 | __u32 dosattr; | 785 | __u32 dosattr, origattr; |
784 | FILE_BASIC_INFO *info_buf; | 786 | FILE_BASIC_INFO *info_buf = NULL; |
785 | 787 | ||
786 | rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, | 788 | rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, |
787 | DELETE|FILE_WRITE_ATTRIBUTES, | 789 | DELETE|FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR, |
788 | CREATE_NOT_DIR|CREATE_DELETE_ON_CLOSE, | ||
789 | &netfid, &oplock, NULL, cifs_sb->local_nls, | 790 | &netfid, &oplock, NULL, cifs_sb->local_nls, |
790 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | 791 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); |
791 | if (rc != 0) | 792 | if (rc != 0) |
792 | goto out; | 793 | goto out; |
793 | 794 | ||
794 | /* set ATTR_HIDDEN and clear ATTR_READONLY */ | 795 | origattr = cifsInode->cifsAttrs; |
795 | cifsInode = CIFS_I(inode); | 796 | if (origattr == 0) |
796 | dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY; | 797 | origattr |= ATTR_NORMAL; |
798 | |||
799 | dosattr = origattr & ~ATTR_READONLY; | ||
797 | if (dosattr == 0) | 800 | if (dosattr == 0) |
798 | dosattr |= ATTR_NORMAL; | 801 | dosattr |= ATTR_NORMAL; |
799 | dosattr |= ATTR_HIDDEN; | 802 | dosattr |= ATTR_HIDDEN; |
800 | 803 | ||
801 | info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL); | 804 | /* set ATTR_HIDDEN and clear ATTR_READONLY, but only if needed */ |
802 | if (info_buf == NULL) { | 805 | if (dosattr != origattr) { |
803 | rc = -ENOMEM; | 806 | info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL); |
804 | goto out_close; | 807 | if (info_buf == NULL) { |
808 | rc = -ENOMEM; | ||
809 | goto out_close; | ||
810 | } | ||
811 | info_buf->Attributes = cpu_to_le32(dosattr); | ||
812 | rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, netfid, | ||
813 | current->tgid); | ||
814 | /* although we would like to mark the file hidden | ||
815 | if that fails we will still try to rename it */ | ||
816 | if (rc != 0) | ||
817 | cifsInode->cifsAttrs = dosattr; | ||
818 | else | ||
819 | dosattr = origattr; /* since not able to change them */ | ||
805 | } | 820 | } |
806 | info_buf->Attributes = cpu_to_le32(dosattr); | ||
807 | rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, netfid, current->tgid); | ||
808 | kfree(info_buf); | ||
809 | if (rc != 0) | ||
810 | goto out_close; | ||
811 | cifsInode->cifsAttrs = dosattr; | ||
812 | 821 | ||
813 | /* silly-rename the file */ | 822 | /* rename the file */ |
814 | CIFSSMBRenameOpenFile(xid, tcon, netfid, NULL, cifs_sb->local_nls, | 823 | rc = CIFSSMBRenameOpenFile(xid, tcon, netfid, NULL, cifs_sb->local_nls, |
815 | cifs_sb->mnt_cifs_flags & | 824 | cifs_sb->mnt_cifs_flags & |
816 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 825 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
826 | if (rc != 0) { | ||
827 | rc = -ETXTBSY; | ||
828 | goto undo_setattr; | ||
829 | } | ||
817 | 830 | ||
818 | /* set DELETE_ON_CLOSE */ | 831 | /* try to set DELETE_ON_CLOSE */ |
819 | rc = CIFSSMBSetFileDisposition(xid, tcon, true, netfid, current->tgid); | 832 | if (!cifsInode->delete_pending) { |
820 | 833 | rc = CIFSSMBSetFileDisposition(xid, tcon, true, netfid, | |
821 | /* | 834 | current->tgid); |
822 | * some samba versions return -ENOENT when we try to set the file | 835 | /* |
823 | * disposition here. Likely a samba bug, but work around it for now | 836 | * some samba versions return -ENOENT when we try to set the |
824 | */ | 837 | * file disposition here. Likely a samba bug, but work around |
825 | if (rc == -ENOENT) | 838 | * it for now. This means that some cifsXXX files may hang |
826 | rc = 0; | 839 | * around after they shouldn't. |
840 | * | ||
841 | * BB: remove this hack after more servers have the fix | ||
842 | */ | ||
843 | if (rc == -ENOENT) | ||
844 | rc = 0; | ||
845 | else if (rc != 0) { | ||
846 | rc = -ETXTBSY; | ||
847 | goto undo_rename; | ||
848 | } | ||
849 | cifsInode->delete_pending = true; | ||
850 | } | ||
827 | 851 | ||
828 | out_close: | 852 | out_close: |
829 | CIFSSMBClose(xid, tcon, netfid); | 853 | CIFSSMBClose(xid, tcon, netfid); |
830 | out: | 854 | out: |
855 | kfree(info_buf); | ||
831 | return rc; | 856 | return rc; |
857 | |||
858 | /* | ||
859 | * reset everything back to the original state. Don't bother | ||
860 | * dealing with errors here since we can't do anything about | ||
861 | * them anyway. | ||
862 | */ | ||
863 | undo_rename: | ||
864 | CIFSSMBRenameOpenFile(xid, tcon, netfid, dentry->d_name.name, | ||
865 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & | ||
866 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
867 | undo_setattr: | ||
868 | if (dosattr != origattr) { | ||
869 | info_buf->Attributes = cpu_to_le32(origattr); | ||
870 | if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, netfid, | ||
871 | current->tgid)) | ||
872 | cifsInode->cifsAttrs = origattr; | ||
873 | } | ||
874 | |||
875 | goto out_close; | ||
832 | } | 876 | } |
833 | 877 | ||
834 | int cifs_unlink(struct inode *dir, struct dentry *dentry) | 878 | int cifs_unlink(struct inode *dir, struct dentry *dentry) |
@@ -878,7 +922,7 @@ psx_del_no_retry: | |||
878 | } else if (rc == -ENOENT) { | 922 | } else if (rc == -ENOENT) { |
879 | d_drop(dentry); | 923 | d_drop(dentry); |
880 | } else if (rc == -ETXTBSY) { | 924 | } else if (rc == -ETXTBSY) { |
881 | rc = cifs_rename_pending_delete(full_path, inode, xid); | 925 | rc = cifs_rename_pending_delete(full_path, dentry, xid); |
882 | if (rc == 0) | 926 | if (rc == 0) |
883 | drop_nlink(inode); | 927 | drop_nlink(inode); |
884 | } else if (rc == -EACCES && dosattr == 0) { | 928 | } else if (rc == -EACCES && dosattr == 0) { |
@@ -1241,22 +1285,21 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, | |||
1241 | return rc; | 1285 | return rc; |
1242 | } | 1286 | } |
1243 | 1287 | ||
1244 | int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, | 1288 | int cifs_rename(struct inode *source_dir, struct dentry *source_dentry, |
1245 | struct inode *target_inode, struct dentry *target_direntry) | 1289 | struct inode *target_dir, struct dentry *target_dentry) |
1246 | { | 1290 | { |
1247 | char *fromName = NULL; | 1291 | char *fromName = NULL; |
1248 | char *toName = NULL; | 1292 | char *toName = NULL; |
1249 | struct cifs_sb_info *cifs_sb_source; | 1293 | struct cifs_sb_info *cifs_sb_source; |
1250 | struct cifs_sb_info *cifs_sb_target; | 1294 | struct cifs_sb_info *cifs_sb_target; |
1251 | struct cifsTconInfo *pTcon; | 1295 | struct cifsTconInfo *tcon; |
1252 | FILE_UNIX_BASIC_INFO *info_buf_source = NULL; | 1296 | FILE_UNIX_BASIC_INFO *info_buf_source = NULL; |
1253 | FILE_UNIX_BASIC_INFO *info_buf_target; | 1297 | FILE_UNIX_BASIC_INFO *info_buf_target; |
1254 | int xid; | 1298 | int xid, rc, tmprc; |
1255 | int rc; | ||
1256 | 1299 | ||
1257 | cifs_sb_target = CIFS_SB(target_inode->i_sb); | 1300 | cifs_sb_target = CIFS_SB(target_dir->i_sb); |
1258 | cifs_sb_source = CIFS_SB(source_inode->i_sb); | 1301 | cifs_sb_source = CIFS_SB(source_dir->i_sb); |
1259 | pTcon = cifs_sb_source->tcon; | 1302 | tcon = cifs_sb_source->tcon; |
1260 | 1303 | ||
1261 | xid = GetXid(); | 1304 | xid = GetXid(); |
1262 | 1305 | ||
@@ -1264,7 +1307,7 @@ int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, | |||
1264 | * BB: this might be allowed if same server, but different share. | 1307 | * BB: this might be allowed if same server, but different share. |
1265 | * Consider adding support for this | 1308 | * Consider adding support for this |
1266 | */ | 1309 | */ |
1267 | if (pTcon != cifs_sb_target->tcon) { | 1310 | if (tcon != cifs_sb_target->tcon) { |
1268 | rc = -EXDEV; | 1311 | rc = -EXDEV; |
1269 | goto cifs_rename_exit; | 1312 | goto cifs_rename_exit; |
1270 | } | 1313 | } |
@@ -1273,65 +1316,65 @@ int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, | |||
1273 | * we already have the rename sem so we do not need to | 1316 | * we already have the rename sem so we do not need to |
1274 | * grab it again here to protect the path integrity | 1317 | * grab it again here to protect the path integrity |
1275 | */ | 1318 | */ |
1276 | fromName = build_path_from_dentry(source_direntry); | 1319 | fromName = build_path_from_dentry(source_dentry); |
1277 | if (fromName == NULL) { | 1320 | if (fromName == NULL) { |
1278 | rc = -ENOMEM; | 1321 | rc = -ENOMEM; |
1279 | goto cifs_rename_exit; | 1322 | goto cifs_rename_exit; |
1280 | } | 1323 | } |
1281 | 1324 | ||
1282 | toName = build_path_from_dentry(target_direntry); | 1325 | toName = build_path_from_dentry(target_dentry); |
1283 | if (toName == NULL) { | 1326 | if (toName == NULL) { |
1284 | rc = -ENOMEM; | 1327 | rc = -ENOMEM; |
1285 | goto cifs_rename_exit; | 1328 | goto cifs_rename_exit; |
1286 | } | 1329 | } |
1287 | 1330 | ||
1288 | rc = cifs_do_rename(xid, source_direntry, fromName, | 1331 | rc = cifs_do_rename(xid, source_dentry, fromName, |
1289 | target_direntry, toName); | 1332 | target_dentry, toName); |
1290 | 1333 | ||
1291 | if (rc == -EEXIST) { | 1334 | if (rc == -EEXIST && tcon->unix_ext) { |
1292 | if (pTcon->unix_ext) { | 1335 | /* |
1293 | /* | 1336 | * Are src and dst hardlinks of same inode? We can |
1294 | * Are src and dst hardlinks of same inode? We can | 1337 | * only tell with unix extensions enabled |
1295 | * only tell with unix extensions enabled | 1338 | */ |
1296 | */ | 1339 | info_buf_source = |
1297 | info_buf_source = | 1340 | kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO), |
1298 | kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO), | 1341 | GFP_KERNEL); |
1299 | GFP_KERNEL); | 1342 | if (info_buf_source == NULL) { |
1300 | if (info_buf_source == NULL) | 1343 | rc = -ENOMEM; |
1301 | goto unlink_target; | 1344 | goto cifs_rename_exit; |
1302 | 1345 | } | |
1303 | info_buf_target = info_buf_source + 1; | ||
1304 | rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName, | ||
1305 | info_buf_source, | ||
1306 | cifs_sb_source->local_nls, | ||
1307 | cifs_sb_source->mnt_cifs_flags & | ||
1308 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
1309 | if (rc != 0) | ||
1310 | goto unlink_target; | ||
1311 | |||
1312 | rc = CIFSSMBUnixQPathInfo(xid, pTcon, | ||
1313 | toName, info_buf_target, | ||
1314 | cifs_sb_target->local_nls, | ||
1315 | /* remap based on source sb */ | ||
1316 | cifs_sb_source->mnt_cifs_flags & | ||
1317 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
1318 | 1346 | ||
1319 | if (rc == 0 && (info_buf_source->UniqueId == | 1347 | info_buf_target = info_buf_source + 1; |
1320 | info_buf_target->UniqueId)) | 1348 | tmprc = CIFSSMBUnixQPathInfo(xid, tcon, fromName, |
1321 | /* same file, POSIX says that this is a noop */ | 1349 | info_buf_source, |
1322 | goto cifs_rename_exit; | 1350 | cifs_sb_source->local_nls, |
1323 | } /* else ... BB we could add the same check for Windows by | 1351 | cifs_sb_source->mnt_cifs_flags & |
1352 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
1353 | if (tmprc != 0) | ||
1354 | goto unlink_target; | ||
1355 | |||
1356 | tmprc = CIFSSMBUnixQPathInfo(xid, tcon, | ||
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 | |||
1363 | if (tmprc == 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 | ||
1324 | checking the UniqueId via FILE_INTERNAL_INFO */ | 1368 | checking the UniqueId via FILE_INTERNAL_INFO */ |
1369 | |||
1325 | unlink_target: | 1370 | unlink_target: |
1326 | /* | 1371 | if ((rc == -EACCES) || (rc == -EEXIST)) { |
1327 | * we either can not tell the files are hardlinked (as with | 1372 | tmprc = cifs_unlink(target_dir, target_dentry); |
1328 | * Windows servers) or files are not hardlinked. Delete the | 1373 | if (tmprc) |
1329 | * target manually before renaming to follow POSIX rather than | 1374 | goto cifs_rename_exit; |
1330 | * Windows semantics | 1375 | |
1331 | */ | 1376 | rc = cifs_do_rename(xid, source_dentry, fromName, |
1332 | cifs_unlink(target_inode, target_direntry); | 1377 | target_dentry, toName); |
1333 | rc = cifs_do_rename(xid, source_direntry, fromName, | ||
1334 | target_direntry, toName); | ||
1335 | } | 1378 | } |
1336 | 1379 | ||
1337 | cifs_rename_exit: | 1380 | cifs_rename_exit: |