Commit 86ed5a93 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] Check that last search entry resume key is valid
  [CIFS] make sure we have the right resume info before calling CIFSFindNext
  [CIFS]  clean up error handling in cifs_unlink
  [CIFS] fix some settings of cifsAttrs after calling SetFileInfo and SetPathInfo
  cifs: explicitly revoke SPNEGO key after session setup
  cifs: Convert cifs to new aops.
  [CIFS] update DOS attributes in cifsInode if we successfully changed them
  cifs: remove NULL termination from rename target in CIFSSMBRenameOpenFIle
  cifs: work around samba returning -ENOENT on SetFileDisposition call
  cifs: fix inverted NULL check after kmalloc
  [CIFS] clean up upcall handling for dns_resolver keys
  [CIFS]  fix busy-file renames and refactor cifs_rename logic
  cifs: add function to set file disposition
  [CIFS] add constants for string lengths of keynames in SPNEGO upcall string
  cifs: move rename and delete-on-close logic into helper function
  cifs: have find_writeable_file prefer filehandles opened by same task
  cifs: don't use GFP_KERNEL with GFP_NOFS
  [CIFS] use common code for turning off ATTR_READONLY in cifs_unlink
  cifs: clean up variables in cifs_unlink
parents 835a1c09 b77d753c
...@@ -66,11 +66,28 @@ struct key_type cifs_spnego_key_type = { ...@@ -66,11 +66,28 @@ struct key_type cifs_spnego_key_type = {
.describe = user_describe, .describe = user_describe,
}; };
#define MAX_VER_STR_LEN 8 /* length of longest version string e.g. /* length of longest version string e.g. strlen("ver=0xFF") */
strlen("ver=0xFF") */ #define MAX_VER_STR_LEN 8
#define MAX_MECH_STR_LEN 13 /* length of longest security mechanism name, eg
in future could have strlen(";sec=ntlmsspi") */ /* length of longest security mechanism name, eg in future could have
#define MAX_IPV6_ADDR_LEN 42 /* eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/60 */ * strlen(";sec=ntlmsspi") */
#define MAX_MECH_STR_LEN 13
/* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/60 */
#define MAX_IPV6_ADDR_LEN 42
/* strlen of "host=" */
#define HOST_KEY_LEN 5
/* strlen of ";ip4=" or ";ip6=" */
#define IP_KEY_LEN 5
/* strlen of ";uid=0x" */
#define UID_KEY_LEN 7
/* strlen of ";user=" */
#define USER_KEY_LEN 6
/* get a key struct with a SPNEGO security blob, suitable for session setup */ /* get a key struct with a SPNEGO security blob, suitable for session setup */
struct key * struct key *
cifs_get_spnego_key(struct cifsSesInfo *sesInfo) cifs_get_spnego_key(struct cifsSesInfo *sesInfo)
...@@ -84,11 +101,11 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo) ...@@ -84,11 +101,11 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo)
/* length of fields (with semicolons): ver=0xyz ip4=ipaddress /* length of fields (with semicolons): ver=0xyz ip4=ipaddress
host=hostname sec=mechanism uid=0xFF user=username */ host=hostname sec=mechanism uid=0xFF user=username */
desc_len = MAX_VER_STR_LEN + desc_len = MAX_VER_STR_LEN +
6 /* len of "host=" */ + strlen(hostname) + HOST_KEY_LEN + strlen(hostname) +
5 /* len of ";ipv4=" */ + MAX_IPV6_ADDR_LEN + IP_KEY_LEN + MAX_IPV6_ADDR_LEN +
MAX_MECH_STR_LEN + MAX_MECH_STR_LEN +
7 /* len of ";uid=0x" */ + (sizeof(uid_t) * 2) + UID_KEY_LEN + (sizeof(uid_t) * 2) +
6 /* len of ";user=" */ + strlen(sesInfo->userName) + 1; USER_KEY_LEN + strlen(sesInfo->userName) + 1;
spnego_key = ERR_PTR(-ENOMEM); spnego_key = ERR_PTR(-ENOMEM);
description = kzalloc(desc_len, GFP_KERNEL); description = kzalloc(desc_len, GFP_KERNEL);
......
...@@ -41,7 +41,7 @@ extern int cifs_create(struct inode *, struct dentry *, int, ...@@ -41,7 +41,7 @@ extern int cifs_create(struct inode *, struct dentry *, int,
struct nameidata *); struct nameidata *);
extern struct dentry *cifs_lookup(struct inode *, struct dentry *, extern struct dentry *cifs_lookup(struct inode *, struct dentry *,
struct nameidata *); struct nameidata *);
extern int cifs_unlink(struct inode *, struct dentry *); extern int cifs_unlink(struct inode *dir, struct dentry *dentry);
extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *); extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *);
extern int cifs_mknod(struct inode *, struct dentry *, int, dev_t); extern int cifs_mknod(struct inode *, struct dentry *, int, dev_t);
extern int cifs_mkdir(struct inode *, struct dentry *, int); extern int cifs_mkdir(struct inode *, struct dentry *, int);
......
...@@ -309,6 +309,7 @@ struct cifs_search_info { ...@@ -309,6 +309,7 @@ struct cifs_search_info {
__u32 resume_key; __u32 resume_key;
char *ntwrk_buf_start; char *ntwrk_buf_start;
char *srch_entries_start; char *srch_entries_start;
char *last_entry;
char *presume_name; char *presume_name;
unsigned int resume_name_len; unsigned int resume_name_len;
bool endOfSearch:1; bool endOfSearch:1;
......
...@@ -179,6 +179,8 @@ extern int CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon, ...@@ -179,6 +179,8 @@ extern int CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
extern int CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon, extern int CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
const FILE_BASIC_INFO *data, __u16 fid, const FILE_BASIC_INFO *data, __u16 fid,
__u32 pid_of_opener); __u32 pid_of_opener);
extern int CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
bool delete_file, __u16 fid, __u32 pid_of_opener);
#if 0 #if 0
extern int CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, extern int CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon,
char *fileName, __u16 dos_attributes, char *fileName, __u16 dos_attributes,
...@@ -229,7 +231,7 @@ extern int CIFSSMBRename(const int xid, struct cifsTconInfo *tcon, ...@@ -229,7 +231,7 @@ extern int CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
const struct nls_table *nls_codepage, const struct nls_table *nls_codepage,
int remap_special_chars); int remap_special_chars);
extern int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon, extern int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
int netfid, char *target_name, int netfid, const char *target_name,
const struct nls_table *nls_codepage, const struct nls_table *nls_codepage,
int remap_special_chars); int remap_special_chars);
extern int CIFSCreateHardLink(const int xid, extern int CIFSCreateHardLink(const int xid,
......
...@@ -2017,7 +2017,7 @@ CIFSSMBRename(const int xid, struct cifsTconInfo *tcon, ...@@ -2017,7 +2017,7 @@ CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
} }
int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon, int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
int netfid, char *target_name, int netfid, const char *target_name,
const struct nls_table *nls_codepage, int remap) const struct nls_table *nls_codepage, int remap)
{ {
struct smb_com_transaction2_sfi_req *pSMB = NULL; struct smb_com_transaction2_sfi_req *pSMB = NULL;
...@@ -2071,7 +2071,7 @@ int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon, ...@@ -2071,7 +2071,7 @@ int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
remap); remap);
} }
rename_info->target_name_len = cpu_to_le32(2 * len_of_str); rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str) + 2; count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
byte_count += count; byte_count += count;
pSMB->DataCount = cpu_to_le16(count); pSMB->DataCount = cpu_to_le16(count);
pSMB->TotalDataCount = pSMB->DataCount; pSMB->TotalDataCount = pSMB->DataCount;
...@@ -3614,6 +3614,8 @@ CIFSFindFirst(const int xid, struct cifsTconInfo *tcon, ...@@ -3614,6 +3614,8 @@ CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
/* BB remember to free buffer if error BB */ /* BB remember to free buffer if error BB */
rc = validate_t2((struct smb_t2_rsp *)pSMBr); rc = validate_t2((struct smb_t2_rsp *)pSMBr);
if (rc == 0) { if (rc == 0) {
unsigned int lnoff;
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
psrch_inf->unicode = true; psrch_inf->unicode = true;
else else
...@@ -3636,6 +3638,17 @@ CIFSFindFirst(const int xid, struct cifsTconInfo *tcon, ...@@ -3636,6 +3638,17 @@ CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
le16_to_cpu(parms->SearchCount); le16_to_cpu(parms->SearchCount);
psrch_inf->index_of_last_entry = 2 /* skip . and .. */ + psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
psrch_inf->entries_in_buffer; psrch_inf->entries_in_buffer;
lnoff = le16_to_cpu(parms->LastNameOffset);
if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
lnoff) {
cERROR(1, ("ignoring corrupt resume name"));
psrch_inf->last_entry = NULL;
return rc;
}
psrch_inf->last_entry = psrch_inf->srch_entries_start +
lnoff;
*pnetfid = parms->SearchHandle; *pnetfid = parms->SearchHandle;
} else { } else {
cifs_buf_release(pSMB); cifs_buf_release(pSMB);
...@@ -3725,6 +3738,8 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, ...@@ -3725,6 +3738,8 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
rc = validate_t2((struct smb_t2_rsp *)pSMBr); rc = validate_t2((struct smb_t2_rsp *)pSMBr);
if (rc == 0) { if (rc == 0) {
unsigned int lnoff;
/* BB fixme add lock for file (srch_info) struct here */ /* BB fixme add lock for file (srch_info) struct here */
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
psrch_inf->unicode = true; psrch_inf->unicode = true;
...@@ -3751,6 +3766,16 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, ...@@ -3751,6 +3766,16 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
le16_to_cpu(parms->SearchCount); le16_to_cpu(parms->SearchCount);
psrch_inf->index_of_last_entry += psrch_inf->index_of_last_entry +=
psrch_inf->entries_in_buffer; psrch_inf->entries_in_buffer;
lnoff = le16_to_cpu(parms->LastNameOffset);
if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
lnoff) {
cERROR(1, ("ignoring corrupt resume name"));
psrch_inf->last_entry = NULL;
return rc;
} else
psrch_inf->last_entry =
psrch_inf->srch_entries_start + lnoff;
/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d", /* cFYI(1,("fnxt2 entries in buf %d index_of_last %d",
psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */ psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */
...@@ -4876,6 +4901,61 @@ CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon, ...@@ -4876,6 +4901,61 @@ CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
return rc; return rc;
} }
int
CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
bool delete_file, __u16 fid, __u32 pid_of_opener)
{
struct smb_com_transaction2_sfi_req *pSMB = NULL;
char *data_offset;
int rc = 0;
__u16 params, param_offset, offset, byte_count, count;
cFYI(1, ("Set File Disposition (via SetFileInfo)"));
rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
if (rc)
return rc;
pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
params = 6;
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
pSMB->Flags = 0;
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
offset = param_offset + params;
data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
count = 1;
pSMB->MaxParameterCount = cpu_to_le16(2);
/* BB find max SMB PDU from sess */
pSMB->MaxDataCount = cpu_to_le16(1000);
pSMB->SetupCount = 1;
pSMB->Reserved3 = 0;
pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
byte_count = 3 /* pad */ + params + count;
pSMB->DataCount = cpu_to_le16(count);
pSMB->ParameterCount = cpu_to_le16(params);
pSMB->TotalDataCount = pSMB->DataCount;
pSMB->TotalParameterCount = pSMB->ParameterCount;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
pSMB->DataOffset = cpu_to_le16(offset);
pSMB->Fid = fid;
pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
pSMB->Reserved4 = 0;
pSMB->hdr.smb_buf_length += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
*data_offset = delete_file ? 1 : 0;
rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
if (rc)
cFYI(1, ("Send error in SetFileDisposition = %d", rc));
return rc;
}
int int
CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon, CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
......
...@@ -29,19 +29,55 @@ ...@@ -29,19 +29,55 @@
#include "cifsproto.h" #include "cifsproto.h"
#include "cifs_debug.h" #include "cifs_debug.h"
static int dns_resolver_instantiate(struct key *key, const void *data, /* Checks if supplied name is IP address
* returns:
* 1 - name is IP
* 0 - name is not IP
*/
static int
is_ip(const char *name)
{
int rc;
struct sockaddr_in sin_server;
struct sockaddr_in6 sin_server6;
rc = cifs_inet_pton(AF_INET, name,
&sin_server.sin_addr.s_addr);
if (rc <= 0) {
/* not ipv4 address, try ipv6 */
rc = cifs_inet_pton(AF_INET6, name,
&sin_server6.sin6_addr.in6_u);
if (rc > 0)
return 1;
} else {
return 1;
}
/* we failed translating address */
return 0;
}
static int
dns_resolver_instantiate(struct key *key, const void *data,
size_t datalen) size_t datalen)
{ {
int rc = 0; int rc = 0;
char *ip; char *ip;
ip = kmalloc(datalen+1, GFP_KERNEL); ip = kmalloc(datalen + 1, GFP_KERNEL);
if (!ip) if (!ip)
return -ENOMEM; return -ENOMEM;
memcpy(ip, data, datalen); memcpy(ip, data, datalen);
ip[datalen] = '\0'; ip[datalen] = '\0';
/* make sure this looks like an address */
if (!is_ip((const char *) ip)) {
kfree(ip);
return -EINVAL;
}
key->type_data.x[0] = datalen;
rcu_assign_pointer(key->payload.data, ip); rcu_assign_pointer(key->payload.data, ip);
return rc; return rc;
...@@ -62,33 +98,6 @@ struct key_type key_type_dns_resolver = { ...@@ -62,33 +98,6 @@ struct key_type key_type_dns_resolver = {
.match = user_match, .match = user_match,
}; };
/* Checks if supplied name is IP address
* returns:
* 1 - name is IP
* 0 - name is not IP
*/
static int is_ip(const char *name)
{
int rc;
struct sockaddr_in sin_server;
struct sockaddr_in6 sin_server6;
rc = cifs_inet_pton(AF_INET, name,
&sin_server.sin_addr.s_addr);
if (rc <= 0) {
/* not ipv4 address, try ipv6 */
rc = cifs_inet_pton(AF_INET6, name,
&sin_server6.sin6_addr.in6_u);
if (rc > 0)
return 1;
} else {
return 1;
}
/* we failed translating address */
return 0;
}
/* Resolves server name to ip address. /* Resolves server name to ip address.
* input: * input:
* unc - server UNC * unc - server UNC
...@@ -140,6 +149,7 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) ...@@ -140,6 +149,7 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
rkey = request_key(&key_type_dns_resolver, name, ""); rkey = request_key(&key_type_dns_resolver, name, "");
if (!IS_ERR(rkey)) { if (!IS_ERR(rkey)) {
len = rkey->type_data.x[0];
data = rkey->payload.data; data = rkey->payload.data;
} else { } else {
cERROR(1, ("%s: unable to resolve: %s", __func__, name)); cERROR(1, ("%s: unable to resolve: %s", __func__, name));
...@@ -148,11 +158,9 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) ...@@ -148,11 +158,9 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
skip_upcall: skip_upcall:
if (data) { if (data) {
len = strlen(data); *ip_addr = kmalloc(len + 1, GFP_KERNEL);
*ip_addr = kmalloc(len+1, GFP_KERNEL);
if (*ip_addr) { if (*ip_addr) {
memcpy(*ip_addr, data, len); memcpy(*ip_addr, data, len + 1);
(*ip_addr)[len] = '\0';
if (!IS_ERR(rkey)) if (!IS_ERR(rkey))
cFYI(1, ("%s: resolved: %s to %s", __func__, cFYI(1, ("%s: resolved: %s to %s", __func__,
name, name,
......
...@@ -107,7 +107,7 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, ...@@ -107,7 +107,7 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
/* want handles we can use to read with first /* want handles we can use to read with first
in the list so we do not have to walk the in the list so we do not have to walk the
list to search for one in prepare_write */ list to search for one in write_begin */
if ((file->f_flags & O_ACCMODE) == O_WRONLY) { if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
list_add_tail(&pCifsFile->flist, list_add_tail(&pCifsFile->flist,
&pCifsInode->openFileList); &pCifsInode->openFileList);
...@@ -915,7 +915,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, ...@@ -915,7 +915,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
} }
static ssize_t cifs_write(struct file *file, const char *write_data, static ssize_t cifs_write(struct file *file, const char *write_data,
size_t write_size, loff_t *poffset) size_t write_size, loff_t *poffset)
{ {
int rc = 0; int rc = 0;
unsigned int bytes_written = 0; unsigned int bytes_written = 0;
...@@ -1065,6 +1065,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode) ...@@ -1065,6 +1065,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
{ {
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
bool any_available = false;
int rc; int rc;
/* Having a null inode here (because mapping->host was set to zero by /* Having a null inode here (because mapping->host was set to zero by
...@@ -1080,8 +1081,10 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) ...@@ -1080,8 +1081,10 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
read_lock(&GlobalSMBSeslock); read_lock(&GlobalSMBSeslock);
refind_writable: refind_writable:
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
if (open_file->closePend) if (open_file->closePend ||
(!any_available && open_file->pid != current->tgid))
continue; continue;
if (open_file->pfile && if (open_file->pfile &&
((open_file->pfile->f_flags & O_RDWR) || ((open_file->pfile->f_flags & O_RDWR) ||
(open_file->pfile->f_flags & O_WRONLY))) { (open_file->pfile->f_flags & O_WRONLY))) {
...@@ -1131,6 +1134,11 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) ...@@ -1131,6 +1134,11 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
of the loop here. */ of the loop here. */
} }
} }
/* couldn't find useable FH with same pid, try any available */
if (!any_available) {
any_available = true;
goto refind_writable;
}
read_unlock(&GlobalSMBSeslock); read_unlock(&GlobalSMBSeslock);
return NULL; return NULL;
} }
...@@ -1447,49 +1455,52 @@ static int cifs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -1447,49 +1455,52 @@ static int cifs_writepage(struct page *page, struct writeback_control *wbc)
return rc; return rc;
} }
static int cifs_commit_write(struct file *file, struct page *page, static int cifs_write_end(struct file *file, struct address_space *mapping,
unsigned offset, unsigned to) loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata)
{ {
int xid; int rc;
int rc = 0; struct inode *inode = mapping->host;
struct inode *inode = page->mapping->host;
loff_t position = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
char *page_data;
xid = GetXid(); cFYI(1, ("write_end for page %p from pos %lld with %d bytes",
cFYI(1, ("commit write for page %p up to position %lld for %d", page, pos, copied));
page, position, to));
spin_lock(&inode->i_lock); if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE)
if (position > inode->i_size) SetPageUptodate(page);
i_size_write(inode, position);
spin_unlock(&inode->i_lock);
if (!PageUptodate(page)) { if (!PageUptodate(page)) {
position = ((loff_t)page->index << PAGE_CACHE_SHIFT) + offset; char *page_data;
/* can not rely on (or let) writepage write this data */ unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
if (to < offset) { int xid;
cFYI(1, ("Illegal offsets, can not copy from %d to %d",
offset, to)); xid = GetXid();
FreeXid(xid);
return rc;
}
/* this is probably better than directly calling /* this is probably better than directly calling
partialpage_write since in this function the file handle is partialpage_write since in this function the file handle is
known which we might as well leverage */ known which we might as well leverage */
/* BB check if anything else missing out of ppw /* BB check if anything else missing out of ppw
such as updating last write time */ such as updating last write time */
page_data = kmap(page); page_data = kmap(page);
rc = cifs_write(file, page_data + offset, to-offset, rc = cifs_write(file, page_data + offset, copied, &pos);
&position); /* if (rc < 0) should we set writebehind rc? */
if (rc > 0)
rc = 0;
/* else if (rc < 0) should we set writebehind rc? */
kunmap(page); kunmap(page);
FreeXid(xid);
} else { } else {
rc = copied;
pos += copied;
set_page_dirty(page); set_page_dirty(page);
} }
FreeXid(xid); if (rc > 0) {
spin_lock(&inode->i_lock);
if (pos > inode->i_size)
i_size_write(inode, pos);
spin_unlock(&inode->i_lock);
}
unlock_page(page);
page_cache_release(page);
return rc; return rc;
} }
...@@ -2035,49 +2046,44 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file) ...@@ -2035,49 +2046,44 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file)
return true; return true;
} }
static int cifs_prepare_write(struct file *file, struct page *page, static int cifs_write_begin(struct file *file, struct address_space *mapping,
unsigned from, unsigned to) loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
{ {
int rc = 0; pgoff_t index = pos >> PAGE_CACHE_SHIFT;
loff_t i_size; loff_t offset = pos & (PAGE_CACHE_SIZE - 1);
loff_t offset;
cFYI(1, ("prepare write for page %p from %d to %d", page, from, to)); cFYI(1, ("write_begin from %lld len %d", (long long)pos, len));
if (PageUptodate(page))
*pagep = __grab_cache_page(mapping, index);
if (!*pagep)
return -ENOMEM;
if (PageUptodate(*pagep))
return 0; return 0;
/* If we are writing a full page it will be up to date, /* If we are writing a full page it will be up to date,
no need to read from the server */ no need to read from the server */
if ((to == PAGE_CACHE_SIZE) && (from == 0)) { if (len == PAGE_CACHE_SIZE && flags & AOP_FLAG_UNINTERRUPTIBLE)
SetPageUptodate(page);
return 0; return 0;
}
offset = (loff_t)page->index << PAGE_CACHE_SHIFT; if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
i_size = i_size_read(page->mapping->host); int rc;
if ((offset >= i_size) ||
((from == 0) && (offset + to) >= i_size)) {
/*
* We don't need to read data beyond the end of the file.
* zero it, and set the page uptodate
*/
simple_prepare_write(file, page, from, to);
SetPageUptodate(page);
} else if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
/* might as well read a page, it is fast enough */ /* might as well read a page, it is fast enough */
rc = cifs_readpage_worker(file, page, &offset); rc = cifs_readpage_worker(file, *pagep, &offset);
/* we do not need to pass errors back
e.g. if we do not have read access to the file
because cifs_write_end will attempt synchronous writes
-- shaggy */
} else { } else {
/* we could try using another file handle if there is one - /* we could try using another file handle if there is one -
but how would we lock it to prevent close of that handle but how would we lock it to prevent close of that handle
racing with this read? In any case racing with this read? In any case
this will be written out by commit_write so is fine */ this will be written out by write_end so is fine */
} }
/* we do not need to pass errors back
e.g. if we do not have read access to the file
because cifs_commit_write will do the right thing. -- shaggy */
return 0; return 0;
} }
...@@ -2086,8 +2092,8 @@ const struct address_space_operations cifs_addr_ops = { ...@@ -2086,8 +2092,8 @@ const struct address_space_operations cifs_addr_ops = {
.readpages = cifs_readpages, .readpages = cifs_readpages,
.writepage = cifs_writepage, .writepage = cifs_writepage,
.writepages = cifs_writepages, .writepages = cifs_writepages,
.prepare_write = cifs_prepare_write, .write_begin = cifs_write_begin,
.commit_write = cifs_commit_write, .write_end = cifs_write_end,
.set_page_dirty = __set_page_dirty_nobuffers, .set_page_dirty = __set_page_dirty_nobuffers,
/* .sync_page = cifs_sync_page, */ /* .sync_page = cifs_sync_page, */
/* .direct_IO = */ /* .direct_IO = */
...@@ -2102,8 +2108,8 @@ const struct address_space_operations cifs_addr_ops_smallbuf = { ...@@ -2102,8 +2108,8 @@ const struct address_space_operations cifs_addr_ops_smallbuf = {
.readpage = cifs_readpage, .readpage = cifs_readpage,
.writepage = cifs_writepage, .writepage = cifs_writepage,
.writepages = cifs_writepages, .writepages = cifs_writepages,
.prepare_write = cifs_prepare_write, .write_begin = cifs_write_begin,
.commit_write = cifs_commit_write, .write_end = cifs_write_end,
.set_page_dirty = __set_page_dirty_nobuffers, .set_page_dirty = __set_page_dirty_nobuffers,
/* .sync_page = cifs_sync_page, */ /* .sync_page = cifs_sync_page, */
/* .direct_IO = */ /* .direct_IO = */
......
...@@ -665,40 +665,201 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino) ...@@ -665,40 +665,201 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
return inode; return inode;
} }
int cifs_unlink(struct inode *inode, struct dentry *direntry) static int
cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid,
char *full_path, __u32 dosattr)
{
int rc;
int oplock = 0;
__u16 netfid;
__u32 netpid;
bool set_time = false;
struct cifsFileInfo *open_file;
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsTconInfo *pTcon = cifs_sb->tcon;
FILE_BASIC_INFO info_buf;
if (attrs->ia_valid & ATTR_ATIME) {
set_time = true;
info_buf.LastAccessTime =
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime));
} else
info_buf.LastAccessTime = 0;
if (attrs->ia_valid & ATTR_MTIME) {
set_time = true;
info_buf.LastWriteTime =
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime));
} else
info_buf.LastWriteTime = 0;
/*
* Samba throws this field away, but windows may actually use it.
* Do not set ctime unless other time stamps are changed explicitly
* (i.e. by utimes()) since we would then have a mix of client and
* server times.
*/
if (set_time && (attrs->ia_valid & ATTR_CTIME)) {
cFYI(1, ("CIFS - CTIME changed"));
info_buf.ChangeTime =
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
} else
info_buf.ChangeTime = 0;
info_buf.CreationTime = 0; /* don't change */
info_buf.Attributes = cpu_to_le32(dosattr);
/*
* If the file is already open for write, just use that fileid
*/
open_file = find_writable_file(cifsInode);
if (open_file) {
netfid = open_file->netfid;
netpid = open_file->pid;
goto set_via_filehandle;
}
/*
* NT4 apparently returns success on this call, but it doesn't
* really work.
*/
if (!(pTcon->ses->flags & CIFS_SES_NT4)) {
rc = CIFSSMBSetPathInfo(xid, pTcon, full_path,
&info_buf, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc == 0) {
cifsInode->cifsAttrs = dosattr;
goto out;
} else if (rc != -EOPNOTSUPP && rc != -EINVAL)
goto out;
}
cFYI(1, ("calling SetFileInfo since SetPathInfo for "
"times not supported by this server"));
rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN,
SYNCHRONIZE | 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) {
if (rc == -EIO)
rc = -EINVAL;
goto out;
}
netpid = current->tgid;
set_via_filehandle:
rc = CIFSSMBSetFileInfo(xid, pTcon, &info_buf, netfid, netpid);
if (!rc)
cifsInode->cifsAttrs = dosattr;
if (open_file == NULL)
CIFSSMBClose(xid, pTcon, netfid);
else
atomic_dec(&open_file->wrtPending);
out:
return rc;
}
/*
* open the given file (if it isn't already), set the DELETE_ON_CLOSE bit
* and rename it to a random name that hopefully won't conflict with
* anything else.
*/
static int
cifs_rename_pending_delete(char *full_path, struct inode *inode, int xid)
{
int oplock = 0;
int rc;
__u16 netfid;
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;
rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
DELETE|FILE_WRITE_ATTRIBUTES,
CREATE_NOT_DIR|CREATE_DELETE_ON_CLOSE,
&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;
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;
}
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,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
/* 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;
out_close:
CIFSSMBClose(xid, tcon, netfid);
out:
return rc;
}
int cifs_unlink(struct inode *dir, struct dentry *dentry)
{ {
int rc = 0; int rc = 0;
int xid; int xid;
struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon;
char *full_path = NULL; char *full_path = NULL;
struct cifsInodeInfo *cifsInode; struct inode *inode = dentry->d_inode;
FILE_BASIC_INFO *pinfo_buf; struct cifsInodeInfo *cifsInode = CIFS_I(inode);
struct super_block *sb = dir->i_sb;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifsTconInfo *tcon = cifs_sb->tcon;
struct iattr *attrs = NULL;
__u32 dosattr = 0, origattr = 0;
cFYI(1, ("cifs_unlink, inode = 0x%p", inode)); cFYI(1, ("cifs_unlink, dir=0x%p, dentry=0x%p", dir, dentry));
xid = GetXid(); xid = GetXid();
if (inode) /* Unlink can be called from rename so we can not take the
cifs_sb = CIFS_SB(inode->i_sb); * sb->s_vfs_rename_mutex here */
else full_path = build_path_from_dentry(dentry);
cifs_sb = CIFS_SB(direntry->d_sb);
pTcon = cifs_sb->tcon;
/* Unlink can be called from rename so we can not grab the sem here
since we deadlock otherwise */
/* mutex_lock(&direntry->d_sb->s_vfs_rename_mutex);*/
full_path = build_path_from_dentry(direntry);
/* mutex_unlock(&direntry->d_sb->s_vfs_rename_mutex);*/
if (full_path == NULL) { if (full_path == NULL) {
FreeXid(xid); FreeXid(xid);
return -ENOMEM; return -ENOMEM;
} }
if ((pTcon->ses->capabilities & CAP_UNIX) && if ((tcon->ses->capabilities & CAP_UNIX) &&
(CIFS_UNIX_POSIX_PATH_OPS_CAP & (CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(pTcon->fsUnixInfo.Capability))) { le64_to_cpu(tcon->fsUnixInfo.Capability))) {
rc = CIFSPOSIXDelFile(xid, pTcon, full_path, rc = CIFSPOSIXDelFile(xid, tcon, full_path,
SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls, SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
cFYI(1, ("posix del rc %d", rc)); cFYI(1, ("posix del rc %d", rc));
...@@ -706,125 +867,60 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry) ...@@ -706,125 +867,60 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry)
goto psx_del_no_retry; goto psx_del_no_retry;
} }
rc = CIFSSMBDelFile(xid, pTcon, full_path, cifs_sb->local_nls, retry_std_delete:
rc = CIFSSMBDelFile(xid, tcon, full_path, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
psx_del_no_retry: psx_del_no_retry:
if (!rc) { if (!rc) {
if (direntry->d_inode) if (inode)
drop_nlink(direntry->d_inode); drop_nlink(inode);
} else if (rc == -ENOENT) { } else if (rc == -ENOENT) {
d_drop(direntry); d_drop(dentry);
} else if (rc == -ETXTBSY) { } else if (rc == -ETXTBSY) {
int oplock = 0; rc = cifs_rename_pending_delete(full_path, inode, xid);
__u16 netfid; if (rc == 0)
drop_nlink(inode);
rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, DELETE, } else if (rc == -EACCES && dosattr == 0) {
CREATE_NOT_DIR | CREATE_DELETE_ON_CLOSE, attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
&netfid, &oplock, NULL, cifs_sb->local_nls, if (attrs == NULL) {
cifs_sb->mnt_cifs_flags & rc = -ENOMEM;
CIFS_MOUNT_MAP_SPECIAL_CHR); goto out_reval;
if (rc == 0) {
CIFSSMBRenameOpenFile(xid, pTcon, netfid, NULL,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
CIFSSMBClose(xid, pTcon, netfid);
if (direntry->d_inode)
drop_nlink(direntry->d_inode);
} }
} else if (rc == -EACCES) {
/* try only if r/o attribute set in local lookup data? */
pinfo_buf = kzalloc(sizeof(FILE_BASIC_INFO), GFP_KERNEL);
if (pinfo_buf) {
/* ATTRS set to normal clears r/o bit */
pinfo_buf->Attributes = cpu_to_le32(ATTR_NORMAL);
if (!(pTcon->ses->flags & CIFS_SES_NT4))
rc = CIFSSMBSetPathInfo(xid, pTcon, full_path,
pinfo_buf,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
else
rc = -EOPNOTSUPP;
if (rc == -EOPNOTSUPP) { /* try to reset dos attributes */
int oplock = 0; origattr = cifsInode->cifsAttrs;
__u16 netfid; if (origattr == 0)
/* rc = CIFSSMBSetAttrLegacy(xid, pTcon, origattr |= ATTR_NORMAL;
full_path, dosattr = origattr & ~ATTR_READONLY;
(__u16)ATTR_NORMAL, if (dosattr == 0)
cifs_sb->local_nls); dosattr |= ATTR_NORMAL;
For some strange reason it seems that NT4 eats the dosattr |= ATTR_HIDDEN;
old setattr call without actually setting the
attributes so on to the third attempted workaround rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr);
*/ if (rc != 0)
goto out_reval;
/* BB could scan to see if we already have it open
and pass in pid of opener to function */ goto retry_std_delete;
rc = CIFSSMBOpen(xid, pTcon, full_path,
FILE_OPEN, SYNCHRONIZE |
FILE_WRITE_ATTRIBUTES, 0,
&netfid, &oplock, NULL,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc == 0) {
rc = CIFSSMBSetFileInfo(xid, pTcon,
pinfo_buf,
netfid,
current->tgid);
CIFSSMBClose(xid, pTcon, netfid);
}
}
kfree(pinfo_buf);
}
if (rc == 0) {
rc = CIFSSMBDelFile(xid, pTcon, full_path,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (!rc) {
if (direntry->d_inode)
drop_nlink(direntry->d_inode);
} else if (rc == -ETXTBSY) {
int oplock = 0;
__u16 netfid;
rc = CIFSSMBOpen(xid, pTcon, full_path,
FILE_OPEN, DELETE,
CREATE_NOT_DIR |
CREATE_DELETE_ON_CLOSE,
&netfid, &oplock, NULL,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc == 0) {
CIFSSMBRenameOpenFile(xid, pTcon,
netfid, NULL,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
CIFSSMBClose(xid, pTcon, netfid);
if (direntry->d_inode)
drop_nlink(direntry->d_inode);
}
/* BB if rc = -ETXTBUSY goto the rename logic BB */
}
}
}
if (direntry->d_inode) {
cifsInode = CIFS_I(direntry->d_inode);
cifsInode->time = 0; /* will force revalidate to get info
when needed */
direntry->d_inode->i_ctime = current_fs_time(inode->i_sb);
} }
/* undo the setattr if we errored out and it's needed */
if (rc != 0 && dosattr != 0)
cifs_set_file_info(inode, attrs, xid, full_path, origattr);
out_reval:
if (inode) { if (inode) {
inode->i_ctime = inode->i_mtime = current_fs_time(inode->i_sb);
cifsInode = CIFS_I(inode); cifsInode = CIFS_I(inode);
cifsInode->time = 0; /* force revalidate of dir as well */ cifsInode->time = 0; /* will force revalidate to get info
when needed */
inode->i_ctime = current_fs_time(sb);
} }
dir->i_ctime = dir->i_mtime = current_fs_time(sb);
cifsInode = CIFS_I(dir);
CIFS_I(dir)->time = 0; /* force revalidate of dir as well */
kfree(full_path); kfree(full_path);
kfree(attrs);
FreeXid(xid); FreeXid(xid);
return rc; return rc;
} }
...@@ -869,7 +965,7 @@ static void posix_fill_in_inode(struct inode *tmp_inode, ...@@ -869,7 +965,7 @@ static void posix_fill_in_inode(struct inode *tmp_inode,
int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
{ {
int rc = 0; int rc = 0, tmprc;
int xid; int xid;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
...@@ -931,6 +1027,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) ...@@ -931,6 +1027,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
kfree(pInfo); kfree(pInfo);
goto mkdir_get_info; goto mkdir_get_info;
} }
/* Is an i_ino of zero legal? */ /* Is an i_ino of zero legal? */
/* Are there sanity checks we can use to ensure that /* Are there sanity checks we can use to ensure that
the server is really filling in that field? */ the server is really filling in that field? */
...@@ -1019,12 +1116,20 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) ...@@ -1019,12 +1116,20 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
(mode & S_IWUGO) == 0) { (mode & S_IWUGO) == 0) {
FILE_BASIC_INFO pInfo; FILE_BASIC_INFO pInfo;
struct cifsInodeInfo *cifsInode;
u32 dosattrs;
memset(&pInfo, 0, sizeof(pInfo)); memset(&pInfo, 0, sizeof(pInfo));
pInfo.Attributes = cpu_to_le32(ATTR_READONLY); cifsInode = CIFS_I(newinode);
CIFSSMBSetPathInfo(xid, pTcon, full_path, dosattrs = cifsInode->cifsAttrs|ATTR_READONLY;
&pInfo, cifs_sb->local_nls, pInfo.Attributes = cpu_to_le32(dosattrs);
tmprc = CIFSSMBSetPathInfo(xid, pTcon,
full_path, &pInfo,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
if (tmprc == 0)
cifsInode->cifsAttrs = dosattrs;
} }
if (direntry->d_inode) { if (direntry->d_inode) {
if (cifs_sb->mnt_cifs_flags & if (cifs_sb->mnt_cifs_flags &
...@@ -1096,117 +1201,141 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) ...@@ -1096,117 +1201,141 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
return rc; return rc;
} }
static int
cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
struct dentry *to_dentry, const char *toPath)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb);
struct cifsTconInfo *pTcon = cifs_sb->tcon;
__u16 srcfid;
int oplock, rc;
/* try path-based rename first */
rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
/*
* don't bother with rename by filehandle unless file is busy and
* source Note that cross directory moves do not work with
* rename by filehandle to various Windows servers.
*/
if (rc == 0 || rc != -ETXTBSY)
return rc;
/* open the file to be renamed -- we need DELETE perms */
rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE,
CREATE_NOT_DIR, &srcfid, &oplock, NULL,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc == 0) {
rc = CIFSSMBRenameOpenFile(xid, pTcon, srcfid,
(const char *) to_dentry->d_name.name,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
CIFSSMBClose(xid, pTcon, srcfid);
}
return rc;
}
int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, int cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
struct inode *target_inode, struct dentry *target_direntry) struct inode *target_inode, struct dentry *target_direntry)
{ {
char *fromName; char *fromName = NULL;
char *toName; char *toName = NULL;
struct cifs_sb_info *cifs_sb_source; struct cifs_sb_info *cifs_sb_source;
struct cifs_sb_info *cifs_sb_target; struct cifs_sb_info *cifs_sb_target;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
FILE_UNIX_BASIC_INFO *info_buf_source = NULL;
FILE_UNIX_BASIC_INFO *info_buf_target;
int xid; int xid;
int rc = 0; int rc;
xid = GetXid();
cifs_sb_target = CIFS_SB(target_inode->i_sb); cifs_sb_target = CIFS_SB(target_inode->i_sb);
cifs_sb_source = CIFS_SB(source_inode->i_sb); cifs_sb_source = CIFS_SB(source_inode->i_sb);
pTcon = cifs_sb_source->tcon; pTcon = cifs_sb_source->tcon;
xid = GetXid();
/*
* BB: this might be allowed if same server, but different share.
* Consider adding support for this
*/
if (pTcon != cifs_sb_target->tcon) { if (pTcon != cifs_sb_target->tcon) {
FreeXid(xid); rc = -EXDEV;
return -EXDEV; /* BB actually could be allowed if same server, goto cifs_rename_exit;
but different share.
Might eventually add support for this */
} }
/* we already have the rename sem so we do not need to grab it again /*
here to protect the path integrity */ * 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_direntry);
if (fromName == NULL) {
rc = -ENOMEM;
goto cifs_rename_exit;
}
toName = build_path_from_dentry(target_direntry); toName = build_path_from_dentry(target_direntry);
if ((fromName == NULL) || (toName == NULL)) { if (toName == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
goto cifs_rename_exit; goto cifs_rename_exit;
} }
rc = CIFSSMBRename(xid, pTcon, fromName, toName, rc = cifs_do_rename(xid, source_direntry, fromName,
cifs_sb_source->local_nls, target_direntry, toName);
cifs_sb_source->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc == -EEXIST) { if (rc == -EEXIST) {
/* check if they are the same file because rename of hardlinked if (pTcon->unix_ext) {
files is a noop */ /*
FILE_UNIX_BASIC_INFO *info_buf_source; * Are src and dst hardlinks of same inode? We can
FILE_UNIX_BASIC_INFO *info_buf_target; * only tell with unix extensions enabled
*/
info_buf_source = info_buf_source =
kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),
if (info_buf_source != NULL) { GFP_KERNEL);
if (info_buf_source == NULL)
goto unlink_target;
info_buf_target = info_buf_source + 1; info_buf_target = info_buf_source + 1;
if (pTcon->unix_ext) rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName,
rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName, info_buf_source,
info_buf_source, cifs_sb_source->local_nls,
cifs_sb_source->local_nls, cifs_sb_source->mnt_cifs_flags &
cifs_sb_source->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
/* else rc is still EEXIST so will fall through to if (rc != 0)
unlink the target and retry rename */ goto unlink_target;
if (rc == 0) {
rc = CIFSSMBUnixQPathInfo(xid, pTcon, toName, rc = CIFSSMBUnixQPathInfo(xid, pTcon,
info_buf_target, toName, info_buf_target,
cifs_sb_target->local_nls, cifs_sb_target->local_nls,
/* remap based on source sb */ /* remap based on source sb */
cifs_sb_source->mnt_cifs_flags & cifs_sb_source->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
}
if ((rc == 0) &&
(info_buf_source->UniqueId ==
info_buf_target->UniqueId)) {
/* do not rename since the files are hardlinked which
is a noop */
} else {
/* we either can not tell the files are hardlinked
(as with Windows servers) or files are not
hardlinked so delete the target manually before
renaming to follow POSIX rather than Windows
semantics */
cifs_unlink(target_inode, target_direntry);
rc = CIFSSMBRename(xid, pTcon, fromName,
toName,
cifs_sb_source->local_nls,
cifs_sb_source->mnt_cifs_flags
& CIFS_MOUNT_MAP_SPECIAL_CHR);
}
kfree(info_buf_source);
} /* if we can not get memory just leave rc as EEXIST */
}
if (rc)
cFYI(1, ("rename rc %d", rc));
if ((rc == -EIO) || (rc == -EEXIST)) {
int oplock = 0;
__u16 netfid;
/* BB FIXME Is Generic Read correct for rename? */
/* if renaming directory - we should not say CREATE_NOT_DIR,
need to test renaming open directory, also GENERIC_READ
might not right be right access to request */
rc = CIFSSMBOpen(xid, pTcon, fromName, FILE_OPEN, GENERIC_READ,
CREATE_NOT_DIR, &netfid, &oplock, NULL,
cifs_sb_source->local_nls,
cifs_sb_source->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc == 0) {
rc = CIFSSMBRenameOpenFile(xid, pTcon, netfid, toName,
cifs_sb_source->local_nls,
cifs_sb_source->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
CIFSSMBClose(xid, pTcon, netfid);
} 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:
/*
* 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);
} }
cifs_rename_exit: cifs_rename_exit:
kfree(info_buf_source);
kfree(fromName); kfree(fromName);
kfree(toName); kfree(toName);
FreeXid(xid); FreeXid(xid);
...@@ -1506,101 +1635,6 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, ...@@ -1506,101 +1635,6 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
return rc; return rc;
} }
static int
cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid,
char *full_path, __u32 dosattr)
{
int rc;
int oplock = 0;
__u16 netfid;
__u32 netpid;
bool set_time = false;
struct cifsFileInfo *open_file;
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsTconInfo *pTcon = cifs_sb->tcon;
FILE_BASIC_INFO info_buf;
if (attrs->ia_valid & ATTR_ATIME) {
set_time = true;
info_buf.LastAccessTime =
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime));
} else
info_buf.LastAccessTime = 0;
if (attrs->ia_valid & ATTR_MTIME) {
set_time = true;
info_buf.LastWriteTime =
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime));
} else
info_buf.LastWriteTime = 0;
/*
* Samba throws this field away, but windows may actually use it.
* Do not set ctime unless other time stamps are changed explicitly
* (i.e. by utimes()) since we would then have a mix of client and
* server times.
*/
if (set_time && (attrs->ia_valid & ATTR_CTIME)) {
cFYI(1, ("CIFS - CTIME changed"));
info_buf.ChangeTime =
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
} else
info_buf.ChangeTime = 0;
info_buf.CreationTime = 0; /* don't change */
info_buf.Attributes = cpu_to_le32(dosattr);
/*
* If the file is already open for write, just use that fileid
*/
open_file = find_writable_file(cifsInode);
if (open_file) {
netfid = open_file->netfid;
netpid = open_file->pid;
goto set_via_filehandle;
}
/*
* NT4 apparently returns success on this call, but it doesn't
* really work.
*/
if (!(pTcon->ses->flags & CIFS_SES_NT4)) {
rc = CIFSSMBSetPathInfo(xid, pTcon, full_path,
&info_buf, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc != -EOPNOTSUPP && rc != -EINVAL)
goto out;
}
cFYI(1, ("calling SetFileInfo since SetPathInfo for "
"times not supported by this server"));
rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN,
SYNCHRONIZE | 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) {
if (rc == -EIO)
rc = -EINVAL;
goto out;
}
netpid = current->tgid;
set_via_filehandle:
rc = CIFSSMBSetFileInfo(xid, pTcon, &info_buf, netfid, netpid);
if (open_file == NULL)
CIFSSMBClose(xid, pTcon, netfid);
else
atomic_dec(&open_file->wrtPending);
out:
return rc;
}
static int static int
cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
{ {
......
...@@ -150,8 +150,7 @@ cifs_buf_get(void) ...@@ -150,8 +150,7 @@ cifs_buf_get(void)
but it may be more efficient to always alloc same size but it may be more efficient to always alloc same size
albeit slightly larger than necessary and maxbuffersize albeit slightly larger than necessary and maxbuffersize
defaults to this and can not be bigger */ defaults to this and can not be bigger */
ret_buf = (struct smb_hdr *) mempool_alloc(cifs_req_poolp, ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS);
GFP_KERNEL | GFP_NOFS);
/* clear the first few header bytes */ /* clear the first few header bytes */
/* for most paths, more is cleared in header_assemble */ /* for most paths, more is cleared in header_assemble */
...@@ -188,8 +187,7 @@ cifs_small_buf_get(void) ...@@ -188,8 +187,7 @@ cifs_small_buf_get(void)
but it may be more efficient to always alloc same size but it may be more efficient to always alloc same size
albeit slightly larger than necessary and maxbuffersize albeit slightly larger than necessary and maxbuffersize
defaults to this and can not be bigger */ defaults to this and can not be bigger */
ret_buf = (struct smb_hdr *) mempool_alloc(cifs_sm_req_poolp, ret_buf = mempool_alloc(cifs_sm_req_poolp, GFP_NOFS);
GFP_KERNEL | GFP_NOFS);
if (ret_buf) { if (ret_buf) {
/* No need to clear memory here, cleared in header assemble */ /* No need to clear memory here, cleared in header assemble */
/* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/ /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/
......
...@@ -640,6 +640,70 @@ static int is_dir_changed(struct file *file) ...@@ -640,6 +640,70 @@ static int is_dir_changed(struct file *file)
} }
static int cifs_save_resume_key(const char *current_entry,
struct cifsFileInfo *cifsFile)
{
int rc = 0;
unsigned int len = 0;
__u16 level;
char *filename;
if ((cifsFile == NULL) || (current_entry == NULL))
return -EINVAL;
level = cifsFile->srch_inf.info_level;
if (level == SMB_FIND_FILE_UNIX) {
FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
filename = &pFindData->FileName[0];
if (cifsFile->srch_inf.unicode) {
len = cifs_unicode_bytelen(filename);
} else {
/* BB should we make this strnlen of PATH_MAX? */
len = strnlen(filename, PATH_MAX);
}
cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
FILE_DIRECTORY_INFO *pFindData =
(FILE_DIRECTORY_INFO *)current_entry;
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
} else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
FILE_FULL_DIRECTORY_INFO *pFindData =
(FILE_FULL_DIRECTORY_INFO *)current_entry;
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
} else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
SEARCH_ID_FULL_DIR_INFO *pFindData =
(SEARCH_ID_FULL_DIR_INFO *)current_entry;
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
} else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
FILE_BOTH_DIRECTORY_INFO *pFindData =
(FILE_BOTH_DIRECTORY_INFO *)current_entry;
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
} else if (level == SMB_FIND_FILE_INFO_STANDARD) {
FIND_FILE_STANDARD_INFO *pFindData =
(FIND_FILE_STANDARD_INFO *)current_entry;
filename = &pFindData->FileName[0];
/* one byte length, no name conversion */
len = (unsigned int)pFindData->FileNameLength;
cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
} else {
cFYI(1, ("Unknown findfirst level %d", level));
return -EINVAL;
}
cifsFile->srch_inf.resume_name_len = len;
cifsFile->srch_inf.presume_name = filename;
return rc;
}
/* find the corresponding entry in the search */ /* find the corresponding entry in the search */
/* Note that the SMB server returns search entries for . and .. which /* Note that the SMB server returns search entries for . and .. which
complicates logic here if we choose to parse for them and we do not complicates logic here if we choose to parse for them and we do not
...@@ -703,6 +767,7 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, ...@@ -703,6 +767,7 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) && while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) &&
(rc == 0) && !cifsFile->srch_inf.endOfSearch) { (rc == 0) && !cifsFile->srch_inf.endOfSearch) {
cFYI(1, ("calling findnext2")); cFYI(1, ("calling findnext2"));
cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile);
rc = CIFSFindNext(xid, pTcon, cifsFile->netfid, rc = CIFSFindNext(xid, pTcon, cifsFile->netfid,
&cifsFile->srch_inf); &cifsFile->srch_inf);
if (rc) if (rc)
...@@ -919,69 +984,6 @@ static int cifs_filldir(char *pfindEntry, struct file *file, ...@@ -919,69 +984,6 @@ static int cifs_filldir(char *pfindEntry, struct file *file,
return rc; return rc;
} }
static int cifs_save_resume_key(const char *current_entry,
struct cifsFileInfo *cifsFile)
{
int rc = 0;
unsigned int len = 0;
__u16 level;
char *filename;
if ((cifsFile == NULL) || (current_entry == NULL))
return -EINVAL;
level = cifsFile->srch_inf.info_level;
if (level == SMB_FIND_FILE_UNIX) {
FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
filename = &pFindData->FileName[0];
if (cifsFile->srch_inf.unicode) {
len = cifs_unicode_bytelen(filename);
} else {
/* BB should we make this strnlen of PATH_MAX? */
len = strnlen(filename, PATH_MAX);
}
cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
FILE_DIRECTORY_INFO *pFindData =
(FILE_DIRECTORY_INFO *)current_entry;
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
} else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
FILE_FULL_DIRECTORY_INFO *pFindData =
(FILE_FULL_DIRECTORY_INFO *)current_entry;
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
} else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
SEARCH_ID_FULL_DIR_INFO *pFindData =
(SEARCH_ID_FULL_DIR_INFO *)current_entry;
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
} else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
FILE_BOTH_DIRECTORY_INFO *pFindData =
(FILE_BOTH_DIRECTORY_INFO *)current_entry;
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
} else if (level == SMB_FIND_FILE_INFO_STANDARD) {
FIND_FILE_STANDARD_INFO *pFindData =
(FIND_FILE_STANDARD_INFO *)current_entry;
filename = &pFindData->FileName[0];
/* one byte length, no name conversion */
len = (unsigned int)pFindData->FileNameLength;
cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
} else {
cFYI(1, ("Unknown findfirst level %d", level));
return -EINVAL;
}
cifsFile->srch_inf.resume_name_len = len;
cifsFile->srch_inf.presume_name = filename;
return rc;
}
int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
{ {
......
...@@ -624,8 +624,10 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, ...@@ -624,8 +624,10 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
ses, nls_cp); ses, nls_cp);
ssetup_exit: ssetup_exit:
if (spnego_key) if (spnego_key) {
key_revoke(spnego_key);
key_put(spnego_key); key_put(spnego_key);
}
kfree(str_area); kfree(str_area);
if (resp_buf_type == CIFS_SMALL_BUFFER) { if (resp_buf_type == CIFS_SMALL_BUFFER) {
cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base)); cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base));
......
...@@ -50,8 +50,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) ...@@ -50,8 +50,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
return NULL; return NULL;
} }
temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp, temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
GFP_KERNEL | GFP_NOFS);
if (temp == NULL) if (temp == NULL)
return temp; return temp;
else { else {
......
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