Commit 8d4ab5da authored by Linus Torvalds's avatar Linus Torvalds

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: when renaming don't try to unlink negative dentry
  cifs: remove unneeded bcc_ptr update in CIFSTCon
  cifs: add cFYI messages with some of the saved strings from ssetup/tcon
  cifs: fix buffer size for tcon->nativeFileSystem field
  cifs: fix unicode string area word alignment in session setup
  [CIFS] Fix build break caused by change to new current_umask helper function
  [CIFS] Fix sparse warnings
  [CIFS] Add support for posix open during lookup
  cifs: no need to use rcu_assign_pointer on immutable keys
  cifs: remove dnotify thread code
  [CIFS] remove some build warnings
  cifs: vary timeout on writes past EOF based on offset (try #5)
  [CIFS] Fix build break from recent DFS patch when DFS support not enabled
  Remote DFS root support.
  [CIFS] Endian convert UniqueId when reporting inode numbers from server files
  cifs: remove some pointless conditionals before kfree()
  cifs: flush data on any setattr
parents df89f1ba fc6f3943
...@@ -15,7 +15,8 @@ Posix file open support added (turned off after one attempt if server ...@@ -15,7 +15,8 @@ Posix file open support added (turned off after one attempt if server
fails to support it properly, as with Samba server versions prior to 3.3.2) fails to support it properly, as with Samba server versions prior to 3.3.2)
Fix "redzone overwritten" bug in cifs_put_tcon (CIFSTcon may allocate too Fix "redzone overwritten" bug in cifs_put_tcon (CIFSTcon may allocate too
little memory for the "nativeFileSystem" field returned by the server little memory for the "nativeFileSystem" field returned by the server
during mount). during mount). Endian convert inode numbers if necessary (makes it easier
to compare inode numbers on network files from big endian systems).
Version 1.56 Version 1.56
------------ ------------
......
...@@ -41,7 +41,7 @@ cifs_spnego_key_instantiate(struct key *key, const void *data, size_t datalen) ...@@ -41,7 +41,7 @@ cifs_spnego_key_instantiate(struct key *key, const void *data, size_t datalen)
/* attach the data */ /* attach the data */
memcpy(payload, data, datalen); memcpy(payload, data, datalen);
rcu_assign_pointer(key->payload.data, payload); key->payload.data = payload;
ret = 0; ret = 0;
error: error:
......
...@@ -66,9 +66,6 @@ unsigned int sign_CIFS_PDUs = 1; ...@@ -66,9 +66,6 @@ unsigned int sign_CIFS_PDUs = 1;
extern struct task_struct *oplockThread; /* remove sparse warning */ extern struct task_struct *oplockThread; /* remove sparse warning */
struct task_struct *oplockThread = NULL; struct task_struct *oplockThread = NULL;
/* extern struct task_struct * dnotifyThread; remove sparse warning */ /* extern struct task_struct * dnotifyThread; remove sparse warning */
#ifdef CONFIG_CIFS_EXPERIMENTAL
static struct task_struct *dnotifyThread = NULL;
#endif
static const struct super_operations cifs_super_ops; static const struct super_operations cifs_super_ops;
unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
module_param(CIFSMaxBufSize, int, 0); module_param(CIFSMaxBufSize, int, 0);
...@@ -316,6 +313,7 @@ cifs_alloc_inode(struct super_block *sb) ...@@ -316,6 +313,7 @@ cifs_alloc_inode(struct super_block *sb)
cifs_inode->clientCanCacheAll = false; cifs_inode->clientCanCacheAll = false;
cifs_inode->delete_pending = false; cifs_inode->delete_pending = false;
cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */
cifs_inode->server_eof = 0;
/* Can not set i_flags here - they get immediately overwritten /* Can not set i_flags here - they get immediately overwritten
to zero by the VFS */ to zero by the VFS */
...@@ -1040,34 +1038,6 @@ static int cifs_oplock_thread(void *dummyarg) ...@@ -1040,34 +1038,6 @@ static int cifs_oplock_thread(void *dummyarg)
return 0; return 0;
} }
#ifdef CONFIG_CIFS_EXPERIMENTAL
static int cifs_dnotify_thread(void *dummyarg)
{
struct list_head *tmp;
struct TCP_Server_Info *server;
do {
if (try_to_freeze())
continue;
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(15*HZ);
/* check if any stuck requests that need
to be woken up and wakeq so the
thread can wake up and error out */
read_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &cifs_tcp_ses_list) {
server = list_entry(tmp, struct TCP_Server_Info,
tcp_ses_list);
if (atomic_read(&server->inFlight))
wake_up_all(&server->response_q);
}
read_unlock(&cifs_tcp_ses_lock);
} while (!kthread_should_stop());
return 0;
}
#endif
static int __init static int __init
init_cifs(void) init_cifs(void)
{ {
...@@ -1144,21 +1114,8 @@ init_cifs(void) ...@@ -1144,21 +1114,8 @@ init_cifs(void)
goto out_unregister_dfs_key_type; goto out_unregister_dfs_key_type;
} }
#ifdef CONFIG_CIFS_EXPERIMENTAL
dnotifyThread = kthread_run(cifs_dnotify_thread, NULL, "cifsdnotifyd");
if (IS_ERR(dnotifyThread)) {
rc = PTR_ERR(dnotifyThread);
cERROR(1, ("error %d create dnotify thread", rc));
goto out_stop_oplock_thread;
}
#endif
return 0; return 0;
#ifdef CONFIG_CIFS_EXPERIMENTAL
out_stop_oplock_thread:
#endif
kthread_stop(oplockThread);
out_unregister_dfs_key_type: out_unregister_dfs_key_type:
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
unregister_key_type(&key_type_dns_resolver); unregister_key_type(&key_type_dns_resolver);
...@@ -1196,9 +1153,6 @@ exit_cifs(void) ...@@ -1196,9 +1153,6 @@ exit_cifs(void)
cifs_destroy_inodecache(); cifs_destroy_inodecache();
cifs_destroy_mids(); cifs_destroy_mids();
cifs_destroy_request_bufs(); cifs_destroy_request_bufs();
#ifdef CONFIG_CIFS_EXPERIMENTAL
kthread_stop(dnotifyThread);
#endif
kthread_stop(oplockThread); kthread_stop(oplockThread);
} }
......
...@@ -350,7 +350,7 @@ struct cifsFileInfo { ...@@ -350,7 +350,7 @@ struct cifsFileInfo {
bool invalidHandle:1; /* file closed via session abend */ bool invalidHandle:1; /* file closed via session abend */
bool messageMode:1; /* for pipes: message vs byte mode */ bool messageMode:1; /* for pipes: message vs byte mode */
atomic_t wrtPending; /* handle in use - defer close */ atomic_t wrtPending; /* handle in use - defer close */
struct semaphore fh_sem; /* prevents reopen race after dead ses*/ struct mutex fh_mutex; /* prevents reopen race after dead ses*/
struct cifs_search_info srch_inf; struct cifs_search_info srch_inf;
}; };
...@@ -370,6 +370,7 @@ struct cifsInodeInfo { ...@@ -370,6 +370,7 @@ struct cifsInodeInfo {
bool clientCanCacheAll:1; /* read and writebehind oplock */ bool clientCanCacheAll:1; /* read and writebehind oplock */
bool oplockPending:1; bool oplockPending:1;
bool delete_pending:1; /* DELETE_ON_CLOSE is set */ bool delete_pending:1; /* DELETE_ON_CLOSE is set */
u64 server_eof; /* current file size on server */
struct inode vfs_inode; struct inode vfs_inode;
}; };
......
...@@ -2163,7 +2163,7 @@ typedef struct { ...@@ -2163,7 +2163,7 @@ typedef struct {
__le32 Type; __le32 Type;
__le64 DevMajor; __le64 DevMajor;
__le64 DevMinor; __le64 DevMinor;
__u64 UniqueId; __le64 UniqueId;
__le64 Permissions; __le64 Permissions;
__le64 Nlinks; __le64 Nlinks;
} __attribute__((packed)) FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */ } __attribute__((packed)) FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */
...@@ -2308,7 +2308,7 @@ struct unlink_psx_rq { /* level 0x20a SetPathInfo */ ...@@ -2308,7 +2308,7 @@ struct unlink_psx_rq { /* level 0x20a SetPathInfo */
} __attribute__((packed)); } __attribute__((packed));
struct file_internal_info { struct file_internal_info {
__u64 UniqueId; /* inode number */ __le64 UniqueId; /* inode number */
} __attribute__((packed)); /* level 0x3ee */ } __attribute__((packed)); /* level 0x3ee */
struct file_mode_info { struct file_mode_info {
...@@ -2338,7 +2338,7 @@ typedef struct { ...@@ -2338,7 +2338,7 @@ typedef struct {
__le32 Type; __le32 Type;
__le64 DevMajor; __le64 DevMajor;
__le64 DevMinor; __le64 DevMinor;
__u64 UniqueId; __le64 UniqueId;
__le64 Permissions; __le64 Permissions;
__le64 Nlinks; __le64 Nlinks;
char FileName[1]; char FileName[1];
...@@ -2386,7 +2386,7 @@ typedef struct { ...@@ -2386,7 +2386,7 @@ typedef struct {
__le32 FileNameLength; __le32 FileNameLength;
__le32 EaSize; /* EA size */ __le32 EaSize; /* EA size */
__le32 Reserved; __le32 Reserved;
__u64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
char FileName[1]; char FileName[1];
} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */ } __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */
......
...@@ -1626,6 +1626,8 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, ...@@ -1626,6 +1626,8 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
int smb_hdr_len; int smb_hdr_len;
int resp_buf_type = 0; int resp_buf_type = 0;
*nbytes = 0;
cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count)); cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count));
if (tcon->ses->capabilities & CAP_LARGE_FILES) { if (tcon->ses->capabilities & CAP_LARGE_FILES) {
...@@ -1682,11 +1684,9 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, ...@@ -1682,11 +1684,9 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
cifs_stats_inc(&tcon->num_writes); cifs_stats_inc(&tcon->num_writes);
if (rc) { if (rc) {
cFYI(1, ("Send error Write2 = %d", rc)); cFYI(1, ("Send error Write2 = %d", rc));
*nbytes = 0;
} else if (resp_buf_type == 0) { } else if (resp_buf_type == 0) {
/* presumably this can not happen, but best to be safe */ /* presumably this can not happen, but best to be safe */
rc = -EIO; rc = -EIO;
*nbytes = 0;
} else { } else {
WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base; WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
*nbytes = le16_to_cpu(pSMBr->CountHigh); *nbytes = le16_to_cpu(pSMBr->CountHigh);
...@@ -3918,7 +3918,7 @@ GetInodeNumberRetry: ...@@ -3918,7 +3918,7 @@ GetInodeNumberRetry:
} }
pfinfo = (struct file_internal_info *) pfinfo = (struct file_internal_info *)
(data_offset + (char *) &pSMBr->hdr.Protocol); (data_offset + (char *) &pSMBr->hdr.Protocol);
*inode_number = pfinfo->UniqueId; *inode_number = le64_to_cpu(pfinfo->UniqueId);
} }
} }
GetInodeNumOut: GetInodeNumOut:
......
This diff is collapsed.
...@@ -129,12 +129,62 @@ cifs_bp_rename_retry: ...@@ -129,12 +129,62 @@ cifs_bp_rename_retry:
return full_path; return full_path;
} }
static void
cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle,
struct cifsTconInfo *tcon, bool write_only)
{
int oplock = 0;
struct cifsFileInfo *pCifsFile;
struct cifsInodeInfo *pCifsInode;
pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
if (pCifsFile == NULL)
return;
if (oplockEnabled)
oplock = REQ_OPLOCK;
pCifsFile->netfid = fileHandle;
pCifsFile->pid = current->tgid;
pCifsFile->pInode = newinode;
pCifsFile->invalidHandle = false;
pCifsFile->closePend = false;
mutex_init(&pCifsFile->fh_mutex);
mutex_init(&pCifsFile->lock_mutex);
INIT_LIST_HEAD(&pCifsFile->llist);
atomic_set(&pCifsFile->wrtPending, 0);
/* set the following in open now
pCifsFile->pfile = file; */
write_lock(&GlobalSMBSeslock);
list_add(&pCifsFile->tlist, &tcon->openFileList);
pCifsInode = CIFS_I(newinode);
if (pCifsInode) {
/* if readable file instance put first in list*/
if (write_only)
list_add_tail(&pCifsFile->flist,
&pCifsInode->openFileList);
else
list_add(&pCifsFile->flist, &pCifsInode->openFileList);
if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
pCifsInode->clientCanCacheAll = true;
pCifsInode->clientCanCacheRead = true;
cFYI(1, ("Exclusive Oplock inode %p", newinode));
} else if ((oplock & 0xF) == OPLOCK_READ)
pCifsInode->clientCanCacheRead = true;
}
write_unlock(&GlobalSMBSeslock);
}
int cifs_posix_open(char *full_path, struct inode **pinode, int cifs_posix_open(char *full_path, struct inode **pinode,
struct super_block *sb, int mode, int oflags, struct super_block *sb, int mode, int oflags,
int *poplock, __u16 *pnetfid, int xid) int *poplock, __u16 *pnetfid, int xid)
{ {
int rc; int rc;
__u32 oplock; __u32 oplock;
bool write_only = false;
FILE_UNIX_BASIC_INFO *presp_data; FILE_UNIX_BASIC_INFO *presp_data;
__u32 posix_flags = 0; __u32 posix_flags = 0;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
...@@ -172,6 +222,8 @@ int cifs_posix_open(char *full_path, struct inode **pinode, ...@@ -172,6 +222,8 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
if (oflags & O_DIRECT) if (oflags & O_DIRECT)
posix_flags |= SMB_O_DIRECT; posix_flags |= SMB_O_DIRECT;
if (!(oflags & FMODE_READ))
write_only = true;
rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode, rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode,
pnetfid, presp_data, &oplock, full_path, pnetfid, presp_data, &oplock, full_path,
...@@ -187,8 +239,10 @@ int cifs_posix_open(char *full_path, struct inode **pinode, ...@@ -187,8 +239,10 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
if (!pinode) if (!pinode)
goto posix_open_ret; /* caller does not need info */ goto posix_open_ret; /* caller does not need info */
if (*pinode == NULL) if (*pinode == NULL) {
*pinode = cifs_new_inode(sb, &presp_data->UniqueId); __u64 unique_id = le64_to_cpu(presp_data->UniqueId);
*pinode = cifs_new_inode(sb, &unique_id);
}
/* else an inode was passed in. Update its info, don't create one */ /* else an inode was passed in. Update its info, don't create one */
/* We do not need to close the file if new_inode fails since /* We do not need to close the file if new_inode fails since
...@@ -198,6 +252,8 @@ int cifs_posix_open(char *full_path, struct inode **pinode, ...@@ -198,6 +252,8 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
posix_fill_in_inode(*pinode, presp_data, 1); posix_fill_in_inode(*pinode, presp_data, 1);
cifs_fill_fileinfo(*pinode, *pnetfid, cifs_sb->tcon, write_only);
posix_open_ret: posix_open_ret:
kfree(presp_data); kfree(presp_data);
return rc; return rc;
...@@ -239,7 +295,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, ...@@ -239,7 +295,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
char *full_path = NULL; char *full_path = NULL;
FILE_ALL_INFO *buf = NULL; FILE_ALL_INFO *buf = NULL;
struct inode *newinode = NULL; struct inode *newinode = NULL;
struct cifsInodeInfo *pCifsInode;
int disposition = FILE_OVERWRITE_IF; int disposition = FILE_OVERWRITE_IF;
bool write_only = false; bool write_only = false;
...@@ -410,44 +465,8 @@ cifs_create_set_dentry: ...@@ -410,44 +465,8 @@ cifs_create_set_dentry:
/* mknod case - do not leave file open */ /* mknod case - do not leave file open */
CIFSSMBClose(xid, tcon, fileHandle); CIFSSMBClose(xid, tcon, fileHandle);
} else if (newinode) { } else if (newinode) {
struct cifsFileInfo *pCifsFile = cifs_fill_fileinfo(newinode, fileHandle,
kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); cifs_sb->tcon, write_only);
if (pCifsFile == NULL)
goto cifs_create_out;
pCifsFile->netfid = fileHandle;
pCifsFile->pid = current->tgid;
pCifsFile->pInode = newinode;
pCifsFile->invalidHandle = false;
pCifsFile->closePend = false;
init_MUTEX(&pCifsFile->fh_sem);
mutex_init(&pCifsFile->lock_mutex);
INIT_LIST_HEAD(&pCifsFile->llist);
atomic_set(&pCifsFile->wrtPending, 0);
/* set the following in open now
pCifsFile->pfile = file; */
write_lock(&GlobalSMBSeslock);
list_add(&pCifsFile->tlist, &tcon->openFileList);
pCifsInode = CIFS_I(newinode);
if (pCifsInode) {
/* if readable file instance put first in list*/
if (write_only) {
list_add_tail(&pCifsFile->flist,
&pCifsInode->openFileList);
} else {
list_add(&pCifsFile->flist,
&pCifsInode->openFileList);
}
if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
pCifsInode->clientCanCacheAll = true;
pCifsInode->clientCanCacheRead = true;
cFYI(1, ("Exclusive Oplock inode %p",
newinode));
} else if ((oplock & 0xF) == OPLOCK_READ)
pCifsInode->clientCanCacheRead = true;
}
write_unlock(&GlobalSMBSeslock);
} }
cifs_create_out: cifs_create_out:
kfree(buf); kfree(buf);
...@@ -580,17 +599,21 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, ...@@ -580,17 +599,21 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
return rc; return rc;
} }
struct dentry * struct dentry *
cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
struct nameidata *nd) struct nameidata *nd)
{ {
int xid; int xid;
int rc = 0; /* to get around spurious gcc warning, set to zero here */ int rc = 0; /* to get around spurious gcc warning, set to zero here */
int oplock = 0;
int mode;
__u16 fileHandle = 0;
bool posix_open = false;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
struct inode *newInode = NULL; struct inode *newInode = NULL;
char *full_path = NULL; char *full_path = NULL;
struct file *filp;
xid = GetXid(); xid = GetXid();
...@@ -632,12 +655,37 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -632,12 +655,37 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
} }
cFYI(1, ("Full path: %s inode = 0x%p", full_path, direntry->d_inode)); cFYI(1, ("Full path: %s inode = 0x%p", full_path, direntry->d_inode));
if (pTcon->unix_ext) if (pTcon->unix_ext) {
rc = cifs_get_inode_info_unix(&newInode, full_path, if (!(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) &&
parent_dir_inode->i_sb, xid); (nd->flags & LOOKUP_OPEN)) {
else if (!((nd->intent.open.flags & O_CREAT) &&
(nd->intent.open.flags & O_EXCL))) {
mode = nd->intent.open.create_mode &
~current_umask();
rc = cifs_posix_open(full_path, &newInode,
parent_dir_inode->i_sb, mode,
nd->intent.open.flags, &oplock,
&fileHandle, xid);
/*
* This code works around a bug in
* samba posix open in samba versions 3.3.1
* and earlier where create works
* but open fails with invalid parameter.
* If either of these error codes are
* returned, follow the normal lookup.
* Otherwise, the error during posix open
* is handled.
*/
if ((rc != -EINVAL) && (rc != -EOPNOTSUPP))
posix_open = true;
}
}
if (!posix_open)
rc = cifs_get_inode_info_unix(&newInode, full_path,
parent_dir_inode->i_sb, xid);
} else
rc = cifs_get_inode_info(&newInode, full_path, NULL, rc = cifs_get_inode_info(&newInode, full_path, NULL,
parent_dir_inode->i_sb, xid, NULL); parent_dir_inode->i_sb, xid, NULL);
if ((rc == 0) && (newInode != NULL)) { if ((rc == 0) && (newInode != NULL)) {
if (pTcon->nocase) if (pTcon->nocase)
...@@ -645,7 +693,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -645,7 +693,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
else else
direntry->d_op = &cifs_dentry_ops; direntry->d_op = &cifs_dentry_ops;
d_add(direntry, newInode); d_add(direntry, newInode);
if (posix_open)
filp = lookup_instantiate_filp(nd, direntry, NULL);
/* since paths are not looked up by component - the parent /* since paths are not looked up by component - the parent
directories are presumed to be good here */ directories are presumed to be good here */
renew_parental_timestamps(direntry); renew_parental_timestamps(direntry);
......
...@@ -78,7 +78,7 @@ dns_resolver_instantiate(struct key *key, const void *data, ...@@ -78,7 +78,7 @@ dns_resolver_instantiate(struct key *key, const void *data,
} }
key->type_data.x[0] = datalen; key->type_data.x[0] = datalen;
rcu_assign_pointer(key->payload.data, ip); key->payload.data = ip;
return rc; return rc;
} }
......
...@@ -46,7 +46,7 @@ static inline struct cifsFileInfo *cifs_init_private( ...@@ -46,7 +46,7 @@ static inline struct cifsFileInfo *cifs_init_private(
memset(private_data, 0, sizeof(struct cifsFileInfo)); memset(private_data, 0, sizeof(struct cifsFileInfo));
private_data->netfid = netfid; private_data->netfid = netfid;
private_data->pid = current->tgid; private_data->pid = current->tgid;
init_MUTEX(&private_data->fh_sem); mutex_init(&private_data->fh_mutex);
mutex_init(&private_data->lock_mutex); mutex_init(&private_data->lock_mutex);
INIT_LIST_HEAD(&private_data->llist); INIT_LIST_HEAD(&private_data->llist);
private_data->pfile = file; /* needed for writepage */ private_data->pfile = file; /* needed for writepage */
...@@ -284,35 +284,32 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -284,35 +284,32 @@ int cifs_open(struct inode *inode, struct file *file)
cifs_sb = CIFS_SB(inode->i_sb); cifs_sb = CIFS_SB(inode->i_sb);
tcon = cifs_sb->tcon; tcon = cifs_sb->tcon;
if (file->f_flags & O_CREAT) { /* search inode for this file and fill in file->private_data */
/* search inode for this file and fill in file->private_data */ pCifsInode = CIFS_I(file->f_path.dentry->d_inode);
pCifsInode = CIFS_I(file->f_path.dentry->d_inode); read_lock(&GlobalSMBSeslock);
read_lock(&GlobalSMBSeslock); list_for_each(tmp, &pCifsInode->openFileList) {
list_for_each(tmp, &pCifsInode->openFileList) { pCifsFile = list_entry(tmp, struct cifsFileInfo,
pCifsFile = list_entry(tmp, struct cifsFileInfo, flist);
flist); if ((pCifsFile->pfile == NULL) &&
if ((pCifsFile->pfile == NULL) && (pCifsFile->pid == current->tgid)) {
(pCifsFile->pid == current->tgid)) { /* mode set in cifs_create */
/* mode set in cifs_create */
/* needed for writepage */
/* needed for writepage */ pCifsFile->pfile = file;
pCifsFile->pfile = file;
file->private_data = pCifsFile;
file->private_data = pCifsFile; break;
break;
}
}
read_unlock(&GlobalSMBSeslock);
if (file->private_data != NULL) {
rc = 0;
FreeXid(xid);
return rc;
} else {
if (file->f_flags & O_EXCL)
cERROR(1, ("could not find file instance for "
"new file %p", file));
} }
} }
read_unlock(&GlobalSMBSeslock);
if (file->private_data != NULL) {
rc = 0;
FreeXid(xid);
return rc;
} else if ((file->f_flags & O_CREAT) && (file->f_flags & O_EXCL))
cERROR(1, ("could not find file instance for "
"new file %p", file));
full_path = build_path_from_dentry(file->f_path.dentry); full_path = build_path_from_dentry(file->f_path.dentry);
if (full_path == NULL) { if (full_path == NULL) {
...@@ -500,9 +497,9 @@ static int cifs_reopen_file(struct file *file, bool can_flush) ...@@ -500,9 +497,9 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
return -EBADF; return -EBADF;
xid = GetXid(); xid = GetXid();
down(&pCifsFile->fh_sem); mutex_unlock(&pCifsFile->fh_mutex);
if (!pCifsFile->invalidHandle) { if (!pCifsFile->invalidHandle) {
up(&pCifsFile->fh_sem); mutex_lock(&pCifsFile->fh_mutex);
FreeXid(xid); FreeXid(xid);
return 0; return 0;
} }
...@@ -533,7 +530,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush) ...@@ -533,7 +530,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
if (full_path == NULL) { if (full_path == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
reopen_error_exit: reopen_error_exit:
up(&pCifsFile->fh_sem); mutex_lock(&pCifsFile->fh_mutex);
FreeXid(xid); FreeXid(xid);
return rc; return rc;
} }
...@@ -575,14 +572,14 @@ reopen_error_exit: ...@@ -575,14 +572,14 @@ reopen_error_exit:
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc) { if (rc) {
up(&pCifsFile->fh_sem); mutex_lock(&pCifsFile->fh_mutex);
cFYI(1, ("cifs_open returned 0x%x", rc)); cFYI(1, ("cifs_open returned 0x%x", rc));
cFYI(1, ("oplock: %d", oplock)); cFYI(1, ("oplock: %d", oplock));
} else { } else {
reopen_success: reopen_success:
pCifsFile->netfid = netfid; pCifsFile->netfid = netfid;
pCifsFile->invalidHandle = false; pCifsFile->invalidHandle = false;
up(&pCifsFile->fh_sem); mutex_lock(&pCifsFile->fh_mutex);
pCifsInode = CIFS_I(inode); pCifsInode = CIFS_I(inode);
if (pCifsInode) { if (pCifsInode) {
if (can_flush) { if (can_flush) {
...@@ -971,6 +968,40 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) ...@@ -971,6 +968,40 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
return rc; return rc;
} }
/*
* Set the timeout on write requests past EOF. For some servers (Windows)
* these calls can be very long.
*
* If we're writing >10M past the EOF we give a 180s timeout. Anything less
* than that gets a 45s timeout. Writes not past EOF get 15s timeouts.
* The 10M cutoff is totally arbitrary. A better scheme for this would be
* welcome if someone wants to suggest one.
*
* We may be able to do a better job with this if there were some way to
* declare that a file should be sparse.
*/
static int
cifs_write_timeout(struct cifsInodeInfo *cifsi, loff_t offset)
{
if (offset <= cifsi->server_eof)
return CIFS_STD_OP;
else if (offset > (cifsi->server_eof + (10 * 1024 * 1024)))
return CIFS_VLONG_OP;
else
return CIFS_LONG_OP;
}
/* update the file size (if needed) after a write */
static void
cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
unsigned int bytes_written)
{
loff_t end_of_write = offset + bytes_written;
if (end_of_write > cifsi->server_eof)
cifsi->server_eof = end_of_write;
}
ssize_t cifs_user_write(struct file *file, const char __user *write_data, ssize_t cifs_user_write(struct file *file, const char __user *write_data,
size_t write_size, loff_t *poffset) size_t write_size, loff_t *poffset)
{ {
...@@ -981,6 +1012,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, ...@@ -981,6 +1012,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
int xid, long_op; int xid, long_op;
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
struct cifsInodeInfo *cifsi = CIFS_I(file->f_path.dentry->d_inode);
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
...@@ -1000,11 +1032,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, ...@@ -1000,11 +1032,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
xid = GetXid(); xid = GetXid();
if (*poffset > file->f_path.dentry->d_inode->i_size) long_op = cifs_write_timeout(cifsi, *poffset);
long_op = CIFS_VLONG_OP; /* writes past EOF take long time */
else
long_op = CIFS_LONG_OP;
for (total_written = 0; write_size > total_written; for (total_written = 0; write_size > total_written;
total_written += bytes_written) { total_written += bytes_written) {
rc = -EAGAIN; rc = -EAGAIN;
...@@ -1048,8 +1076,10 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, ...@@ -1048,8 +1076,10 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
FreeXid(xid); FreeXid(xid);
return rc; return rc;
} }
} else } else {
cifs_update_eof(cifsi, *poffset, bytes_written);
*poffset += bytes_written; *poffset += bytes_written;
}
long_op = CIFS_STD_OP; /* subsequent writes fast - long_op = CIFS_STD_OP; /* subsequent writes fast -
15 seconds is plenty */ 15 seconds is plenty */
} }
...@@ -1085,6 +1115,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data, ...@@ -1085,6 +1115,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
int xid, long_op; int xid, long_op;
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
struct cifsInodeInfo *cifsi = CIFS_I(file->f_path.dentry->d_inode);
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
...@@ -1099,11 +1130,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data, ...@@ -1099,11 +1130,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
xid = GetXid(); xid = GetXid();
if (*poffset > file->f_path.dentry->d_inode->i_size) long_op = cifs_write_timeout(cifsi, *poffset);
long_op = CIFS_VLONG_OP; /* writes past EOF can be slow */
else
long_op = CIFS_LONG_OP;
for (total_written = 0; write_size > total_written; for (total_written = 0; write_size > total_written;
total_written += bytes_written) { total_written += bytes_written) {
rc = -EAGAIN; rc = -EAGAIN;
...@@ -1166,8 +1193,10 @@ static ssize_t cifs_write(struct file *file, const char *write_data, ...@@ -1166,8 +1193,10 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
FreeXid(xid); FreeXid(xid);
return rc; return rc;
} }
} else } else {
cifs_update_eof(cifsi, *poffset, bytes_written);
*poffset += bytes_written; *poffset += bytes_written;
}
long_op = CIFS_STD_OP; /* subsequent writes fast - long_op = CIFS_STD_OP; /* subsequent writes fast -
15 seconds is plenty */ 15 seconds is plenty */
} }
...@@ -1380,11 +1409,12 @@ static int cifs_writepages(struct address_space *mapping, ...@@ -1380,11 +1409,12 @@ static int cifs_writepages(struct address_space *mapping,
int nr_pages; int nr_pages;
__u64 offset = 0; __u64 offset = 0;
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
struct cifsInodeInfo *cifsi = CIFS_I(mapping->host);
struct page *page; struct page *page;
struct pagevec pvec; struct pagevec pvec;
int rc = 0; int rc = 0;
int scanned = 0; int scanned = 0;
int xid; int xid, long_op;
cifs_sb = CIFS_SB(mapping->host->i_sb); cifs_sb = CIFS_SB(mapping->host->i_sb);
...@@ -1528,12 +1558,15 @@ retry: ...@@ -1528,12 +1558,15 @@ retry:
cERROR(1, ("No writable handles for inode")); cERROR(1, ("No writable handles for inode"));
rc = -EBADF; rc = -EBADF;
} else { } else {
long_op = cifs_write_timeout(cifsi, offset);
rc = CIFSSMBWrite2(xid, cifs_sb->tcon, rc = CIFSSMBWrite2(xid, cifs_sb->tcon,
open_file->netfid, open_file->netfid,
bytes_to_write, offset, bytes_to_write, offset,
&bytes_written, iov, n_iov, &bytes_written, iov, n_iov,
CIFS_LONG_OP); long_op);
atomic_dec(&open_file->wrtPending); atomic_dec(&open_file->wrtPending);
cifs_update_eof(cifsi, offset, bytes_written);
if (rc || bytes_written < bytes_to_write) { if (rc || bytes_written < bytes_to_write) {
cERROR(1, ("Write2 ret %d, wrote %d", cERROR(1, ("Write2 ret %d, wrote %d",
rc, bytes_written)); rc, bytes_written));
......
...@@ -143,6 +143,7 @@ static void cifs_unix_info_to_inode(struct inode *inode, ...@@ -143,6 +143,7 @@ static void cifs_unix_info_to_inode(struct inode *inode,
inode->i_nlink = le64_to_cpu(info->Nlinks); inode->i_nlink = le64_to_cpu(info->Nlinks);
cifsInfo->server_eof = end_of_file;
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
if (is_size_safe_to_change(cifsInfo, end_of_file)) { if (is_size_safe_to_change(cifsInfo, end_of_file)) {
/* /*
...@@ -276,7 +277,8 @@ int cifs_get_inode_info_unix(struct inode **pinode, ...@@ -276,7 +277,8 @@ int cifs_get_inode_info_unix(struct inode **pinode,
/* get new inode */ /* get new inode */
if (*pinode == NULL) { if (*pinode == NULL) {
*pinode = cifs_new_inode(sb, &find_data.UniqueId); __u64 unique_id = le64_to_cpu(find_data.UniqueId);
*pinode = cifs_new_inode(sb, &unique_id);
if (*pinode == NULL) { if (*pinode == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
goto cgiiu_exit; goto cgiiu_exit;
...@@ -605,12 +607,12 @@ int cifs_get_inode_info(struct inode **pinode, ...@@ -605,12 +607,12 @@ int cifs_get_inode_info(struct inode **pinode,
inode->i_mode |= S_IFREG; inode->i_mode |= S_IFREG;
} }
cifsInfo->server_eof = le64_to_cpu(pfindData->EndOfFile);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
if (is_size_safe_to_change(cifsInfo, if (is_size_safe_to_change(cifsInfo, cifsInfo->server_eof)) {
le64_to_cpu(pfindData->EndOfFile))) {
/* can not safely shrink the file size here if the /* can not safely shrink the file size here if the
client is writing to it due to potential races */ client is writing to it due to potential races */
i_size_write(inode, le64_to_cpu(pfindData->EndOfFile)); i_size_write(inode, cifsInfo->server_eof);
/* 512 bytes (2**9) is the fake blocksize that must be /* 512 bytes (2**9) is the fake blocksize that must be
used for this calculation */ used for this calculation */
...@@ -1138,6 +1140,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) ...@@ -1138,6 +1140,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
cFYI(1, ("posix mkdir returned 0x%x", rc)); cFYI(1, ("posix mkdir returned 0x%x", rc));
d_drop(direntry); d_drop(direntry);
} else { } else {
__u64 unique_id;
if (pInfo->Type == cpu_to_le32(-1)) { if (pInfo->Type == cpu_to_le32(-1)) {
/* no return info, go query for it */ /* no return info, go query for it */
kfree(pInfo); kfree(pInfo);
...@@ -1151,8 +1154,8 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) ...@@ -1151,8 +1154,8 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
else else
direntry->d_op = &cifs_dentry_ops; direntry->d_op = &cifs_dentry_ops;
newinode = cifs_new_inode(inode->i_sb, unique_id = le64_to_cpu(pInfo->UniqueId);
&pInfo->UniqueId); newinode = cifs_new_inode(inode->i_sb, &unique_id);
if (newinode == NULL) { if (newinode == NULL) {
kfree(pInfo); kfree(pInfo);
goto mkdir_get_info; goto mkdir_get_info;
...@@ -1450,7 +1453,8 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry, ...@@ -1450,7 +1453,8 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
checking the UniqueId via FILE_INTERNAL_INFO */ checking the UniqueId via FILE_INTERNAL_INFO */
unlink_target: unlink_target:
if ((rc == -EACCES) || (rc == -EEXIST)) { /* Try unlinking the target dentry if it's not negative */
if (target_dentry->d_inode && (rc == -EACCES || rc == -EEXIST)) {
tmprc = cifs_unlink(target_dir, target_dentry); tmprc = cifs_unlink(target_dir, target_dentry);
if (tmprc) if (tmprc)
goto cifs_rename_exit; goto cifs_rename_exit;
...@@ -1753,6 +1757,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, ...@@ -1753,6 +1757,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
} }
if (rc == 0) { if (rc == 0) {
cifsInode->server_eof = attrs->ia_size;
rc = cifs_vmtruncate(inode, attrs->ia_size); rc = cifs_vmtruncate(inode, attrs->ia_size);
cifs_truncate_page(inode->i_mapping, inode->i_size); cifs_truncate_page(inode->i_mapping, inode->i_size);
} }
...@@ -1792,20 +1797,21 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) ...@@ -1792,20 +1797,21 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
goto out; goto out;
} }
if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) { /*
/* * Attempt to flush data before changing attributes. We need to do
Flush data before changing file size or changing the last * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the
write time of the file on the server. If the * ownership or mode then we may also need to do this. Here, we take
flush returns error, store it to report later and continue. * the safe way out and just do the flush on all setattr requests. If
BB: This should be smarter. Why bother flushing pages that * the flush returns error, store it to report later and continue.
will be truncated anyway? Also, should we error out here if *
the flush returns error? * BB: This should be smarter. Why bother flushing pages that
*/ * will be truncated anyway? Also, should we error out here if
rc = filemap_write_and_wait(inode->i_mapping); * the flush returns error?
if (rc != 0) { */
cifsInode->write_behind_rc = rc; rc = filemap_write_and_wait(inode->i_mapping);
rc = 0; if (rc != 0) {
} cifsInode->write_behind_rc = rc;
rc = 0;
} }
if (attrs->ia_valid & ATTR_SIZE) { if (attrs->ia_valid & ATTR_SIZE) {
...@@ -1903,20 +1909,21 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) ...@@ -1903,20 +1909,21 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
return -ENOMEM; return -ENOMEM;
} }
if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) { /*
/* * Attempt to flush data before changing attributes. We need to do
Flush data before changing file size or changing the last * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the
write time of the file on the server. If the * ownership or mode then we may also need to do this. Here, we take
flush returns error, store it to report later and continue. * the safe way out and just do the flush on all setattr requests. If
BB: This should be smarter. Why bother flushing pages that * the flush returns error, store it to report later and continue.
will be truncated anyway? Also, should we error out here if *
the flush returns error? * BB: This should be smarter. Why bother flushing pages that
*/ * will be truncated anyway? Also, should we error out here if
rc = filemap_write_and_wait(inode->i_mapping); * the flush returns error?
if (rc != 0) { */
cifsInode->write_behind_rc = rc; rc = filemap_write_and_wait(inode->i_mapping);
rc = 0; if (rc != 0) {
} cifsInode->write_behind_rc = rc;
rc = 0;
} }
if (attrs->ia_valid & ATTR_SIZE) { if (attrs->ia_valid & ATTR_SIZE) {
......
...@@ -239,6 +239,7 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, ...@@ -239,6 +239,7 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type,
if (atomic_read(&cifsInfo->inUse) == 0) if (atomic_read(&cifsInfo->inUse) == 0)
atomic_set(&cifsInfo->inUse, 1); atomic_set(&cifsInfo->inUse, 1);
cifsInfo->server_eof = end_of_file;
spin_lock(&tmp_inode->i_lock); spin_lock(&tmp_inode->i_lock);
if (is_size_safe_to_change(cifsInfo, end_of_file)) { if (is_size_safe_to_change(cifsInfo, end_of_file)) {
/* can not safely change the file size here if the /* can not safely change the file size here if the
...@@ -375,6 +376,7 @@ static void unix_fill_in_inode(struct inode *tmp_inode, ...@@ -375,6 +376,7 @@ static void unix_fill_in_inode(struct inode *tmp_inode,
tmp_inode->i_gid = le64_to_cpu(pfindData->Gid); tmp_inode->i_gid = le64_to_cpu(pfindData->Gid);
tmp_inode->i_nlink = le64_to_cpu(pfindData->Nlinks); tmp_inode->i_nlink = le64_to_cpu(pfindData->Nlinks);
cifsInfo->server_eof = end_of_file;
spin_lock(&tmp_inode->i_lock); spin_lock(&tmp_inode->i_lock);
if (is_size_safe_to_change(cifsInfo, end_of_file)) { if (is_size_safe_to_change(cifsInfo, end_of_file)) {
/* can not safely change the file size here if the /* can not safely change the file size here if the
...@@ -840,7 +842,7 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst, ...@@ -840,7 +842,7 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst,
len = strnlen(filename, PATH_MAX); len = strnlen(filename, PATH_MAX);
} }
*pinum = pFindData->UniqueId; *pinum = le64_to_cpu(pFindData->UniqueId);
} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) { } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
FILE_DIRECTORY_INFO *pFindData = FILE_DIRECTORY_INFO *pFindData =
(FILE_DIRECTORY_INFO *)current_entry; (FILE_DIRECTORY_INFO *)current_entry;
...@@ -856,7 +858,7 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst, ...@@ -856,7 +858,7 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst,
(SEARCH_ID_FULL_DIR_INFO *)current_entry; (SEARCH_ID_FULL_DIR_INFO *)current_entry;
filename = &pFindData->FileName[0]; filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength); len = le32_to_cpu(pFindData->FileNameLength);
*pinum = pFindData->UniqueId; *pinum = le64_to_cpu(pFindData->UniqueId);
} else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
FILE_BOTH_DIRECTORY_INFO *pFindData = FILE_BOTH_DIRECTORY_INFO *pFindData =
(FILE_BOTH_DIRECTORY_INFO *)current_entry; (FILE_BOTH_DIRECTORY_INFO *)current_entry;
......
...@@ -285,35 +285,36 @@ static int decode_unicode_ssetup(char **pbcc_area, int bleft, ...@@ -285,35 +285,36 @@ static int decode_unicode_ssetup(char **pbcc_area, int bleft,
int words_left, len; int words_left, len;
char *data = *pbcc_area; char *data = *pbcc_area;
cFYI(1, ("bleft %d", bleft)); cFYI(1, ("bleft %d", bleft));
/*
/* SMB header is unaligned, so cifs servers word align start of * Windows servers do not always double null terminate their final
Unicode strings */ * Unicode string. Check to see if there are an uneven number of bytes
data++; * left. If so, then add an extra NULL pad byte to the end of the
bleft--; /* Windows servers do not always double null terminate * response.
their final Unicode string - in which case we *
now will not attempt to decode the byte of junk * See section 2.7.2 in "Implementing CIFS" for details
which follows it */ */
if (bleft % 2) {
data[bleft] = 0;
++bleft;
}
words_left = bleft / 2; words_left = bleft / 2;
/* save off server operating system */ /* save off server operating system */
len = UniStrnlen((wchar_t *) data, words_left); len = UniStrnlen((wchar_t *) data, words_left);
/* We look for obvious messed up bcc or strings in response so we do not go off
the end since (at least) WIN2K and Windows XP have a major bug in not null
terminating last Unicode string in response */
if (len >= words_left) if (len >= words_left)
return rc; return rc;
kfree(ses->serverOS); kfree(ses->serverOS);
/* UTF-8 string will not grow more than four times as big as UCS-16 */ /* UTF-8 string will not grow more than four times as big as UCS-16 */
ses->serverOS = kzalloc((4 * len) + 2 /* trailing null */, GFP_KERNEL); ses->serverOS = kzalloc((4 * len) + 2 /* trailing null */, GFP_KERNEL);
if (ses->serverOS != NULL) if (ses->serverOS != NULL) {
cifs_strfromUCS_le(ses->serverOS, (__le16 *)data, len, nls_cp); cifs_strfromUCS_le(ses->serverOS, (__le16 *)data, len, nls_cp);
cFYI(1, ("serverOS=%s", ses->serverOS));
}
data += 2 * (len + 1); data += 2 * (len + 1);
words_left -= len + 1; words_left -= len + 1;
...@@ -328,6 +329,7 @@ static int decode_unicode_ssetup(char **pbcc_area, int bleft, ...@@ -328,6 +329,7 @@ static int decode_unicode_ssetup(char **pbcc_area, int bleft,
if (ses->serverNOS != NULL) { if (ses->serverNOS != NULL) {
cifs_strfromUCS_le(ses->serverNOS, (__le16 *)data, len, cifs_strfromUCS_le(ses->serverNOS, (__le16 *)data, len,
nls_cp); nls_cp);
cFYI(1, ("serverNOS=%s", ses->serverNOS));
if (strncmp(ses->serverNOS, "NT LAN Manager 4", 16) == 0) { if (strncmp(ses->serverNOS, "NT LAN Manager 4", 16) == 0) {
cFYI(1, ("NT4 server")); cFYI(1, ("NT4 server"));
ses->flags |= CIFS_SES_NT4; ses->flags |= CIFS_SES_NT4;
...@@ -343,12 +345,11 @@ static int decode_unicode_ssetup(char **pbcc_area, int bleft, ...@@ -343,12 +345,11 @@ static int decode_unicode_ssetup(char **pbcc_area, int bleft,
return rc; return rc;
kfree(ses->serverDomain); kfree(ses->serverDomain);
ses->serverDomain = kzalloc(2 * (len + 1), GFP_KERNEL); /* BB FIXME wrong length */ ses->serverDomain = kzalloc((4 * len) + 2, GFP_KERNEL);
if (ses->serverDomain != NULL) { if (ses->serverDomain != NULL) {
cifs_strfromUCS_le(ses->serverDomain, (__le16 *)data, len, cifs_strfromUCS_le(ses->serverDomain, (__le16 *)data, len,
nls_cp); nls_cp);
ses->serverDomain[2*len] = 0; cFYI(1, ("serverDomain=%s", ses->serverDomain));
ses->serverDomain[(2*len) + 1] = 0;
} }
data += 2 * (len + 1); data += 2 * (len + 1);
words_left -= len + 1; words_left -= len + 1;
...@@ -702,12 +703,18 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, ...@@ -702,12 +703,18 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
} }
/* BB check if Unicode and decode strings */ /* BB check if Unicode and decode strings */
if (smb_buf->Flags2 & SMBFLG2_UNICODE) if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
/* unicode string area must be word-aligned */
if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) {
++bcc_ptr;
--bytes_remaining;
}
rc = decode_unicode_ssetup(&bcc_ptr, bytes_remaining, rc = decode_unicode_ssetup(&bcc_ptr, bytes_remaining,
ses, nls_cp); ses, nls_cp);
else } else {
rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining, rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining,
ses, nls_cp); ses, nls_cp);
}
ssetup_exit: ssetup_exit:
if (spnego_key) { if (spnego_key) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment