|
@@ -506,6 +506,7 @@ int cifs_get_inode_info(struct inode **pinode,
|
|
|
inode = *pinode;
|
|
|
cifsInfo = CIFS_I(inode);
|
|
|
cifsInfo->cifsAttrs = attr;
|
|
|
+ cifsInfo->delete_pending = pfindData->DeletePending ? true : false;
|
|
|
cFYI(1, ("Old time %ld", cifsInfo->time));
|
|
|
cifsInfo->time = jiffies;
|
|
|
cFYI(1, ("New time %ld", cifsInfo->time));
|
|
@@ -772,63 +773,106 @@ out:
|
|
|
* anything else.
|
|
|
*/
|
|
|
static int
|
|
|
-cifs_rename_pending_delete(char *full_path, struct inode *inode, int xid)
|
|
|
+cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid)
|
|
|
{
|
|
|
int oplock = 0;
|
|
|
int rc;
|
|
|
__u16 netfid;
|
|
|
+ struct inode *inode = dentry->d_inode;
|
|
|
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
|
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
|
|
struct cifsTconInfo *tcon = cifs_sb->tcon;
|
|
|
- __u32 dosattr;
|
|
|
- FILE_BASIC_INFO *info_buf;
|
|
|
+ __u32 dosattr, origattr;
|
|
|
+ FILE_BASIC_INFO *info_buf = NULL;
|
|
|
|
|
|
rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
|
|
|
- DELETE|FILE_WRITE_ATTRIBUTES,
|
|
|
- CREATE_NOT_DIR|CREATE_DELETE_ON_CLOSE,
|
|
|
+ DELETE|FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR,
|
|
|
&netfid, &oplock, NULL, cifs_sb->local_nls,
|
|
|
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
|
if (rc != 0)
|
|
|
goto out;
|
|
|
|
|
|
- /* set ATTR_HIDDEN and clear ATTR_READONLY */
|
|
|
- cifsInode = CIFS_I(inode);
|
|
|
- dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY;
|
|
|
+ origattr = cifsInode->cifsAttrs;
|
|
|
+ if (origattr == 0)
|
|
|
+ origattr |= ATTR_NORMAL;
|
|
|
+
|
|
|
+ dosattr = origattr & ~ATTR_READONLY;
|
|
|
if (dosattr == 0)
|
|
|
dosattr |= ATTR_NORMAL;
|
|
|
dosattr |= ATTR_HIDDEN;
|
|
|
|
|
|
- info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL);
|
|
|
- if (info_buf == NULL) {
|
|
|
- rc = -ENOMEM;
|
|
|
- goto out_close;
|
|
|
+ /* set ATTR_HIDDEN and clear ATTR_READONLY, but only if needed */
|
|
|
+ if (dosattr != origattr) {
|
|
|
+ info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL);
|
|
|
+ if (info_buf == NULL) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto out_close;
|
|
|
+ }
|
|
|
+ info_buf->Attributes = cpu_to_le32(dosattr);
|
|
|
+ rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, netfid,
|
|
|
+ current->tgid);
|
|
|
+ /* although we would like to mark the file hidden
|
|
|
+ if that fails we will still try to rename it */
|
|
|
+ if (rc != 0)
|
|
|
+ cifsInode->cifsAttrs = dosattr;
|
|
|
+ else
|
|
|
+ dosattr = origattr; /* since not able to change them */
|
|
|
}
|
|
|
- info_buf->Attributes = cpu_to_le32(dosattr);
|
|
|
- rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, netfid, current->tgid);
|
|
|
- kfree(info_buf);
|
|
|
- if (rc != 0)
|
|
|
- goto out_close;
|
|
|
- cifsInode->cifsAttrs = dosattr;
|
|
|
|
|
|
- /* silly-rename the file */
|
|
|
- CIFSSMBRenameOpenFile(xid, tcon, netfid, NULL, cifs_sb->local_nls,
|
|
|
+ /* rename the file */
|
|
|
+ rc = CIFSSMBRenameOpenFile(xid, tcon, netfid, NULL, cifs_sb->local_nls,
|
|
|
cifs_sb->mnt_cifs_flags &
|
|
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
|
+ if (rc != 0) {
|
|
|
+ rc = -ETXTBSY;
|
|
|
+ goto undo_setattr;
|
|
|
+ }
|
|
|
|
|
|
- /* set DELETE_ON_CLOSE */
|
|
|
- rc = CIFSSMBSetFileDisposition(xid, tcon, true, netfid, current->tgid);
|
|
|
-
|
|
|
- /*
|
|
|
- * some samba versions return -ENOENT when we try to set the file
|
|
|
- * disposition here. Likely a samba bug, but work around it for now
|
|
|
- */
|
|
|
- if (rc == -ENOENT)
|
|
|
- rc = 0;
|
|
|
+ /* try to set DELETE_ON_CLOSE */
|
|
|
+ if (!cifsInode->delete_pending) {
|
|
|
+ rc = CIFSSMBSetFileDisposition(xid, tcon, true, netfid,
|
|
|
+ current->tgid);
|
|
|
+ /*
|
|
|
+ * some samba versions return -ENOENT when we try to set the
|
|
|
+ * file disposition here. Likely a samba bug, but work around
|
|
|
+ * it for now. This means that some cifsXXX files may hang
|
|
|
+ * around after they shouldn't.
|
|
|
+ *
|
|
|
+ * BB: remove this hack after more servers have the fix
|
|
|
+ */
|
|
|
+ if (rc == -ENOENT)
|
|
|
+ rc = 0;
|
|
|
+ else if (rc != 0) {
|
|
|
+ rc = -ETXTBSY;
|
|
|
+ goto undo_rename;
|
|
|
+ }
|
|
|
+ cifsInode->delete_pending = true;
|
|
|
+ }
|
|
|
|
|
|
out_close:
|
|
|
CIFSSMBClose(xid, tcon, netfid);
|
|
|
out:
|
|
|
+ kfree(info_buf);
|
|
|
return rc;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * reset everything back to the original state. Don't bother
|
|
|
+ * dealing with errors here since we can't do anything about
|
|
|
+ * them anyway.
|
|
|
+ */
|
|
|
+undo_rename:
|
|
|
+ CIFSSMBRenameOpenFile(xid, tcon, netfid, dentry->d_name.name,
|
|
|
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
|
|
|
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
|
+undo_setattr:
|
|
|
+ if (dosattr != origattr) {
|
|
|
+ info_buf->Attributes = cpu_to_le32(origattr);
|
|
|
+ if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, netfid,
|
|
|
+ current->tgid))
|
|
|
+ cifsInode->cifsAttrs = origattr;
|
|
|
+ }
|
|
|
+
|
|
|
+ goto out_close;
|
|
|
}
|
|
|
|
|
|
int cifs_unlink(struct inode *dir, struct dentry *dentry)
|
|
@@ -878,7 +922,7 @@ psx_del_no_retry:
|
|
|
} else if (rc == -ENOENT) {
|
|
|
d_drop(dentry);
|
|
|
} else if (rc == -ETXTBSY) {
|
|
|
- rc = cifs_rename_pending_delete(full_path, inode, xid);
|
|
|
+ rc = cifs_rename_pending_delete(full_path, dentry, xid);
|
|
|
if (rc == 0)
|
|
|
drop_nlink(inode);
|
|
|
} else if (rc == -EACCES && dosattr == 0) {
|
|
@@ -1241,22 +1285,21 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
-int cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
|
|
|
- struct inode *target_inode, struct dentry *target_direntry)
|
|
|
+int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
|
|
|
+ struct inode *target_dir, struct dentry *target_dentry)
|
|
|
{
|
|
|
char *fromName = NULL;
|
|
|
char *toName = NULL;
|
|
|
struct cifs_sb_info *cifs_sb_source;
|
|
|
struct cifs_sb_info *cifs_sb_target;
|
|
|
- struct cifsTconInfo *pTcon;
|
|
|
+ struct cifsTconInfo *tcon;
|
|
|
FILE_UNIX_BASIC_INFO *info_buf_source = NULL;
|
|
|
FILE_UNIX_BASIC_INFO *info_buf_target;
|
|
|
- int xid;
|
|
|
- int rc;
|
|
|
+ int xid, rc, tmprc;
|
|
|
|
|
|
- cifs_sb_target = CIFS_SB(target_inode->i_sb);
|
|
|
- cifs_sb_source = CIFS_SB(source_inode->i_sb);
|
|
|
- pTcon = cifs_sb_source->tcon;
|
|
|
+ cifs_sb_target = CIFS_SB(target_dir->i_sb);
|
|
|
+ cifs_sb_source = CIFS_SB(source_dir->i_sb);
|
|
|
+ tcon = cifs_sb_source->tcon;
|
|
|
|
|
|
xid = GetXid();
|
|
|
|
|
@@ -1264,7 +1307,7 @@ int cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
|
|
|
* BB: this might be allowed if same server, but different share.
|
|
|
* Consider adding support for this
|
|
|
*/
|
|
|
- if (pTcon != cifs_sb_target->tcon) {
|
|
|
+ if (tcon != cifs_sb_target->tcon) {
|
|
|
rc = -EXDEV;
|
|
|
goto cifs_rename_exit;
|
|
|
}
|
|
@@ -1273,65 +1316,65 @@ int cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
|
|
|
* we already have the rename sem so we do not need to
|
|
|
* grab it again here to protect the path integrity
|
|
|
*/
|
|
|
- fromName = build_path_from_dentry(source_direntry);
|
|
|
+ fromName = build_path_from_dentry(source_dentry);
|
|
|
if (fromName == NULL) {
|
|
|
rc = -ENOMEM;
|
|
|
goto cifs_rename_exit;
|
|
|
}
|
|
|
|
|
|
- toName = build_path_from_dentry(target_direntry);
|
|
|
+ toName = build_path_from_dentry(target_dentry);
|
|
|
if (toName == NULL) {
|
|
|
rc = -ENOMEM;
|
|
|
goto cifs_rename_exit;
|
|
|
}
|
|
|
|
|
|
- rc = cifs_do_rename(xid, source_direntry, fromName,
|
|
|
- target_direntry, toName);
|
|
|
+ rc = cifs_do_rename(xid, source_dentry, fromName,
|
|
|
+ target_dentry, toName);
|
|
|
|
|
|
- if (rc == -EEXIST) {
|
|
|
- if (pTcon->unix_ext) {
|
|
|
- /*
|
|
|
- * Are src and dst hardlinks of same inode? We can
|
|
|
- * only tell with unix extensions enabled
|
|
|
- */
|
|
|
- info_buf_source =
|
|
|
- kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),
|
|
|
- GFP_KERNEL);
|
|
|
- if (info_buf_source == NULL)
|
|
|
- goto unlink_target;
|
|
|
-
|
|
|
- info_buf_target = info_buf_source + 1;
|
|
|
- rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName,
|
|
|
- info_buf_source,
|
|
|
- cifs_sb_source->local_nls,
|
|
|
- cifs_sb_source->mnt_cifs_flags &
|
|
|
- CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
|
- if (rc != 0)
|
|
|
- goto unlink_target;
|
|
|
-
|
|
|
- rc = CIFSSMBUnixQPathInfo(xid, pTcon,
|
|
|
- toName, info_buf_target,
|
|
|
- cifs_sb_target->local_nls,
|
|
|
- /* remap based on source sb */
|
|
|
- cifs_sb_source->mnt_cifs_flags &
|
|
|
- CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
|
+ if (rc == -EEXIST && tcon->unix_ext) {
|
|
|
+ /*
|
|
|
+ * Are src and dst hardlinks of same inode? We can
|
|
|
+ * only tell with unix extensions enabled
|
|
|
+ */
|
|
|
+ info_buf_source =
|
|
|
+ kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (info_buf_source == NULL) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto cifs_rename_exit;
|
|
|
+ }
|
|
|
|
|
|
- if (rc == 0 && (info_buf_source->UniqueId ==
|
|
|
- info_buf_target->UniqueId))
|
|
|
- /* same file, POSIX says that this is a noop */
|
|
|
- goto cifs_rename_exit;
|
|
|
- } /* else ... BB we could add the same check for Windows by
|
|
|
+ info_buf_target = info_buf_source + 1;
|
|
|
+ tmprc = CIFSSMBUnixQPathInfo(xid, tcon, fromName,
|
|
|
+ info_buf_source,
|
|
|
+ cifs_sb_source->local_nls,
|
|
|
+ cifs_sb_source->mnt_cifs_flags &
|
|
|
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
|
+ if (tmprc != 0)
|
|
|
+ goto unlink_target;
|
|
|
+
|
|
|
+ tmprc = CIFSSMBUnixQPathInfo(xid, tcon,
|
|
|
+ toName, info_buf_target,
|
|
|
+ cifs_sb_target->local_nls,
|
|
|
+ /* remap based on source sb */
|
|
|
+ cifs_sb_source->mnt_cifs_flags &
|
|
|
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
|
+
|
|
|
+ if (tmprc == 0 && (info_buf_source->UniqueId ==
|
|
|
+ info_buf_target->UniqueId))
|
|
|
+ /* same file, POSIX says that this is a noop */
|
|
|
+ goto cifs_rename_exit;
|
|
|
+ } /* else ... BB we could add the same check for Windows by
|
|
|
checking the UniqueId via FILE_INTERNAL_INFO */
|
|
|
+
|
|
|
unlink_target:
|
|
|
- /*
|
|
|
- * we either can not tell the files are hardlinked (as with
|
|
|
- * Windows servers) or files are not hardlinked. Delete the
|
|
|
- * target manually before renaming to follow POSIX rather than
|
|
|
- * Windows semantics
|
|
|
- */
|
|
|
- cifs_unlink(target_inode, target_direntry);
|
|
|
- rc = cifs_do_rename(xid, source_direntry, fromName,
|
|
|
- target_direntry, toName);
|
|
|
+ if ((rc == -EACCES) || (rc == -EEXIST)) {
|
|
|
+ tmprc = cifs_unlink(target_dir, target_dentry);
|
|
|
+ if (tmprc)
|
|
|
+ goto cifs_rename_exit;
|
|
|
+
|
|
|
+ rc = cifs_do_rename(xid, source_dentry, fromName,
|
|
|
+ target_dentry, toName);
|
|
|
}
|
|
|
|
|
|
cifs_rename_exit:
|