diff options
| author | Enzo Matsumiya <ematsumiya@suse.de> | 2025-07-07 14:22:18 -0300 |
|---|---|---|
| committer | Enzo Matsumiya <ematsumiya@suse.de> | 2025-07-09 02:48:10 -0300 |
| commit | e835f5ff89e9106fe8c8d1fe8ea3917cea67c016 (patch) | |
| tree | d94e108c09b6ba2224bb123968c3b4f9cb9bc82a | |
| parent | 556055e008ee0054151085fbf3f9fbf182cf0b98 (diff) | |
| download | linux-e835f5ff89e9106fe8c8d1fe8ea3917cea67c016.tar.gz linux-e835f5ff89e9106fe8c8d1fe8ea3917cea67c016.tar.bz2 linux-e835f5ff89e9106fe8c8d1fe8ea3917cea67c016.zip | |
smb: client: don't break parent lease on mkdir
When creating a new directory, if we have its parent cached (i.e. we
have a lease), set ParentLeaseKey for the target new dir, while also
already requesting a lease for it.
If it's possible to cache the new dir, try a single Create request first
(without Close). On success, cache it, otherwise, fallback to previous
behaviour (compound Create + Close).
Add some helpers to cached_dir:
- can_cache_dir()
- init_cached_dir() - just allocate the object in the simplest way
- add_cached_dir() - turn cfid available, but up to callers to make it
valid
Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
| -rw-r--r-- | fs/smb/client/cached_dir.c | 88 | ||||
| -rw-r--r-- | fs/smb/client/cached_dir.h | 3 | ||||
| -rw-r--r-- | fs/smb/client/cifsglob.h | 2 | ||||
| -rw-r--r-- | fs/smb/client/cifsproto.h | 12 | ||||
| -rw-r--r-- | fs/smb/client/cifssmb.c | 2 | ||||
| -rw-r--r-- | fs/smb/client/inode.c | 11 | ||||
| -rw-r--r-- | fs/smb/client/smb2inode.c | 149 | ||||
| -rw-r--r-- | fs/smb/client/smb2pdu.c | 108 | ||||
| -rw-r--r-- | fs/smb/client/smb2proto.h | 2 |
9 files changed, 295 insertions, 82 deletions
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c index 696a1d224dee..d28855a1edc1 100644 --- a/fs/smb/client/cached_dir.c +++ b/fs/smb/client/cached_dir.c @@ -12,7 +12,6 @@ #include "smb2proto.h" #include "cached_dir.h" -static struct cached_fid *init_cached_dir(struct cached_fids *cfids, const char *path); static void free_cached_dir(struct cached_fid *cfid); static void smb2_close_cached_fid(struct kref *ref); @@ -192,9 +191,6 @@ replay_again: return 0; } - if (cfids->num_entries >= tcon->max_cached_dirs) - return -ENOENT; - server = cifs_pick_channel(ses); if (!server->ops->new_lease_key) return -EIO; @@ -203,10 +199,10 @@ replay_again: if (!utf16_path) return -ENOMEM; - cfid = init_cached_dir(cfids, path); - if (!cfid) { + rc = add_cached_dir(tcon, path, NULL); + if (rc) { kfree(utf16_path); - return -ENOMEM; + return rc; } pfid = &cfid->fid; @@ -234,7 +230,6 @@ replay_again: } } cfid->dentry = dentry; - cfid->tcon = tcon; /* * We do not hold the lock for the open because in case @@ -617,13 +612,48 @@ bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]) return false; } -static struct cached_fid *init_cached_dir(struct cached_fids *cfids, const char *path) +bool can_cache_dir(struct cifs_tcon *tcon) +{ + struct cached_fids *cfids; + bool ret; + + if (!tcon) + return false; + + cfids = tcon->cfids; + if (!cfids) + return false; + + spin_lock(&cfids->cfid_list_lock); + ret = (cfids->num_entries >= tcon->max_cached_dirs); + spin_unlock(&cfids->cfid_list_lock); + + return ret; +} + +/* + * Bare init a cfid. + * Doesn't add to the list nor set it as valid, use add_cached_dir() later on successful open. + * + * To make the returned cfid valid, callers must add it to the list and set accordingly: + * ->tcon + * ->has_lease + * ->dentry + * ->time + * ->is_open + * ->on_list + * ->file_all_info + * + * Return: new cfid or NULL on error. + */ +struct cached_fid *init_cached_dir(struct cached_fids *cfids, const char *path) { struct cached_fid *cfid; cfid = kzalloc(sizeof(*cfid), GFP_ATOMIC); if (!cfid) return NULL; + cfid->path = kstrdup(path, GFP_ATOMIC); if (!cfid->path) { kfree(cfid); @@ -639,10 +669,30 @@ static struct cached_fid *init_cached_dir(struct cached_fids *cfids, const char kref_init(&cfid->refcount); cfid->cfids = cfids; - cfids->num_entries++; - list_add(&cfid->entry, &cfids->entries); - cfid->on_list = true; - kref_get(&cfid->refcount); + + return cfid; +} + +int add_cached_dir(struct cifs_tcon *tcon, const char *path, struct cached_fid *cfid) +{ + struct cached_fids *cfids = tcon->cfids; + int ret = -ENOENT; + + if (!can_cache_dir(tcon)) + return ret; + + if (!cfid) { + if (!path) { + cifs_dbg(FYI, "no cfid or path to cache, skipping\n"); + goto out; + } + + cfid = init_cached_dir(cfids, path); + if (!cfid) { + ret = -ENOMEM; + goto out; + } + } /* * Set @cfid->has_lease to true during construction so that the lease @@ -652,9 +702,17 @@ static struct cached_fid *init_cached_dir(struct cached_fids *cfids, const char * Concurrent processes won't be to use it yet due to @cfid->time being * zero. */ + spin_lock(&cfids->cfid_list_lock); + cfid->on_list = true; cfid->has_lease = true; - - return cfid; + kref_get(&cfid->refcount); + list_add(&cfid->entry, &cfids->entries); + cfids->num_entries++; + cfid->tcon = tcon; + ret = 0; +out: + spin_unlock(&cfids->cfid_list_lock); + return ret; } static void free_cached_dir(struct cached_fid *cfid) diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h index fd314e7a18ee..4390860fc45c 100644 --- a/fs/smb/client/cached_dir.h +++ b/fs/smb/client/cached_dir.h @@ -71,6 +71,9 @@ enum { extern struct cached_fids *init_cached_dirs(void); extern void free_cached_dirs(struct cached_fids *cfids); +extern bool can_cache_dir(struct cifs_tcon *tcon); +extern struct cached_fid *init_cached_dir(struct cached_fids *cfids, const char *path); +extern int add_cached_dir(struct cifs_tcon *tcon, const char *path, struct cached_fid *cfid); extern struct cached_fid *lookup_cached_dir(struct cached_fids *cfids, const void *key, int type); extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index fdd95e5100cd..ca0b6aecd530 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -439,7 +439,7 @@ struct smb_version_operations { struct cifs_sb_info *cifs_sb); int (*mkdir)(const unsigned int xid, struct inode *inode, umode_t mode, struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *sb); + struct cifs_sb_info *sb, struct dentry *dentry); /* set info on created directory */ void (*mkdir_setinfo)(struct inode *, const char *, struct cifs_sb_info *, struct cifs_tcon *, diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h index 045227ed4efc..2deabf300025 100644 --- a/fs/smb/client/cifsproto.h +++ b/fs/smb/client/cifsproto.h @@ -214,6 +214,15 @@ extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr, extern struct inode *cifs_iget(struct super_block *sb, struct cifs_fattr *fattr); +void cifs_open_info_to_fattr(struct cifs_fattr *fattr, + struct cifs_open_info_data *data, + struct super_block *sb); +int cifs_get_fattr(struct cifs_open_info_data *data, + struct super_block *sb, int xid, + const struct cifs_fid *fid, + struct cifs_fattr *fattr, + struct inode **inode, + const char *full_path); int cifs_get_inode_info(struct inode **inode, const char *full_path, struct cifs_open_info_data *data, struct super_block *sb, int xid, const struct cifs_fid *fid); @@ -441,7 +450,8 @@ extern int CIFSSMBUnixSetPathInfo(const unsigned int xid, extern int CIFSSMBMkDir(const unsigned int xid, struct inode *inode, umode_t mode, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); + const char *name, struct cifs_sb_info *cifs_sb, + struct dentry *dentry); extern int CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, struct cifs_sb_info *cifs_sb); extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index 7216fcec79e8..20f001266aad 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -846,7 +846,7 @@ RmDirRetry: int CIFSSMBMkDir(const unsigned int xid, struct inode *inode, umode_t mode, struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) + struct cifs_sb_info *cifs_sb, struct dentry *dentry) { int rc = 0; CREATE_DIRECTORY_REQ *pSMB = NULL; diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index d8728da54512..50f926aefd68 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -887,7 +887,7 @@ out_reparse: fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); } -static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, +void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data, struct super_block *sb) { @@ -1256,7 +1256,7 @@ out: return rc; } -static int cifs_get_fattr(struct cifs_open_info_data *data, +int cifs_get_fattr(struct cifs_open_info_data *data, struct super_block *sb, int xid, const struct cifs_fid *fid, struct cifs_fattr *fattr, @@ -2273,7 +2273,7 @@ struct dentry *cifs_mkdir(struct mnt_idmap *idmap, struct inode *inode, } /* BB add setting the equivalent of mode via CreateX w/ACLs */ - rc = server->ops->mkdir(xid, inode, mode, tcon, full_path, cifs_sb); + rc = server->ops->mkdir(xid, inode, mode, tcon, full_path, cifs_sb, direntry); if (rc) { cifs_dbg(FYI, "cifs_mkdir returned 0x%x\n", rc); d_drop(direntry); @@ -2281,8 +2281,9 @@ struct dentry *cifs_mkdir(struct mnt_idmap *idmap, struct inode *inode, } /* TODO: skip this for smb2/smb3 */ - rc = cifs_mkdir_qinfo(inode, direntry, mode, full_path, cifs_sb, tcon, - xid); + if (server->vals->protocol_id < SMB20_PROT_ID) + rc = cifs_mkdir_qinfo(inode, direntry, mode, full_path, cifs_sb, tcon, + xid); mkdir_out: /* * Force revalidate to get parent dir info when needed since cached diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index f5fd9fbcc096..cd3901e8649e 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -180,7 +180,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, struct smb2_query_info_rsp *qi_rsp = NULL; struct smb2_compound_vars *vars = NULL; __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_info_data *idata; + struct cifs_open_info_data *idata = NULL; struct cifs_ses *ses = tcon->ses; struct reparse_data_buffer *rbuf; struct TCP_Server_Info *server; @@ -198,6 +198,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, int tmp_rc, rc; int flags = 0; void *data[2]; + struct cached_fid *parent_cfid = NULL; replay_again: /* reinitialize for possible replay */ @@ -205,6 +206,8 @@ replay_again: oplock = SMB2_OPLOCK_LEVEL_NONE; num_rqst = 0; server = cifs_pick_channel(ses); + parent_cfid = NULL; + memset(&fid, 0, sizeof(fid)); vars = kzalloc(sizeof(*vars), GFP_ATOMIC); if (vars == NULL) @@ -243,14 +246,15 @@ replay_again: */ if (dentry) { inode = d_inode(dentry); - if (CIFS_I(inode)->lease_granted && server->ops->get_lease_key) { + if (inode && CIFS_I(inode)->lease_granted && server->ops->get_lease_key) { oplock = SMB2_OPLOCK_LEVEL_LEASE; server->ops->get_lease_key(inode, &fid); } } vars->oparms = *oparms; - vars->oparms.fid = &fid; + if (!vars->oparms.fid) + vars->oparms.fid = &fid; rqst[num_rqst].rq_iov = &vars->open_iov[0]; rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE; @@ -648,7 +652,7 @@ finished: tmp_rc = rc; - if (rc == 0 && num_cmds > 0 && cmds[0] == SMB2_OP_OPEN_QUERY) { + if (rc == 0 && num_cmds > 0 && (cmds[0] == SMB2_OP_OPEN_QUERY || cmds[0] == SMB2_OP_MKDIR)) { create_rsp = rsp_iov[0].iov_base; idata = in_iov[0].iov_base; idata->fi.CreationTime = create_rsp->CreationTime; @@ -663,11 +667,13 @@ finished: idata->fi.DeletePending = 0; idata->fi.Directory = !!(le32_to_cpu(create_rsp->FileAttributes) & ATTR_DIRECTORY); - /* smb2_parse_contexts() fills idata->fi.IndexNumber */ - rc = smb2_parse_contexts(server, &rsp_iov[0], &oparms->fid->epoch, - oparms->fid->lease_key, &oplock, &idata->fi, NULL); - if (rc) - cifs_dbg(VFS, "rc: %d parsing context of compound op\n", rc); + if (cmds[0] == SMB2_OP_OPEN_QUERY) { + /* smb2_parse_contexts() fills idata->fi.IndexNumber */ + rc = smb2_parse_contexts(server, &rsp_iov[0], &oparms->fid->epoch, + oparms->fid->lease_key, &oplock, &idata->fi, NULL); + if (rc) + cifs_dbg(VFS, "rc: %d parsing context of compound op\n", rc); + } } for (i = 0; i < num_cmds; i++) { @@ -850,9 +856,10 @@ finished: num_cmds += 2; if (out_iov && out_buftype) { - memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov)); - memcpy(out_buftype, resp_buftype, - num_cmds * sizeof(*out_buftype)); + if (!cmds || cmds[0] != SMB2_OP_MKDIR) { + memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov)); + memcpy(out_buftype, resp_buftype, num_cmds * sizeof(*out_buftype)); + } } else { for (i = 0; i < num_cmds; i++) free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base); @@ -1108,16 +1115,120 @@ out: int smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode, struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) + struct cifs_sb_info *cifs_sb, struct dentry *dentry) { + struct cifs_open_info_data idata = {}; + struct cached_fid *cfid, *parent_cfid = NULL; struct cifs_open_parms oparms; + struct cifs_fid *fidp, fid = {}; + struct dentry *parent; + struct kvec req_iov; + bool can_cache; + int ret; + + oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES, FILE_CREATE, CREATE_NOT_FILE, mode); + + parent = d_find_alias(parent_inode); + if (parent) { + parent_cfid = lookup_cached_dir(tcon->cfids, parent, CFID_LOOKUP_DENTRY); + dput(parent); + } - oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES, - FILE_CREATE, CREATE_NOT_FILE, mode); - return smb2_compound_op(xid, tcon, cifs_sb, - name, &oparms, NULL, - &(int){SMB2_OP_MKDIR}, 1, - NULL, NULL, NULL, NULL); + can_cache = can_cache_dir(tcon); + if (can_cache) { + struct TCP_Server_Info *server = tcon->ses->server; + __le16 *utf16_path; + u8 oplock = SMB2_OPLOCK_LEVEL_LEASE; + + if (!server->oplocks || tcon->no_lease || !(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING)) + goto mkdir_compound; + + cfid = init_cached_dir(tcon->cfids, name); + if (!cfid) { + ret = -ENOMEM; + goto out; + } + + utf16_path = cifs_convert_path_to_utf16(name, cifs_sb); + if (!utf16_path) { + close_cached_dir(cfid); + ret = -ENOMEM; + goto out; + } + + oparms.fid = fidp = &cfid->fid; + + if (parent_cfid) { + memcpy(fidp->parent_lease_key, parent_cfid->fid.lease_key, SMB2_LEASE_KEY_SIZE); + oparms.lease_flags |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE; + } + + ret = SMB2_open(xid, &oparms, utf16_path, &oplock, &cfid->file_all_info, NULL, NULL, NULL); + kfree(utf16_path); + } else { +mkdir_compound: + req_iov.iov_base = &idata; + oparms.fid = fidp = &fid; + + ret = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms, &req_iov, &(int){SMB2_OP_MKDIR}, 1, + NULL, NULL, NULL, NULL); + } + + if (ret) { + if (can_cache) { + oparms.lease_flags = 0; + oparms.fid = NULL; + close_cached_dir(cfid); + cfid = NULL; + can_cache = false; + goto mkdir_compound; + } + goto out; + } + + if (can_cache) { + struct cifs_fattr fattr = {}; + struct inode *inode = NULL; + + cfid->is_open = true; + idata.fi = cfid->file_all_info; + + ret = cifs_get_fattr(&idata, parent_inode->i_sb, xid, &cfid->fid, &fattr, &inode, name); + if (ret) { + close_cached_dir(cfid); + goto out; + } + + inode = cifs_iget(parent_inode->i_sb, &fattr); + if (!inode) { + close_cached_dir(cfid); + ret = -ENOMEM; + goto out; + } + + d_drop(dentry); + d_add(dentry, inode); + cifs_set_time(dentry, jiffies); + + CIFS_I(inode)->time = jiffies; + tcon->ses->server->ops->set_lease_key(inode, &fid); + + cfid->file_all_info_is_valid = true; + cfid->dentry = dget(dentry); + cfid->time = jiffies; + ret = add_cached_dir(tcon, NULL, cfid); + if (ret) + close_cached_dir(cfid); + } +out: + if (parent_cfid) { + mutex_lock(&parent_cfid->dirents.de_mutex); + parent_cfid->dirents.is_valid = false; + mutex_unlock(&parent_cfid->dirents.de_mutex); + close_cached_dir(parent_cfid); + } + + return ret; } void diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index d478fc58ad70..1e5807415038 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -2999,6 +2999,60 @@ err_free_path: return rc; } +/* + * Set parent lease key for a Create op. + * + * If dir leases are supported, we can always set ParentLeaseKey -- it's only a hint to the server that we're + * not modifying parent itself, only the child. + * + * This avoids a leasebreak of the parent dir in most cases, but the server will still emit one when required + * (e.g. concurrent opens with different options/modes). + * + * Return: true if parent lease key was set, false otherwise. + */ +static bool smb2_set_parent_lease(struct cifs_tcon *tcon, struct cifs_open_parms *oparms) +{ + struct cached_fid *pcfid; + const char *path; + int i, len, sep; + char *tmp; + + if (oparms->lease_flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) + return true; + + if (!oparms->fid) + return false; + + memset(oparms->fid->parent_lease_key, 0, SMB2_LEASE_KEY_SIZE); + + path = oparms->path; + if (!*path) + return false; + + sep = oparms->cifs_sb ? CIFS_DIR_SEP(oparms->cifs_sb) : '\\'; + tmp = strrchr(path, sep); + if (!tmp) + return false; + + len = strlen(tmp); + tmp = kstrdup(path, GFP_ATOMIC); + if (!tmp) + return -ENOMEM; + + i = strlen(path) - len; + memset(&tmp[i], 0, len); + + pcfid = lookup_cached_dir(tcon->cfids, tmp, CFID_LOOKUP_PATH); + if (pcfid) { + memcpy(oparms->fid->parent_lease_key, pcfid->fid.lease_key, SMB2_LEASE_KEY_SIZE); + oparms->lease_flags |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE; + close_cached_dir(pcfid); + } + + kfree(tmp); + return true; +} + int SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, struct smb_rqst *rqst, __u8 *oplock, @@ -3075,48 +3129,24 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, iov[1].iov_len = uni_path_len; iov[1].iov_base = path; - if ((!server->oplocks) || (tcon->no_lease) || !(server->capabilities & SMB2_GLOBAL_CAP_LEASING) || - (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) && - (oparms->create_options & CREATE_NOT_FILE))) + if (!server->oplocks || tcon->no_lease || !(server->capabilities & SMB2_GLOBAL_CAP_LEASING)) { *oplock = SMB2_OPLOCK_LEVEL_NONE; + goto no_lease_ctx; + } - if (*oplock != SMB2_OPLOCK_LEVEL_NONE) { - /* - * If server has dir leases support, we can always set ParentLeaseKey -- it's only a - * hint to the server that we're not modifying parent itself, only the child. - * - * This avoids a leasebreak of the parent dir in most cases, but the server will - * still emit one when required (e.g. concurrent opens with different options/modes). - */ - memset(oparms->fid->parent_lease_key, 0, SMB2_LEASE_KEY_SIZE); - - if (*oparms->path) { - char *tmp_path; - int sep = oparms->cifs_sb ? CIFS_DIR_SEP(oparms->cifs_sb) : '\\'; - - tmp_path = strrchr(oparms->path, sep); - if (tmp_path) { - struct cached_fid *pcfid; - size_t i, len = strlen(tmp_path); - - tmp_path = kstrdup(oparms->path, GFP_ATOMIC); - if (!tmp_path) - return -ENOMEM; - - i = strlen(oparms->path) - len; - memset(&tmp_path[i], 0, len); - - pcfid = lookup_cached_dir(tcon->cfids, tmp_path, CFID_LOOKUP_PATH); - kfree(tmp_path); - if (pcfid) { - memcpy(oparms->fid->parent_lease_key, pcfid->fid.lease_key, - SMB2_LEASE_KEY_SIZE); - oparms->lease_flags |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE; - close_cached_dir(pcfid); - } - } + if (server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) { + if (smb2_set_parent_lease(tcon, oparms)) { + if (*oplock == SMB2_OPLOCK_LEVEL_NONE && + ((oparms->create_options & CREATE_NOT_FILE) || + (oparms->disposition == FILE_OPEN && !(oparms->create_options & CREATE_NOT_DIR)))) + *oplock = SMB2_OPLOCK_LEVEL_LEASE; } + } else if (oparms->create_options & CREATE_NOT_FILE) { + *oplock = SMB2_OPLOCK_LEVEL_NONE; + goto no_lease_ctx; + } + if (*oplock != SMB2_OPLOCK_LEVEL_NONE) { rc = add_lease_context(server, iov, &n_iov, oparms->fid->lease_key, *oplock, oparms->fid->parent_lease_key, @@ -3124,7 +3154,7 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, if (rc) return rc; } - +no_lease_ctx: req->RequestedOplockLevel = *oplock; if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) { diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h index 035aa1624053..a208d7f78931 100644 --- a/fs/smb/client/smb2proto.h +++ b/fs/smb/client/smb2proto.h @@ -85,7 +85,7 @@ extern int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, struct cifs_sb_info *cifs_sb); extern int smb2_mkdir(const unsigned int xid, struct inode *inode, umode_t mode, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); + const char *name, struct cifs_sb_info *cifs_sb, struct dentry *dentry); extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path, struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, const unsigned int xid); |
