Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6: cifs: handle the TCP_Server_Info->tsk field more carefully cifs: fix unlinking of rename target when server doesn't support open file renames [CIFS] improve setlease handling [CIFS] fix saving of resume key before CIFSFindNext cifs: make cifs_rename handle -EACCES errors [CIFS] fix build error [CIFS] undo changes in cifs_rename_pending_delete if it errors out cifs: track DeletePending flag in cifsInodeInfo cifs: don't use CREATE_DELETE_ON_CLOSE in cifs_rename_pending_delete [CIFS] eliminate usage of kthread_stop for cifsd [CIFS] Add nodfs mount option
This commit is contained in:
commit
db563fc2e8
|
@ -1,3 +1,11 @@
|
|||
Version 1.55
|
||||
------------
|
||||
Various fixes to make delete of open files behavior more predictable
|
||||
(when delete of an open file fails we mark the file as "delete-on-close"
|
||||
in a way that more servers accept, but only if we can first rename the
|
||||
file to a temporary name). Add experimental support for more safely
|
||||
handling fcntl(F_SETLEASE).
|
||||
|
||||
Version 1.54
|
||||
------------
|
||||
Fix premature write failure on congested networks (we would give up
|
||||
|
@ -13,6 +21,7 @@ on dns_upcall (resolving DFS referralls). Fix plain text password
|
|||
authentication (requires setting SecurityFlags to 0x30030 to enable
|
||||
lanman and plain text though). Fix writes to be at correct offset when
|
||||
file is open with O_APPEND and file is on a directio (forcediretio) mount.
|
||||
Fix bug in rewinding readdir directory searches. Add nodfs mount option.
|
||||
|
||||
Version 1.53
|
||||
------------
|
||||
|
|
|
@ -463,6 +463,9 @@ A partial list of the supported mount options follows:
|
|||
with cifs style mandatory byte range locks (and most
|
||||
cifs servers do not yet support requesting advisory
|
||||
byte range locks).
|
||||
nodfs Disable DFS (global name space support) even if the
|
||||
server claims to support it. This can help work around
|
||||
a problem with parsing of DFS paths with Samba 3.0.24 server.
|
||||
remount remount the share (often used to change from ro to rw mounts
|
||||
or vice versa)
|
||||
cifsacl Report mode bits (e.g. on stat) based on the Windows ACL for
|
||||
|
@ -488,6 +491,19 @@ A partial list of the supported mount options follows:
|
|||
Note that this differs from the sign mount option in that it
|
||||
causes encryption of data sent over this mounted share but other
|
||||
shares mounted to the same server are unaffected.
|
||||
locallease This option is rarely needed. Fcntl F_SETLEASE is
|
||||
used by some applications such as Samba and NFSv4 server to
|
||||
check to see whether a file is cacheable. CIFS has no way
|
||||
to explicitly request a lease, but can check whether a file
|
||||
is cacheable (oplocked). Unfortunately, even if a file
|
||||
is not oplocked, it could still be cacheable (ie cifs client
|
||||
could grant fcntl leases if no other local processes are using
|
||||
the file) for cases for example such as when the server does not
|
||||
support oplocks and the user is sure that the only updates to
|
||||
the file will be from this client. Specifying this mount option
|
||||
will allow the cifs client to check for leases (only) locally
|
||||
for files which are not oplocked instead of denying leases
|
||||
in that case. (EXPERIMENTAL)
|
||||
sec Security mode. Allowed values are:
|
||||
none attempt to connection as a null user (no name)
|
||||
krb5 Use Kerberos version 5 authentication
|
||||
|
@ -638,6 +654,9 @@ requires enabling CONFIG_CIFS_EXPERIMENTAL
|
|||
cifsacl support needed to retrieve approximated mode bits based on
|
||||
the contents on the CIFS ACL.
|
||||
|
||||
lease support: cifs will check the oplock state before calling into
|
||||
the vfs to see if we can grant a lease on a file.
|
||||
|
||||
DNOTIFY fcntl: needed for support of directory change
|
||||
notification and perhaps later for file leases)
|
||||
|
||||
|
|
|
@ -312,6 +312,7 @@ cifs_alloc_inode(struct super_block *sb)
|
|||
file data or metadata */
|
||||
cifs_inode->clientCanCacheRead = false;
|
||||
cifs_inode->clientCanCacheAll = false;
|
||||
cifs_inode->delete_pending = false;
|
||||
cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */
|
||||
|
||||
/* Can not set i_flags here - they get immediately overwritten
|
||||
|
@ -620,6 +621,37 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin)
|
|||
return generic_file_llseek_unlocked(file, offset, origin);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
||||
static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
|
||||
{
|
||||
/* note that this is called by vfs setlease with the BKL held
|
||||
although I doubt that BKL is needed here in cifs */
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
|
||||
if (!(S_ISREG(inode->i_mode)))
|
||||
return -EINVAL;
|
||||
|
||||
/* check if file is oplocked */
|
||||
if (((arg == F_RDLCK) &&
|
||||
(CIFS_I(inode)->clientCanCacheRead)) ||
|
||||
((arg == F_WRLCK) &&
|
||||
(CIFS_I(inode)->clientCanCacheAll)))
|
||||
return generic_setlease(file, arg, lease);
|
||||
else if (CIFS_SB(inode->i_sb)->tcon->local_lease &&
|
||||
!CIFS_I(inode)->clientCanCacheRead)
|
||||
/* If the server claims to support oplock on this
|
||||
file, then we still need to check oplock even
|
||||
if the local_lease mount option is set, but there
|
||||
are servers which do not support oplock for which
|
||||
this mount option may be useful if the user
|
||||
knows that the file won't be changed on the server
|
||||
by anyone else */
|
||||
return generic_setlease(file, arg, lease);
|
||||
else
|
||||
return -EAGAIN;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct file_system_type cifs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "cifs",
|
||||
|
@ -698,6 +730,7 @@ const struct file_operations cifs_file_ops = {
|
|||
|
||||
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
||||
.dir_notify = cifs_dir_notify,
|
||||
.setlease = cifs_setlease,
|
||||
#endif /* CONFIG_CIFS_EXPERIMENTAL */
|
||||
};
|
||||
|
||||
|
@ -718,6 +751,7 @@ const struct file_operations cifs_file_direct_ops = {
|
|||
.llseek = cifs_llseek,
|
||||
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
||||
.dir_notify = cifs_dir_notify,
|
||||
.setlease = cifs_setlease,
|
||||
#endif /* CONFIG_CIFS_EXPERIMENTAL */
|
||||
};
|
||||
const struct file_operations cifs_file_nobrl_ops = {
|
||||
|
@ -738,6 +772,7 @@ const struct file_operations cifs_file_nobrl_ops = {
|
|||
|
||||
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
||||
.dir_notify = cifs_dir_notify,
|
||||
.setlease = cifs_setlease,
|
||||
#endif /* CONFIG_CIFS_EXPERIMENTAL */
|
||||
};
|
||||
|
||||
|
@ -757,6 +792,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = {
|
|||
.llseek = cifs_llseek,
|
||||
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
||||
.dir_notify = cifs_dir_notify,
|
||||
.setlease = cifs_setlease,
|
||||
#endif /* CONFIG_CIFS_EXPERIMENTAL */
|
||||
};
|
||||
|
||||
|
@ -949,6 +985,12 @@ static int cifs_oplock_thread(void *dummyarg)
|
|||
the call */
|
||||
/* mutex_lock(&inode->i_mutex);*/
|
||||
if (S_ISREG(inode->i_mode)) {
|
||||
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
||||
if (CIFS_I(inode)->clientCanCacheAll == 0)
|
||||
break_lease(inode, FMODE_READ);
|
||||
else if (CIFS_I(inode)->clientCanCacheRead == 0)
|
||||
break_lease(inode, FMODE_WRITE);
|
||||
#endif
|
||||
rc = filemap_fdatawrite(inode->i_mapping);
|
||||
if (CIFS_I(inode)->clientCanCacheRead == 0) {
|
||||
waitrc = filemap_fdatawait(
|
||||
|
|
|
@ -101,5 +101,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
|
|||
extern const struct export_operations cifs_export_ops;
|
||||
#endif /* EXPERIMENTAL */
|
||||
|
||||
#define CIFS_VERSION "1.54"
|
||||
#define CIFS_VERSION "1.55"
|
||||
#endif /* _CIFSFS_H */
|
||||
|
|
|
@ -285,6 +285,7 @@ struct cifsTconInfo {
|
|||
bool seal:1; /* transport encryption for this mounted share */
|
||||
bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol
|
||||
for this mount even if server would support */
|
||||
bool local_lease:1; /* check leases (only) on local system not remote */
|
||||
/* BB add field for back pointer to sb struct(s)? */
|
||||
};
|
||||
|
||||
|
@ -353,6 +354,7 @@ struct cifsInodeInfo {
|
|||
bool clientCanCacheRead:1; /* read oplock */
|
||||
bool clientCanCacheAll:1; /* read and writebehind oplock */
|
||||
bool oplockPending:1;
|
||||
bool delete_pending:1; /* DELETE_ON_CLOSE is set */
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
|
||||
|
|
|
@ -1309,6 +1309,7 @@ OldOpenRetry:
|
|||
cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
|
||||
pfile_info->EndOfFile = pfile_info->AllocationSize;
|
||||
pfile_info->NumberOfLinks = cpu_to_le32(1);
|
||||
pfile_info->DeletePending = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1410,6 +1411,7 @@ openRetry:
|
|||
pfile_info->AllocationSize = pSMBr->AllocationSize;
|
||||
pfile_info->EndOfFile = pSMBr->EndOfFile;
|
||||
pfile_info->NumberOfLinks = cpu_to_le32(1);
|
||||
pfile_info->DeletePending = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -90,6 +90,8 @@ struct smb_vol {
|
|||
bool nocase:1; /* request case insensitive filenames */
|
||||
bool nobrl:1; /* disable sending byte range locks to srv */
|
||||
bool seal:1; /* request transport encryption on share */
|
||||
bool nodfs:1; /* Do not request DFS, even if available */
|
||||
bool local_lease:1; /* check leases only on local system, not remote */
|
||||
unsigned int rsize;
|
||||
unsigned int wsize;
|
||||
unsigned int sockopt;
|
||||
|
@ -124,7 +126,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
|||
struct mid_q_entry *mid_entry;
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (kthread_should_stop()) {
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
/* the demux thread will exit normally
|
||||
next time through the loop */
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
|
@ -184,7 +186,8 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
|||
spin_unlock(&GlobalMid_Lock);
|
||||
up(&server->tcpSem);
|
||||
|
||||
while ((!kthread_should_stop()) && (server->tcpStatus != CifsGood)) {
|
||||
while ((server->tcpStatus != CifsExiting) &&
|
||||
(server->tcpStatus != CifsGood)) {
|
||||
try_to_freeze();
|
||||
if (server->protocolType == IPV6) {
|
||||
rc = ipv6_connect(&server->addr.sockAddr6,
|
||||
|
@ -201,7 +204,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
|||
} else {
|
||||
atomic_inc(&tcpSesReconnectCount);
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (!kthread_should_stop())
|
||||
if (server->tcpStatus != CifsExiting)
|
||||
server->tcpStatus = CifsGood;
|
||||
server->sequence_number = 0;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
|
@ -356,7 +359,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
|
|||
GFP_KERNEL);
|
||||
|
||||
set_freezable();
|
||||
while (!kthread_should_stop()) {
|
||||
while (server->tcpStatus != CifsExiting) {
|
||||
if (try_to_freeze())
|
||||
continue;
|
||||
if (bigbuf == NULL) {
|
||||
|
@ -397,7 +400,7 @@ incomplete_rcv:
|
|||
kernel_recvmsg(csocket, &smb_msg,
|
||||
&iov, 1, pdu_length, 0 /* BB other flags? */);
|
||||
|
||||
if (kthread_should_stop()) {
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
break;
|
||||
} else if (server->tcpStatus == CifsNeedReconnect) {
|
||||
cFYI(1, ("Reconnect after server stopped responding"));
|
||||
|
@ -522,7 +525,7 @@ incomplete_rcv:
|
|||
total_read += length) {
|
||||
length = kernel_recvmsg(csocket, &smb_msg, &iov, 1,
|
||||
pdu_length - total_read, 0);
|
||||
if (kthread_should_stop() ||
|
||||
if ((server->tcpStatus == CifsExiting) ||
|
||||
(length == -EINTR)) {
|
||||
/* then will exit */
|
||||
reconnect = 2;
|
||||
|
@ -651,14 +654,6 @@ multi_t2_fnd:
|
|||
spin_unlock(&GlobalMid_Lock);
|
||||
wake_up_all(&server->response_q);
|
||||
|
||||
/* don't exit until kthread_stop is called */
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
while (!kthread_should_stop()) {
|
||||
schedule();
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
|
||||
/* check if we have blocked requests that need to free */
|
||||
/* Note that cifs_max_pending is normally 50, but
|
||||
can be set at module install time to as little as two */
|
||||
|
@ -755,6 +750,7 @@ multi_t2_fnd:
|
|||
write_unlock(&GlobalSMBSeslock);
|
||||
|
||||
kfree(server->hostname);
|
||||
task_to_wake = xchg(&server->tsk, NULL);
|
||||
kfree(server);
|
||||
|
||||
length = atomic_dec_return(&tcpSesAllocCount);
|
||||
|
@ -762,6 +758,16 @@ multi_t2_fnd:
|
|||
mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
|
||||
GFP_KERNEL);
|
||||
|
||||
/* if server->tsk was NULL then wait for a signal before exiting */
|
||||
if (!task_to_wake) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
while (!signal_pending(current)) {
|
||||
schedule();
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1218,6 +1224,8 @@ cifs_parse_mount_options(char *options, const char *devname,
|
|||
vol->sfu_emul = 1;
|
||||
} else if (strnicmp(data, "nosfu", 5) == 0) {
|
||||
vol->sfu_emul = 0;
|
||||
} else if (strnicmp(data, "nodfs", 5) == 0) {
|
||||
vol->nodfs = 1;
|
||||
} else if (strnicmp(data, "posixpaths", 10) == 0) {
|
||||
vol->posix_paths = 1;
|
||||
} else if (strnicmp(data, "noposixpaths", 12) == 0) {
|
||||
|
@ -1268,6 +1276,10 @@ cifs_parse_mount_options(char *options, const char *devname,
|
|||
vol->no_psx_acl = 0;
|
||||
} else if (strnicmp(data, "noacl", 5) == 0) {
|
||||
vol->no_psx_acl = 1;
|
||||
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
||||
} else if (strnicmp(data, "locallease", 6) == 0) {
|
||||
vol->local_lease = 1;
|
||||
#endif
|
||||
} else if (strnicmp(data, "sign", 4) == 0) {
|
||||
vol->secFlg |= CIFSSEC_MUST_SIGN;
|
||||
} else if (strnicmp(data, "seal", 4) == 0) {
|
||||
|
@ -1845,6 +1857,16 @@ convert_delimiter(char *path, char delim)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
kill_cifsd(struct TCP_Server_Info *server)
|
||||
{
|
||||
struct task_struct *task;
|
||||
|
||||
task = xchg(&server->tsk, NULL);
|
||||
if (task)
|
||||
force_sig(SIGKILL, task);
|
||||
}
|
||||
|
||||
int
|
||||
cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
||||
char *mount_data, const char *devname)
|
||||
|
@ -2166,6 +2188,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||
for the retry flag is used */
|
||||
tcon->retry = volume_info.retry;
|
||||
tcon->nocase = volume_info.nocase;
|
||||
tcon->local_lease = volume_info.local_lease;
|
||||
if (tcon->seal != volume_info.seal)
|
||||
cERROR(1, ("transport encryption setting "
|
||||
"conflicts with existing tid"));
|
||||
|
@ -2197,6 +2220,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||
volume_info.UNC,
|
||||
tcon, cifs_sb->local_nls);
|
||||
cFYI(1, ("CIFS Tcon rc = %d", rc));
|
||||
if (volume_info.nodfs) {
|
||||
tcon->Flags &=
|
||||
~SMB_SHARE_IS_IN_DFS;
|
||||
cFYI(1, ("DFS disabled (%d)",
|
||||
tcon->Flags));
|
||||
}
|
||||
}
|
||||
if (!rc) {
|
||||
atomic_inc(&pSesInfo->inUse);
|
||||
|
@ -2225,14 +2254,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||
spin_lock(&GlobalMid_Lock);
|
||||
srvTcp->tcpStatus = CifsExiting;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
if (srvTcp->tsk) {
|
||||
/* If we could verify that kthread_stop would
|
||||
always wake up processes blocked in
|
||||
tcp in recv_mesg then we could remove the
|
||||
send_sig call */
|
||||
force_sig(SIGKILL, srvTcp->tsk);
|
||||
kthread_stop(srvTcp->tsk);
|
||||
}
|
||||
kill_cifsd(srvTcp);
|
||||
}
|
||||
/* If find_unc succeeded then rc == 0 so we can not end */
|
||||
if (tcon) /* up accidently freeing someone elses tcon struct */
|
||||
|
@ -2245,19 +2267,15 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||
temp_rc = CIFSSMBLogoff(xid, pSesInfo);
|
||||
/* if the socketUseCount is now zero */
|
||||
if ((temp_rc == -ESHUTDOWN) &&
|
||||
(pSesInfo->server) &&
|
||||
(pSesInfo->server->tsk)) {
|
||||
force_sig(SIGKILL,
|
||||
pSesInfo->server->tsk);
|
||||
kthread_stop(pSesInfo->server->tsk);
|
||||
}
|
||||
(pSesInfo->server))
|
||||
kill_cifsd(pSesInfo->server);
|
||||
} else {
|
||||
cFYI(1, ("No session or bad tcon"));
|
||||
if ((pSesInfo->server) &&
|
||||
(pSesInfo->server->tsk)) {
|
||||
force_sig(SIGKILL,
|
||||
pSesInfo->server->tsk);
|
||||
kthread_stop(pSesInfo->server->tsk);
|
||||
if (pSesInfo->server) {
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
srvTcp->tcpStatus = CifsExiting;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
kill_cifsd(pSesInfo->server);
|
||||
}
|
||||
}
|
||||
sesInfoFree(pSesInfo);
|
||||
|
@ -3544,7 +3562,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
|
|||
int rc = 0;
|
||||
int xid;
|
||||
struct cifsSesInfo *ses = NULL;
|
||||
struct task_struct *cifsd_task;
|
||||
char *tmp;
|
||||
|
||||
xid = GetXid();
|
||||
|
@ -3560,7 +3577,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
|
|||
tconInfoFree(cifs_sb->tcon);
|
||||
if ((ses) && (ses->server)) {
|
||||
/* save off task so we do not refer to ses later */
|
||||
cifsd_task = ses->server->tsk;
|
||||
cFYI(1, ("About to do SMBLogoff "));
|
||||
rc = CIFSSMBLogoff(xid, ses);
|
||||
if (rc == -EBUSY) {
|
||||
|
@ -3568,10 +3584,8 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
|
|||
return 0;
|
||||
} else if (rc == -ESHUTDOWN) {
|
||||
cFYI(1, ("Waking up socket by sending signal"));
|
||||
if (cifsd_task) {
|
||||
force_sig(SIGKILL, cifsd_task);
|
||||
kthread_stop(cifsd_task);
|
||||
}
|
||||
if (ses->server)
|
||||
kill_cifsd(ses->server);
|
||||
rc = 0;
|
||||
} /* else - we have an smb session
|
||||
left on this socket do not kill cifsd */
|
||||
|
@ -3701,7 +3715,9 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
|
|||
cERROR(1, ("Send error in SessSetup = %d", rc));
|
||||
} else {
|
||||
cFYI(1, ("CIFS Session Established successfully"));
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
pSesInfo->status = CifsGood;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
|
||||
ss_err_exit:
|
||||
|
|
213
fs/cifs/inode.c
213
fs/cifs/inode.c
|
@ -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 == 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:
|
||||
if (rc == -EEXIST && tcon->unix_ext) {
|
||||
/*
|
||||
* 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
|
||||
* Are src and dst hardlinks of same inode? We can
|
||||
* only tell with unix extensions enabled
|
||||
*/
|
||||
cifs_unlink(target_inode, target_direntry);
|
||||
rc = cifs_do_rename(xid, source_direntry, fromName,
|
||||
target_direntry, toName);
|
||||
info_buf_source =
|
||||
kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),
|
||||
GFP_KERNEL);
|
||||
if (info_buf_source == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto cifs_rename_exit;
|
||||
}
|
||||
|
||||
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:
|
||||
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:
|
||||
|
|
|
@ -762,14 +762,15 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
|
|||
rc));
|
||||
return rc;
|
||||
}
|
||||
cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile);
|
||||
}
|
||||
|
||||
while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) &&
|
||||
(rc == 0) && !cifsFile->srch_inf.endOfSearch) {
|
||||
cFYI(1, ("calling findnext2"));
|
||||
cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile);
|
||||
rc = CIFSFindNext(xid, pTcon, cifsFile->netfid,
|
||||
&cifsFile->srch_inf);
|
||||
cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile);
|
||||
if (rc)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue