Commit fe7a719b authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs fixes from Steve French:
 "Various fixes for stable for CIFS/SMB3 especially for better
  interoperability for SMB3 to Macs.

  It also includes Pavel's improvements to SMB3 async i/o support
  (which is much faster now)"

* 'for-next' of git://git.samba.org/sfrench/cifs-2.6:
  CIFS: add misssing SFM mapping for doublequote
  SMB3: Work around mount failure when using SMB3 dialect to Macs
  cifs: fix CIFS_IOC_GET_MNT_INFO oops
  CIFS: fix mapping of SFM_SPACE and SFM_PERIOD
  CIFS: fix oplock break deadlocks
  cifs: fix CIFS_ENUMERATE_SNAPSHOTS oops
  cifs: fix leak in FSCTL_ENUM_SNAPS response handling
  Set unicode flag on cifs echo request to avoid Mac error
  CIFS: Add asynchronous write support through kernel AIO
  CIFS: Add asynchronous read support through kernel AIO
  CIFS: Add asynchronous context to support kernel AIO
  cifs: fix IPv6 link local, with scope id, address parsing
  cifs: small underflow in cnvrtDosUnixTm()
parents d484467c 85435d7a
...@@ -83,6 +83,9 @@ convert_sfm_char(const __u16 src_char, char *target) ...@@ -83,6 +83,9 @@ convert_sfm_char(const __u16 src_char, char *target)
case SFM_COLON: case SFM_COLON:
*target = ':'; *target = ':';
break; break;
case SFM_DOUBLEQUOTE:
*target = '"';
break;
case SFM_ASTERISK: case SFM_ASTERISK:
*target = '*'; *target = '*';
break; break;
...@@ -418,6 +421,9 @@ static __le16 convert_to_sfm_char(char src_char, bool end_of_string) ...@@ -418,6 +421,9 @@ static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
case ':': case ':':
dest_char = cpu_to_le16(SFM_COLON); dest_char = cpu_to_le16(SFM_COLON);
break; break;
case '"':
dest_char = cpu_to_le16(SFM_DOUBLEQUOTE);
break;
case '*': case '*':
dest_char = cpu_to_le16(SFM_ASTERISK); dest_char = cpu_to_le16(SFM_ASTERISK);
break; break;
......
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
* not conflict (although almost does) with the mapping above. * not conflict (although almost does) with the mapping above.
*/ */
#define SFM_DOUBLEQUOTE ((__u16) 0xF020)
#define SFM_ASTERISK ((__u16) 0xF021) #define SFM_ASTERISK ((__u16) 0xF021)
#define SFM_QUESTION ((__u16) 0xF025) #define SFM_QUESTION ((__u16) 0xF025)
#define SFM_COLON ((__u16) 0xF022) #define SFM_COLON ((__u16) 0xF022)
...@@ -64,8 +65,8 @@ ...@@ -64,8 +65,8 @@
#define SFM_LESSTHAN ((__u16) 0xF023) #define SFM_LESSTHAN ((__u16) 0xF023)
#define SFM_PIPE ((__u16) 0xF027) #define SFM_PIPE ((__u16) 0xF027)
#define SFM_SLASH ((__u16) 0xF026) #define SFM_SLASH ((__u16) 0xF026)
#define SFM_PERIOD ((__u16) 0xF028) #define SFM_SPACE ((__u16) 0xF028)
#define SFM_SPACE ((__u16) 0xF029) #define SFM_PERIOD ((__u16) 0xF029)
/* /*
* Mapping mechanism to use when one of the seven reserved characters is * Mapping mechanism to use when one of the seven reserved characters is
......
...@@ -88,6 +88,7 @@ extern mempool_t *cifs_req_poolp; ...@@ -88,6 +88,7 @@ extern mempool_t *cifs_req_poolp;
extern mempool_t *cifs_mid_poolp; extern mempool_t *cifs_mid_poolp;
struct workqueue_struct *cifsiod_wq; struct workqueue_struct *cifsiod_wq;
struct workqueue_struct *cifsoplockd_wq;
__u32 cifs_lock_secret; __u32 cifs_lock_secret;
/* /*
...@@ -1375,9 +1376,16 @@ init_cifs(void) ...@@ -1375,9 +1376,16 @@ init_cifs(void)
goto out_clean_proc; goto out_clean_proc;
} }
cifsoplockd_wq = alloc_workqueue("cifsoplockd",
WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
if (!cifsoplockd_wq) {
rc = -ENOMEM;
goto out_destroy_cifsiod_wq;
}
rc = cifs_fscache_register(); rc = cifs_fscache_register();
if (rc) if (rc)
goto out_destroy_wq; goto out_destroy_cifsoplockd_wq;
rc = cifs_init_inodecache(); rc = cifs_init_inodecache();
if (rc) if (rc)
...@@ -1425,7 +1433,9 @@ init_cifs(void) ...@@ -1425,7 +1433,9 @@ init_cifs(void)
cifs_destroy_inodecache(); cifs_destroy_inodecache();
out_unreg_fscache: out_unreg_fscache:
cifs_fscache_unregister(); cifs_fscache_unregister();
out_destroy_wq: out_destroy_cifsoplockd_wq:
destroy_workqueue(cifsoplockd_wq);
out_destroy_cifsiod_wq:
destroy_workqueue(cifsiod_wq); destroy_workqueue(cifsiod_wq);
out_clean_proc: out_clean_proc:
cifs_proc_clean(); cifs_proc_clean();
...@@ -1448,6 +1458,7 @@ exit_cifs(void) ...@@ -1448,6 +1458,7 @@ exit_cifs(void)
cifs_destroy_mids(); cifs_destroy_mids();
cifs_destroy_inodecache(); cifs_destroy_inodecache();
cifs_fscache_unregister(); cifs_fscache_unregister();
destroy_workqueue(cifsoplockd_wq);
destroy_workqueue(cifsiod_wq); destroy_workqueue(cifsiod_wq);
cifs_proc_clean(); cifs_proc_clean();
} }
......
...@@ -1115,6 +1115,23 @@ struct cifs_io_parms { ...@@ -1115,6 +1115,23 @@ struct cifs_io_parms {
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
}; };
struct cifs_aio_ctx {
struct kref refcount;
struct list_head list;
struct mutex aio_mutex;
struct completion done;
struct iov_iter iter;
struct kiocb *iocb;
struct cifsFileInfo *cfile;
struct bio_vec *bv;
loff_t pos;
unsigned int npages;
ssize_t rc;
unsigned int len;
unsigned int total_len;
bool should_dirty;
};
struct cifs_readdata; struct cifs_readdata;
/* asynchronous read support */ /* asynchronous read support */
...@@ -1124,6 +1141,7 @@ struct cifs_readdata { ...@@ -1124,6 +1141,7 @@ struct cifs_readdata {
struct completion done; struct completion done;
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct address_space *mapping; struct address_space *mapping;
struct cifs_aio_ctx *ctx;
__u64 offset; __u64 offset;
unsigned int bytes; unsigned int bytes;
unsigned int got_bytes; unsigned int got_bytes;
...@@ -1154,6 +1172,7 @@ struct cifs_writedata { ...@@ -1154,6 +1172,7 @@ struct cifs_writedata {
enum writeback_sync_modes sync_mode; enum writeback_sync_modes sync_mode;
struct work_struct work; struct work_struct work;
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct cifs_aio_ctx *ctx;
__u64 offset; __u64 offset;
pid_t pid; pid_t pid;
unsigned int bytes; unsigned int bytes;
...@@ -1683,6 +1702,7 @@ void cifs_oplock_break(struct work_struct *work); ...@@ -1683,6 +1702,7 @@ void cifs_oplock_break(struct work_struct *work);
extern const struct slow_work_ops cifs_oplock_break_ops; extern const struct slow_work_ops cifs_oplock_break_ops;
extern struct workqueue_struct *cifsiod_wq; extern struct workqueue_struct *cifsiod_wq;
extern struct workqueue_struct *cifsoplockd_wq;
extern __u32 cifs_lock_secret; extern __u32 cifs_lock_secret;
extern mempool_t *cifs_mid_poolp; extern mempool_t *cifs_mid_poolp;
......
...@@ -535,4 +535,7 @@ int __cifs_calc_signature(struct smb_rqst *rqst, ...@@ -535,4 +535,7 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
struct shash_desc *shash); struct shash_desc *shash);
enum securityEnum cifs_select_sectype(struct TCP_Server_Info *, enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
enum securityEnum); enum securityEnum);
struct cifs_aio_ctx *cifs_aio_ctx_alloc(void);
void cifs_aio_ctx_release(struct kref *refcount);
int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw);
#endif /* _CIFSPROTO_H */ #endif /* _CIFSPROTO_H */
...@@ -718,6 +718,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server) ...@@ -718,6 +718,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
if (rc) if (rc)
return rc; return rc;
if (server->capabilities & CAP_UNICODE)
smb->hdr.Flags2 |= SMBFLG2_UNICODE;
/* set up echo request */ /* set up echo request */
smb->hdr.Tid = 0xffff; smb->hdr.Tid = 0xffff;
smb->hdr.WordCount = 1; smb->hdr.WordCount = 1;
......
...@@ -1946,9 +1946,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, ...@@ -1946,9 +1946,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
} }
if (!got_ip) { if (!got_ip) {
int len;
const char *slash;
/* No ip= option specified? Try to get it from UNC */ /* No ip= option specified? Try to get it from UNC */
if (!cifs_convert_address(dstaddr, &vol->UNC[2], /* Use the address part of the UNC. */
strlen(&vol->UNC[2]))) { slash = strchr(&vol->UNC[2], '\\');
len = slash - &vol->UNC[2];
if (!cifs_convert_address(dstaddr, &vol->UNC[2], len)) {
pr_err("Unable to determine destination address.\n"); pr_err("Unable to determine destination address.\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
......
This diff is collapsed.
...@@ -209,10 +209,14 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) ...@@ -209,10 +209,14 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
break; break;
case CIFS_IOC_GET_MNT_INFO: case CIFS_IOC_GET_MNT_INFO:
if (pSMBFile == NULL)
break;
tcon = tlink_tcon(pSMBFile->tlink); tcon = tlink_tcon(pSMBFile->tlink);
rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg); rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
break; break;
case CIFS_ENUMERATE_SNAPSHOTS: case CIFS_ENUMERATE_SNAPSHOTS:
if (pSMBFile == NULL)
break;
if (arg == 0) { if (arg == 0) {
rc = -EINVAL; rc = -EINVAL;
goto cifs_ioc_exit; goto cifs_ioc_exit;
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/mempool.h> #include <linux/mempool.h>
#include <linux/vmalloc.h>
#include "cifspdu.h" #include "cifspdu.h"
#include "cifsglob.h" #include "cifsglob.h"
#include "cifsproto.h" #include "cifsproto.h"
...@@ -488,7 +489,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) ...@@ -488,7 +489,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&pCifsInode->flags); &pCifsInode->flags);
queue_work(cifsiod_wq, queue_work(cifsoplockd_wq,
&netfile->oplock_break); &netfile->oplock_break);
netfile->oplock_break_cancelled = false; netfile->oplock_break_cancelled = false;
...@@ -741,3 +742,122 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, ...@@ -741,3 +742,122 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
} }
return rc; return rc;
} }
struct cifs_aio_ctx *
cifs_aio_ctx_alloc(void)
{
struct cifs_aio_ctx *ctx;
ctx = kzalloc(sizeof(struct cifs_aio_ctx), GFP_KERNEL);
if (!ctx)
return NULL;
INIT_LIST_HEAD(&ctx->list);
mutex_init(&ctx->aio_mutex);
init_completion(&ctx->done);
kref_init(&ctx->refcount);
return ctx;
}
void
cifs_aio_ctx_release(struct kref *refcount)
{
struct cifs_aio_ctx *ctx = container_of(refcount,
struct cifs_aio_ctx, refcount);
cifsFileInfo_put(ctx->cfile);
kvfree(ctx->bv);
kfree(ctx);
}
#define CIFS_AIO_KMALLOC_LIMIT (1024 * 1024)
int
setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw)
{
ssize_t rc;
unsigned int cur_npages;
unsigned int npages = 0;
unsigned int i;
size_t len;
size_t count = iov_iter_count(iter);
unsigned int saved_len;
size_t start;
unsigned int max_pages = iov_iter_npages(iter, INT_MAX);
struct page **pages = NULL;
struct bio_vec *bv = NULL;
if (iter->type & ITER_KVEC) {
memcpy(&ctx->iter, iter, sizeof(struct iov_iter));
ctx->len = count;
iov_iter_advance(iter, count);
return 0;
}
if (max_pages * sizeof(struct bio_vec) <= CIFS_AIO_KMALLOC_LIMIT)
bv = kmalloc_array(max_pages, sizeof(struct bio_vec),
GFP_KERNEL);
if (!bv) {
bv = vmalloc(max_pages * sizeof(struct bio_vec));
if (!bv)
return -ENOMEM;
}
if (max_pages * sizeof(struct page *) <= CIFS_AIO_KMALLOC_LIMIT)
pages = kmalloc_array(max_pages, sizeof(struct page *),
GFP_KERNEL);
if (!pages) {
pages = vmalloc(max_pages * sizeof(struct page *));
if (!bv) {
kvfree(bv);
return -ENOMEM;
}
}
saved_len = count;
while (count && npages < max_pages) {
rc = iov_iter_get_pages(iter, pages, count, max_pages, &start);
if (rc < 0) {
cifs_dbg(VFS, "couldn't get user pages (rc=%zd)\n", rc);
break;
}
if (rc > count) {
cifs_dbg(VFS, "get pages rc=%zd more than %zu\n", rc,
count);
break;
}
iov_iter_advance(iter, rc);
count -= rc;
rc += start;
cur_npages = DIV_ROUND_UP(rc, PAGE_SIZE);
if (npages + cur_npages > max_pages) {
cifs_dbg(VFS, "out of vec array capacity (%u vs %u)\n",
npages + cur_npages, max_pages);
break;
}
for (i = 0; i < cur_npages; i++) {
len = rc > PAGE_SIZE ? PAGE_SIZE : rc;
bv[npages + i].bv_page = pages[i];
bv[npages + i].bv_offset = start;
bv[npages + i].bv_len = len - start;
rc -= len;
start = 0;
}
npages += cur_npages;
}
kvfree(pages);
ctx->bv = bv;
ctx->len = saved_len - count;
ctx->npages = npages;
iov_iter_bvec(&ctx->iter, ITER_BVEC | rw, ctx->bv, npages, ctx->len);
return 0;
}
...@@ -980,10 +980,10 @@ struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset) ...@@ -980,10 +980,10 @@ struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
cifs_dbg(VFS, "illegal hours %d\n", st->Hours); cifs_dbg(VFS, "illegal hours %d\n", st->Hours);
days = sd->Day; days = sd->Day;
month = sd->Month; month = sd->Month;
if ((days > 31) || (month > 12)) { if (days < 1 || days > 31 || month < 1 || month > 12) {
cifs_dbg(VFS, "illegal date, month %d day: %d\n", month, days); cifs_dbg(VFS, "illegal date, month %d day: %d\n", month, days);
if (month > 12) days = clamp(days, 1, 31);
month = 12; month = clamp(month, 1, 12);
} }
month -= 1; month -= 1;
days += total_days_of_prev_months[month]; days += total_days_of_prev_months[month];
......
...@@ -499,7 +499,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, ...@@ -499,7 +499,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
else else
cfile->oplock_break_cancelled = true; cfile->oplock_break_cancelled = true;
queue_work(cifsiod_wq, &cfile->oplock_break); queue_work(cifsoplockd_wq, &cfile->oplock_break);
kfree(lw); kfree(lw);
return true; return true;
} }
...@@ -643,7 +643,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -643,7 +643,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags); &cinode->flags);
spin_unlock(&cfile->file_info_lock); spin_unlock(&cfile->file_info_lock);
queue_work(cifsiod_wq, &cfile->oplock_break); queue_work(cifsoplockd_wq,
&cfile->oplock_break);
spin_unlock(&tcon->open_file_lock); spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
......
...@@ -942,6 +942,7 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -942,6 +942,7 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
} }
if (snapshot_in.snapshot_array_size < sizeof(struct smb_snapshot_array)) { if (snapshot_in.snapshot_array_size < sizeof(struct smb_snapshot_array)) {
rc = -ERANGE; rc = -ERANGE;
kfree(retbuf);
return rc; return rc;
} }
......
...@@ -633,8 +633,12 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -633,8 +633,12 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
} }
if (rsplen != sizeof(struct validate_negotiate_info_rsp)) { if (rsplen != sizeof(struct validate_negotiate_info_rsp)) {
cifs_dbg(VFS, "invalid size of protocol negotiate response\n"); cifs_dbg(VFS, "invalid protocol negotiate response size: %d\n",
return -EIO; rsplen);
/* relax check since Mac returns max bufsize allowed on ioctl */
if (rsplen > CIFSMaxBufSize)
return -EIO;
} }
/* check validate negotiate info response matches what we got earlier */ /* check validate negotiate info response matches what we got earlier */
...@@ -1854,8 +1858,12 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, ...@@ -1854,8 +1858,12 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
* than one credit. Windows typically sets this smaller, but for some * than one credit. Windows typically sets this smaller, but for some
* ioctls it may be useful to allow server to send more. No point * ioctls it may be useful to allow server to send more. No point
* limiting what the server can send as long as fits in one credit * limiting what the server can send as long as fits in one credit
* Unfortunately - we can not handle more than CIFS_MAX_MSG_SIZE
* (by default, note that it can be overridden to make max larger)
* in responses (except for read responses which can be bigger.
* We may want to bump this limit up
*/ */
req->MaxOutputResponse = cpu_to_le32(0xFF00); /* < 64K uses 1 credit */ req->MaxOutputResponse = cpu_to_le32(CIFSMaxBufSize);
if (is_fsctl) if (is_fsctl)
req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL);
......
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