mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-18 20:04:16 +08:00
15 cifs/smb3 fixes including 2 for stable
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmNLLdMACgkQiiy9cAdy T1EWGwv/SN2nGgQ5QBxcqHmKyYyP/ndo3iig6L55VdBrWjTIH6vMAJfDAZwq5Veb YNkN6EQhT4XQ8RQ3nzJdSuwSLZu3CF5JMdChyoM3qgLSrT9CU3n2E5n0S/OK7TNI QvX2RMZrOiwl2sApJXk5xQgmocGwNOMdsI9IwxZ8zDATpLUicwYmcNajDTmPHjX7 W8IVh4hHTBmVpnN5WkKLninLCz23eqcEMQb+nxtD3sMhOAUGn6gkKo+0L1EOYwgS jFfZxGbLoIBi1IhMcoWEcODnDtpwS9vtGDy2ZPVkefp5LqjjEEqR7LQLAvSt0dYK MpXHAvTUO8T7EEDx2djUNVM0JfXYBa0w3iOqSkOS2EBZF/Q8/ABWccfNpCTnGoAZ G3VJvDP/K5iPiRwy/0KK6CeCvCiXgBslcvRVdIvSCsehuvEyYTXRrjRLHElZBVZz Odwn8C9UGy5w0DTQ6QvwYtYWpoi2UFm8GuoswOfljYwnrul+7P0R14xxARJE8TOh fBXL0FAu =8BkB -----END PGP SIGNATURE----- Merge tag '6.1-rc-smb3-client-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6 Pull more cifs updates from Steve French: - fix a regression in guest mounts to old servers - improvements to directory leasing (caching directory entries safely beyond the root directory) - symlink improvement (reducing roundtrips needed to process symlinks) - an lseek fix (to problem where some dir entries could be skipped) - improved ioctl for returning more detailed information on directory change notifications - clarify multichannel interface query warning - cleanup fix (for better aligning buffers using ALIGN and round_up) - a compounding fix - fix some uninitialized variable bugs found by Coverity and the kernel test robot * tag '6.1-rc-smb3-client-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6: smb3: improve SMB3 change notification support cifs: lease key is uninitialized in two additional functions when smb1 cifs: lease key is uninitialized in smb1 paths smb3: must initialize two ACL struct fields to zero cifs: fix double-fault crash during ntlmssp cifs: fix static checker warning cifs: use ALIGN() and round_up() macros cifs: find and use the dentry for cached non-root directories also cifs: enable caching of directories for which a lease is held cifs: prevent copying past input buffer boundaries cifs: fix uninitialised var in smb2_compound_op() cifs: improve symlink handling for smb2+ smb3: clarify multichannel warning cifs: fix regression in very old smb1 mounts cifs: fix skipping to incorrect offset in emit_cached_dirents
This commit is contained in:
commit
b08cd74448
@ -5,13 +5,98 @@
|
||||
* Copyright (c) 2022, Ronnie Sahlberg <lsahlber@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/namei.h>
|
||||
#include "cifsglob.h"
|
||||
#include "cifsproto.h"
|
||||
#include "cifs_debug.h"
|
||||
#include "smb2proto.h"
|
||||
#include "cached_dir.h"
|
||||
|
||||
struct cached_fid *init_cached_dir(const char *path);
|
||||
static struct cached_fid *init_cached_dir(const char *path);
|
||||
static void free_cached_dir(struct cached_fid *cfid);
|
||||
|
||||
static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
|
||||
const char *path,
|
||||
bool lookup_only)
|
||||
{
|
||||
struct cached_fid *cfid;
|
||||
|
||||
spin_lock(&cfids->cfid_list_lock);
|
||||
list_for_each_entry(cfid, &cfids->entries, entry) {
|
||||
if (!strcmp(cfid->path, path)) {
|
||||
/*
|
||||
* If it doesn't have a lease it is either not yet
|
||||
* fully cached or it may be in the process of
|
||||
* being deleted due to a lease break.
|
||||
*/
|
||||
if (!cfid->has_lease) {
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
return NULL;
|
||||
}
|
||||
kref_get(&cfid->refcount);
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
return cfid;
|
||||
}
|
||||
}
|
||||
if (lookup_only) {
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
return NULL;
|
||||
}
|
||||
if (cfids->num_entries >= MAX_CACHED_FIDS) {
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
return NULL;
|
||||
}
|
||||
cfid = init_cached_dir(path);
|
||||
if (cfid == NULL) {
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
return NULL;
|
||||
}
|
||||
cfid->cfids = cfids;
|
||||
cfids->num_entries++;
|
||||
list_add(&cfid->entry, &cfids->entries);
|
||||
cfid->on_list = true;
|
||||
kref_get(&cfid->refcount);
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
return cfid;
|
||||
}
|
||||
|
||||
static struct dentry *
|
||||
path_to_dentry(struct cifs_sb_info *cifs_sb, const char *path)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
const char *s, *p;
|
||||
char sep;
|
||||
|
||||
sep = CIFS_DIR_SEP(cifs_sb);
|
||||
dentry = dget(cifs_sb->root);
|
||||
s = path;
|
||||
|
||||
do {
|
||||
struct inode *dir = d_inode(dentry);
|
||||
struct dentry *child;
|
||||
|
||||
if (!S_ISDIR(dir->i_mode)) {
|
||||
dput(dentry);
|
||||
dentry = ERR_PTR(-ENOTDIR);
|
||||
break;
|
||||
}
|
||||
|
||||
/* skip separators */
|
||||
while (*s == sep)
|
||||
s++;
|
||||
if (!*s)
|
||||
break;
|
||||
p = s++;
|
||||
/* next separator */
|
||||
while (*s && *s != sep)
|
||||
s++;
|
||||
|
||||
child = lookup_positive_unlocked(p, dentry, s - p);
|
||||
dput(dentry);
|
||||
dentry = child;
|
||||
} while (!IS_ERR(dentry));
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the and cache a directory handle.
|
||||
@ -33,61 +118,57 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
|
||||
struct kvec qi_iov[1];
|
||||
int rc, flags = 0;
|
||||
__le16 utf16_path = 0; /* Null - since an open of top of share */
|
||||
__le16 *utf16_path = NULL;
|
||||
u8 oplock = SMB2_OPLOCK_LEVEL_II;
|
||||
struct cifs_fid *pfid;
|
||||
struct dentry *dentry;
|
||||
struct dentry *dentry = NULL;
|
||||
struct cached_fid *cfid;
|
||||
struct cached_fids *cfids;
|
||||
|
||||
if (tcon == NULL || tcon->nohandlecache ||
|
||||
if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache ||
|
||||
is_smb1_server(tcon->ses->server))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ses = tcon->ses;
|
||||
server = ses->server;
|
||||
cfids = tcon->cfids;
|
||||
|
||||
if (!server->ops->new_lease_key)
|
||||
return -EIO;
|
||||
|
||||
if (cifs_sb->root == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
if (!path[0])
|
||||
dentry = cifs_sb->root;
|
||||
else
|
||||
return -ENOENT;
|
||||
|
||||
cfid = tcon->cfids->cfid;
|
||||
if (cfid == NULL) {
|
||||
cfid = init_cached_dir(path);
|
||||
tcon->cfids->cfid = cfid;
|
||||
}
|
||||
if (cfid == NULL)
|
||||
utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
|
||||
if (!utf16_path)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&cfid->fid_mutex);
|
||||
if (cfid->is_valid) {
|
||||
cifs_dbg(FYI, "found a cached root file handle\n");
|
||||
cfid = find_or_create_cached_dir(cfids, path, lookup_only);
|
||||
if (cfid == NULL) {
|
||||
kfree(utf16_path);
|
||||
return -ENOENT;
|
||||
}
|
||||
/*
|
||||
* At this point we either have a lease already and we can just
|
||||
* return it. If not we are guaranteed to be the only thread accessing
|
||||
* this cfid.
|
||||
*/
|
||||
if (cfid->has_lease) {
|
||||
*ret_cfid = cfid;
|
||||
kref_get(&cfid->refcount);
|
||||
mutex_unlock(&cfid->fid_mutex);
|
||||
kfree(utf16_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We do not hold the lock for the open because in case
|
||||
* SMB2_open needs to reconnect, it will end up calling
|
||||
* cifs_mark_open_files_invalid() which takes the lock again
|
||||
* thus causing a deadlock
|
||||
* SMB2_open needs to reconnect.
|
||||
* This is safe because no other thread will be able to get a ref
|
||||
* to the cfid until we have finished opening the file and (possibly)
|
||||
* acquired a lease.
|
||||
*/
|
||||
mutex_unlock(&cfid->fid_mutex);
|
||||
|
||||
if (lookup_only)
|
||||
return -ENOENT;
|
||||
|
||||
if (smb3_encryption_required(tcon))
|
||||
flags |= CIFS_TRANSFORM_REQ;
|
||||
|
||||
if (!server->ops->new_lease_key)
|
||||
return -EIO;
|
||||
|
||||
pfid = &cfid->fid;
|
||||
server->ops->new_lease_key(pfid);
|
||||
|
||||
@ -108,7 +189,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
|
||||
oparms.reconnect = false;
|
||||
|
||||
rc = SMB2_open_init(tcon, server,
|
||||
&rqst[0], &oplock, &oparms, &utf16_path);
|
||||
&rqst[0], &oplock, &oparms, utf16_path);
|
||||
if (rc)
|
||||
goto oshr_free;
|
||||
smb2_set_next_command(tcon, &rqst[0]);
|
||||
@ -131,47 +212,13 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
|
||||
rc = compound_send_recv(xid, ses, server,
|
||||
flags, 2, rqst,
|
||||
resp_buftype, rsp_iov);
|
||||
mutex_lock(&cfid->fid_mutex);
|
||||
|
||||
/*
|
||||
* Now we need to check again as the cached root might have
|
||||
* been successfully re-opened from a concurrent process
|
||||
*/
|
||||
|
||||
if (cfid->is_valid) {
|
||||
/* work was already done */
|
||||
|
||||
/* stash fids for close() later */
|
||||
struct cifs_fid fid = {
|
||||
.persistent_fid = pfid->persistent_fid,
|
||||
.volatile_fid = pfid->volatile_fid,
|
||||
};
|
||||
|
||||
/*
|
||||
* caller expects this func to set the fid in cfid to valid
|
||||
* cached root, so increment the refcount.
|
||||
*/
|
||||
kref_get(&cfid->refcount);
|
||||
|
||||
mutex_unlock(&cfid->fid_mutex);
|
||||
|
||||
if (rc == 0) {
|
||||
/* close extra handle outside of crit sec */
|
||||
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
|
||||
}
|
||||
rc = 0;
|
||||
goto oshr_free;
|
||||
}
|
||||
|
||||
/* Cached root is still invalid, continue normaly */
|
||||
|
||||
if (rc) {
|
||||
if (rc == -EREMCHG) {
|
||||
tcon->need_reconnect = true;
|
||||
pr_warn_once("server share %s deleted\n",
|
||||
tcon->tree_name);
|
||||
}
|
||||
goto oshr_exit;
|
||||
goto oshr_free;
|
||||
}
|
||||
|
||||
atomic_inc(&tcon->num_remote_opens);
|
||||
@ -183,31 +230,18 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
|
||||
oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
|
||||
#endif /* CIFS_DEBUG2 */
|
||||
|
||||
cfid->tcon = tcon;
|
||||
cfid->is_valid = true;
|
||||
cfid->dentry = dentry;
|
||||
if (dentry)
|
||||
dget(dentry);
|
||||
kref_init(&cfid->refcount);
|
||||
if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE)
|
||||
goto oshr_free;
|
||||
|
||||
/* BB TBD check to see if oplock level check can be removed below */
|
||||
if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
|
||||
/*
|
||||
* See commit 2f94a3125b87. Increment the refcount when we
|
||||
* get a lease for root, release it if lease break occurs
|
||||
*/
|
||||
kref_get(&cfid->refcount);
|
||||
cfid->has_lease = true;
|
||||
smb2_parse_contexts(server, o_rsp,
|
||||
&oparms.fid->epoch,
|
||||
oparms.fid->lease_key, &oplock,
|
||||
NULL, NULL);
|
||||
} else
|
||||
goto oshr_exit;
|
||||
|
||||
smb2_parse_contexts(server, o_rsp,
|
||||
&oparms.fid->epoch,
|
||||
oparms.fid->lease_key, &oplock,
|
||||
NULL, NULL);
|
||||
|
||||
qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
|
||||
if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
|
||||
goto oshr_exit;
|
||||
goto oshr_free;
|
||||
if (!smb2_validate_and_copy_iov(
|
||||
le16_to_cpu(qi_rsp->OutputBufferOffset),
|
||||
sizeof(struct smb2_file_all_info),
|
||||
@ -215,15 +249,40 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
|
||||
(char *)&cfid->file_all_info))
|
||||
cfid->file_all_info_is_valid = true;
|
||||
|
||||
if (!path[0])
|
||||
dentry = dget(cifs_sb->root);
|
||||
else {
|
||||
dentry = path_to_dentry(cifs_sb, path);
|
||||
if (IS_ERR(dentry))
|
||||
goto oshr_free;
|
||||
}
|
||||
cfid->dentry = dentry;
|
||||
cfid->tcon = tcon;
|
||||
cfid->time = jiffies;
|
||||
cfid->is_open = true;
|
||||
cfid->has_lease = true;
|
||||
|
||||
oshr_exit:
|
||||
mutex_unlock(&cfid->fid_mutex);
|
||||
oshr_free:
|
||||
kfree(utf16_path);
|
||||
SMB2_open_free(&rqst[0]);
|
||||
SMB2_query_info_free(&rqst[1]);
|
||||
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
|
||||
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
|
||||
spin_lock(&cfids->cfid_list_lock);
|
||||
if (!cfid->has_lease) {
|
||||
if (cfid->on_list) {
|
||||
list_del(&cfid->entry);
|
||||
cfid->on_list = false;
|
||||
cfids->num_entries--;
|
||||
}
|
||||
rc = -ENOENT;
|
||||
}
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
if (rc) {
|
||||
free_cached_dir(cfid);
|
||||
cfid = NULL;
|
||||
}
|
||||
|
||||
if (rc == 0)
|
||||
*ret_cfid = cfid;
|
||||
|
||||
@ -235,20 +294,22 @@ int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
|
||||
struct cached_fid **ret_cfid)
|
||||
{
|
||||
struct cached_fid *cfid;
|
||||
struct cached_fids *cfids = tcon->cfids;
|
||||
|
||||
cfid = tcon->cfids->cfid;
|
||||
if (cfid == NULL)
|
||||
if (cfids == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
mutex_lock(&cfid->fid_mutex);
|
||||
if (cfid->dentry == dentry) {
|
||||
cifs_dbg(FYI, "found a cached root file handle by dentry\n");
|
||||
*ret_cfid = cfid;
|
||||
kref_get(&cfid->refcount);
|
||||
mutex_unlock(&cfid->fid_mutex);
|
||||
return 0;
|
||||
spin_lock(&cfids->cfid_list_lock);
|
||||
list_for_each_entry(cfid, &cfids->entries, entry) {
|
||||
if (dentry && cfid->dentry == dentry) {
|
||||
cifs_dbg(FYI, "found a cached root file handle by dentry\n");
|
||||
kref_get(&cfid->refcount);
|
||||
*ret_cfid = cfid;
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&cfid->fid_mutex);
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
@ -257,63 +318,29 @@ smb2_close_cached_fid(struct kref *ref)
|
||||
{
|
||||
struct cached_fid *cfid = container_of(ref, struct cached_fid,
|
||||
refcount);
|
||||
struct cached_dirent *dirent, *q;
|
||||
|
||||
if (cfid->is_valid) {
|
||||
cifs_dbg(FYI, "clear cached root file handle\n");
|
||||
spin_lock(&cfid->cfids->cfid_list_lock);
|
||||
if (cfid->on_list) {
|
||||
list_del(&cfid->entry);
|
||||
cfid->on_list = false;
|
||||
cfid->cfids->num_entries--;
|
||||
}
|
||||
spin_unlock(&cfid->cfids->cfid_list_lock);
|
||||
|
||||
dput(cfid->dentry);
|
||||
cfid->dentry = NULL;
|
||||
|
||||
if (cfid->is_open) {
|
||||
SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
|
||||
cfid->fid.volatile_fid);
|
||||
}
|
||||
|
||||
/*
|
||||
* We only check validity above to send SMB2_close,
|
||||
* but we still need to invalidate these entries
|
||||
* when this function is called
|
||||
*/
|
||||
cfid->is_valid = false;
|
||||
cfid->file_all_info_is_valid = false;
|
||||
cfid->has_lease = false;
|
||||
if (cfid->dentry) {
|
||||
dput(cfid->dentry);
|
||||
cfid->dentry = NULL;
|
||||
}
|
||||
/*
|
||||
* Delete all cached dirent names
|
||||
*/
|
||||
mutex_lock(&cfid->dirents.de_mutex);
|
||||
list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
|
||||
list_del(&dirent->entry);
|
||||
kfree(dirent->name);
|
||||
kfree(dirent);
|
||||
}
|
||||
cfid->dirents.is_valid = 0;
|
||||
cfid->dirents.is_failed = 0;
|
||||
cfid->dirents.ctx = NULL;
|
||||
cfid->dirents.pos = 0;
|
||||
mutex_unlock(&cfid->dirents.de_mutex);
|
||||
|
||||
free_cached_dir(cfid);
|
||||
}
|
||||
|
||||
void close_cached_dir(struct cached_fid *cfid)
|
||||
{
|
||||
mutex_lock(&cfid->fid_mutex);
|
||||
kref_put(&cfid->refcount, smb2_close_cached_fid);
|
||||
mutex_unlock(&cfid->fid_mutex);
|
||||
}
|
||||
|
||||
void close_cached_dir_lease_locked(struct cached_fid *cfid)
|
||||
{
|
||||
if (cfid->has_lease) {
|
||||
cfid->has_lease = false;
|
||||
kref_put(&cfid->refcount, smb2_close_cached_fid);
|
||||
}
|
||||
}
|
||||
|
||||
void close_cached_dir_lease(struct cached_fid *cfid)
|
||||
{
|
||||
mutex_lock(&cfid->fid_mutex);
|
||||
close_cached_dir_lease_locked(cfid);
|
||||
mutex_unlock(&cfid->fid_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -326,41 +353,62 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
|
||||
struct cached_fid *cfid;
|
||||
struct cifs_tcon *tcon;
|
||||
struct tcon_link *tlink;
|
||||
struct cached_fids *cfids;
|
||||
|
||||
for (node = rb_first(root); node; node = rb_next(node)) {
|
||||
tlink = rb_entry(node, struct tcon_link, tl_rbnode);
|
||||
tcon = tlink_tcon(tlink);
|
||||
if (IS_ERR(tcon))
|
||||
continue;
|
||||
cfid = tcon->cfids->cfid;
|
||||
if (cfid == NULL)
|
||||
cfids = tcon->cfids;
|
||||
if (cfids == NULL)
|
||||
continue;
|
||||
mutex_lock(&cfid->fid_mutex);
|
||||
if (cfid->dentry) {
|
||||
list_for_each_entry(cfid, &cfids->entries, entry) {
|
||||
dput(cfid->dentry);
|
||||
cfid->dentry = NULL;
|
||||
}
|
||||
mutex_unlock(&cfid->fid_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalidate and close all cached dirs when a TCON has been reset
|
||||
* Invalidate all cached dirs when a TCON has been reset
|
||||
* due to a session loss.
|
||||
*/
|
||||
void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
|
||||
{
|
||||
struct cached_fid *cfid = tcon->cfids->cfid;
|
||||
struct cached_fids *cfids = tcon->cfids;
|
||||
struct cached_fid *cfid, *q;
|
||||
struct list_head entry;
|
||||
|
||||
if (cfid == NULL)
|
||||
return;
|
||||
INIT_LIST_HEAD(&entry);
|
||||
spin_lock(&cfids->cfid_list_lock);
|
||||
list_for_each_entry_safe(cfid, q, &cfids->entries, entry) {
|
||||
list_del(&cfid->entry);
|
||||
list_add(&cfid->entry, &entry);
|
||||
cfids->num_entries--;
|
||||
cfid->is_open = false;
|
||||
/* To prevent race with smb2_cached_lease_break() */
|
||||
kref_get(&cfid->refcount);
|
||||
}
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
|
||||
mutex_lock(&cfid->fid_mutex);
|
||||
cfid->is_valid = false;
|
||||
/* cached handle is not valid, so SMB2_CLOSE won't be sent below */
|
||||
close_cached_dir_lease_locked(cfid);
|
||||
memset(&cfid->fid, 0, sizeof(struct cifs_fid));
|
||||
mutex_unlock(&cfid->fid_mutex);
|
||||
list_for_each_entry_safe(cfid, q, &entry, entry) {
|
||||
cfid->on_list = false;
|
||||
list_del(&cfid->entry);
|
||||
cancel_work_sync(&cfid->lease_break);
|
||||
if (cfid->has_lease) {
|
||||
/*
|
||||
* We lease was never cancelled from the server so we
|
||||
* need to drop the reference.
|
||||
*/
|
||||
spin_lock(&cfids->cfid_list_lock);
|
||||
cfid->has_lease = false;
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
kref_put(&cfid->refcount, smb2_close_cached_fid);
|
||||
}
|
||||
/* Drop the extra reference opened above*/
|
||||
kref_put(&cfid->refcount, smb2_close_cached_fid);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -369,51 +417,83 @@ smb2_cached_lease_break(struct work_struct *work)
|
||||
struct cached_fid *cfid = container_of(work,
|
||||
struct cached_fid, lease_break);
|
||||
|
||||
close_cached_dir_lease(cfid);
|
||||
spin_lock(&cfid->cfids->cfid_list_lock);
|
||||
cfid->has_lease = false;
|
||||
spin_unlock(&cfid->cfids->cfid_list_lock);
|
||||
kref_put(&cfid->refcount, smb2_close_cached_fid);
|
||||
}
|
||||
|
||||
int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
|
||||
{
|
||||
struct cached_fid *cfid = tcon->cfids->cfid;
|
||||
struct cached_fids *cfids = tcon->cfids;
|
||||
struct cached_fid *cfid;
|
||||
|
||||
if (cfid == NULL)
|
||||
if (cfids == NULL)
|
||||
return false;
|
||||
|
||||
if (cfid->is_valid &&
|
||||
!memcmp(lease_key,
|
||||
cfid->fid.lease_key,
|
||||
SMB2_LEASE_KEY_SIZE)) {
|
||||
cfid->time = 0;
|
||||
INIT_WORK(&cfid->lease_break,
|
||||
smb2_cached_lease_break);
|
||||
queue_work(cifsiod_wq,
|
||||
&cfid->lease_break);
|
||||
return true;
|
||||
spin_lock(&cfids->cfid_list_lock);
|
||||
list_for_each_entry(cfid, &cfids->entries, entry) {
|
||||
if (cfid->has_lease &&
|
||||
!memcmp(lease_key,
|
||||
cfid->fid.lease_key,
|
||||
SMB2_LEASE_KEY_SIZE)) {
|
||||
cfid->time = 0;
|
||||
/*
|
||||
* We found a lease remove it from the list
|
||||
* so no threads can access it.
|
||||
*/
|
||||
list_del(&cfid->entry);
|
||||
cfid->on_list = false;
|
||||
cfids->num_entries--;
|
||||
|
||||
queue_work(cifsiod_wq,
|
||||
&cfid->lease_break);
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct cached_fid *init_cached_dir(const char *path)
|
||||
static struct cached_fid *init_cached_dir(const char *path)
|
||||
{
|
||||
struct cached_fid *cfid;
|
||||
|
||||
cfid = kzalloc(sizeof(*cfid), GFP_KERNEL);
|
||||
cfid = kzalloc(sizeof(*cfid), GFP_ATOMIC);
|
||||
if (!cfid)
|
||||
return NULL;
|
||||
cfid->path = kstrdup(path, GFP_KERNEL);
|
||||
cfid->path = kstrdup(path, GFP_ATOMIC);
|
||||
if (!cfid->path) {
|
||||
kfree(cfid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_WORK(&cfid->lease_break, smb2_cached_lease_break);
|
||||
INIT_LIST_HEAD(&cfid->entry);
|
||||
INIT_LIST_HEAD(&cfid->dirents.entries);
|
||||
mutex_init(&cfid->dirents.de_mutex);
|
||||
mutex_init(&cfid->fid_mutex);
|
||||
spin_lock_init(&cfid->fid_lock);
|
||||
kref_init(&cfid->refcount);
|
||||
return cfid;
|
||||
}
|
||||
|
||||
void free_cached_dir(struct cached_fid *cfid)
|
||||
static void free_cached_dir(struct cached_fid *cfid)
|
||||
{
|
||||
struct cached_dirent *dirent, *q;
|
||||
|
||||
dput(cfid->dentry);
|
||||
cfid->dentry = NULL;
|
||||
|
||||
/*
|
||||
* Delete all cached dirent names
|
||||
*/
|
||||
list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
|
||||
list_del(&dirent->entry);
|
||||
kfree(dirent->name);
|
||||
kfree(dirent);
|
||||
}
|
||||
|
||||
kfree(cfid->path);
|
||||
cfid->path = NULL;
|
||||
kfree(cfid);
|
||||
@ -426,15 +506,34 @@ struct cached_fids *init_cached_dirs(void)
|
||||
cfids = kzalloc(sizeof(*cfids), GFP_KERNEL);
|
||||
if (!cfids)
|
||||
return NULL;
|
||||
mutex_init(&cfids->cfid_list_mutex);
|
||||
spin_lock_init(&cfids->cfid_list_lock);
|
||||
INIT_LIST_HEAD(&cfids->entries);
|
||||
return cfids;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from tconInfoFree when we are tearing down the tcon.
|
||||
* There are no active users or open files/directories at this point.
|
||||
*/
|
||||
void free_cached_dirs(struct cached_fids *cfids)
|
||||
{
|
||||
if (cfids->cfid) {
|
||||
free_cached_dir(cfids->cfid);
|
||||
cfids->cfid = NULL;
|
||||
struct cached_fid *cfid, *q;
|
||||
struct list_head entry;
|
||||
|
||||
INIT_LIST_HEAD(&entry);
|
||||
spin_lock(&cfids->cfid_list_lock);
|
||||
list_for_each_entry_safe(cfid, q, &cfids->entries, entry) {
|
||||
cfid->on_list = false;
|
||||
cfid->is_open = false;
|
||||
list_del(&cfid->entry);
|
||||
list_add(&cfid->entry, &entry);
|
||||
}
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
|
||||
list_for_each_entry_safe(cfid, q, &entry, entry) {
|
||||
list_del(&cfid->entry);
|
||||
free_cached_dir(cfid);
|
||||
}
|
||||
|
||||
kfree(cfids);
|
||||
}
|
||||
|
@ -31,14 +31,17 @@ struct cached_dirents {
|
||||
};
|
||||
|
||||
struct cached_fid {
|
||||
struct list_head entry;
|
||||
struct cached_fids *cfids;
|
||||
const char *path;
|
||||
bool is_valid:1; /* Do we have a useable root fid */
|
||||
bool file_all_info_is_valid:1;
|
||||
bool has_lease:1;
|
||||
bool is_open:1;
|
||||
bool on_list:1;
|
||||
bool file_all_info_is_valid:1;
|
||||
unsigned long time; /* jiffies of when lease was taken */
|
||||
struct kref refcount;
|
||||
struct cifs_fid fid;
|
||||
struct mutex fid_mutex;
|
||||
spinlock_t fid_lock;
|
||||
struct cifs_tcon *tcon;
|
||||
struct dentry *dentry;
|
||||
struct work_struct lease_break;
|
||||
@ -46,9 +49,14 @@ struct cached_fid {
|
||||
struct cached_dirents dirents;
|
||||
};
|
||||
|
||||
#define MAX_CACHED_FIDS 16
|
||||
struct cached_fids {
|
||||
struct mutex cfid_list_mutex;
|
||||
struct cached_fid *cfid;
|
||||
/* Must be held when:
|
||||
* - accessing the cfids->entries list
|
||||
*/
|
||||
spinlock_t cfid_list_lock;
|
||||
int num_entries;
|
||||
struct list_head entries;
|
||||
};
|
||||
|
||||
extern struct cached_fids *init_cached_dirs(void);
|
||||
@ -61,8 +69,6 @@ extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
|
||||
struct dentry *dentry,
|
||||
struct cached_fid **cfid);
|
||||
extern void close_cached_dir(struct cached_fid *cfid);
|
||||
extern void close_cached_dir_lease(struct cached_fid *cfid);
|
||||
extern void close_cached_dir_lease_locked(struct cached_fid *cfid);
|
||||
extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
|
||||
extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
|
||||
extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
|
||||
|
@ -91,6 +91,13 @@ struct smb3_notify {
|
||||
bool watch_tree;
|
||||
} __packed;
|
||||
|
||||
struct smb3_notify_info {
|
||||
__u32 completion_filter;
|
||||
bool watch_tree;
|
||||
__u32 data_len; /* size of notify data below */
|
||||
__u8 notify_data[];
|
||||
} __packed;
|
||||
|
||||
#define CIFS_IOCTL_MAGIC 0xCF
|
||||
#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
|
||||
#define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4)
|
||||
@ -100,6 +107,7 @@ struct smb3_notify {
|
||||
#define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info)
|
||||
#define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)
|
||||
#define CIFS_DUMP_FULL_KEY _IOWR(CIFS_IOCTL_MAGIC, 10, struct smb3_full_key_debug_info)
|
||||
#define CIFS_IOC_NOTIFY_INFO _IOWR(CIFS_IOCTL_MAGIC, 11, struct smb3_notify_info)
|
||||
#define CIFS_IOC_SHUTDOWN _IOR ('X', 125, __u32)
|
||||
|
||||
/*
|
||||
|
@ -396,6 +396,7 @@ cifs_alloc_inode(struct super_block *sb)
|
||||
cifs_inode->epoch = 0;
|
||||
spin_lock_init(&cifs_inode->open_file_lock);
|
||||
generate_random_uuid(cifs_inode->lease_key);
|
||||
cifs_inode->symlink_target = NULL;
|
||||
|
||||
/*
|
||||
* Can not set i_flags here - they get immediately overwritten to zero
|
||||
@ -412,7 +413,11 @@ cifs_alloc_inode(struct super_block *sb)
|
||||
static void
|
||||
cifs_free_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(cifs_inode_cachep, CIFS_I(inode));
|
||||
struct cifsInodeInfo *cinode = CIFS_I(inode);
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
kfree(cinode->symlink_target);
|
||||
kmem_cache_free(cifs_inode_cachep, cinode);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1139,7 +1144,7 @@ const struct inode_operations cifs_file_inode_ops = {
|
||||
};
|
||||
|
||||
const struct inode_operations cifs_symlink_inode_ops = {
|
||||
.get_link = cifs_get_link,
|
||||
.get_link = simple_get_link,
|
||||
.permission = cifs_permission,
|
||||
.listxattr = cifs_listxattr,
|
||||
};
|
||||
|
@ -185,6 +185,19 @@ struct cifs_cred {
|
||||
struct cifs_ace *aces;
|
||||
};
|
||||
|
||||
struct cifs_open_info_data {
|
||||
char *symlink_target;
|
||||
union {
|
||||
struct smb2_file_all_info fi;
|
||||
struct smb311_posix_qinfo posix_fi;
|
||||
};
|
||||
};
|
||||
|
||||
static inline void cifs_free_open_info(struct cifs_open_info_data *data)
|
||||
{
|
||||
kfree(data->symlink_target);
|
||||
}
|
||||
|
||||
/*
|
||||
*****************************************************************
|
||||
* Except the CIFS PDUs themselves all the
|
||||
@ -307,20 +320,20 @@ struct smb_version_operations {
|
||||
int (*is_path_accessible)(const unsigned int, struct cifs_tcon *,
|
||||
struct cifs_sb_info *, const char *);
|
||||
/* query path data from the server */
|
||||
int (*query_path_info)(const unsigned int, struct cifs_tcon *,
|
||||
struct cifs_sb_info *, const char *,
|
||||
FILE_ALL_INFO *, bool *, bool *);
|
||||
int (*query_path_info)(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse);
|
||||
/* query file data from the server */
|
||||
int (*query_file_info)(const unsigned int, struct cifs_tcon *,
|
||||
struct cifs_fid *, FILE_ALL_INFO *);
|
||||
int (*query_file_info)(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile, struct cifs_open_info_data *data);
|
||||
/* query reparse tag from srv to determine which type of special file */
|
||||
int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *path,
|
||||
__u32 *reparse_tag);
|
||||
/* get server index number */
|
||||
int (*get_srv_inum)(const unsigned int, struct cifs_tcon *,
|
||||
struct cifs_sb_info *, const char *,
|
||||
u64 *uniqueid, FILE_ALL_INFO *);
|
||||
int (*get_srv_inum)(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path, u64 *uniqueid,
|
||||
struct cifs_open_info_data *data);
|
||||
/* set size by path */
|
||||
int (*set_path_size)(const unsigned int, struct cifs_tcon *,
|
||||
const char *, __u64, struct cifs_sb_info *, bool);
|
||||
@ -369,8 +382,8 @@ struct smb_version_operations {
|
||||
struct cifs_sb_info *, const char *,
|
||||
char **, bool);
|
||||
/* open a file for non-posix mounts */
|
||||
int (*open)(const unsigned int, struct cifs_open_parms *,
|
||||
__u32 *, FILE_ALL_INFO *);
|
||||
int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
|
||||
void *buf);
|
||||
/* set fid protocol-specific info */
|
||||
void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
|
||||
/* close a file */
|
||||
@ -441,7 +454,7 @@ struct smb_version_operations {
|
||||
int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *src_file, void __user *);
|
||||
int (*notify)(const unsigned int xid, struct file *pfile,
|
||||
void __user *pbuf);
|
||||
void __user *pbuf, bool return_changes);
|
||||
int (*query_mf_symlink)(unsigned int, struct cifs_tcon *,
|
||||
struct cifs_sb_info *, const unsigned char *,
|
||||
char *, unsigned int *);
|
||||
@ -1123,6 +1136,7 @@ struct cifs_fattr {
|
||||
struct timespec64 cf_mtime;
|
||||
struct timespec64 cf_ctime;
|
||||
u32 cf_cifstag;
|
||||
char *cf_symlink_target;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1385,6 +1399,7 @@ struct cifsFileInfo {
|
||||
struct work_struct put; /* work for the final part of _put */
|
||||
struct delayed_work deferred;
|
||||
bool deferred_close_scheduled; /* Flag to indicate close is scheduled */
|
||||
char *symlink_target;
|
||||
};
|
||||
|
||||
struct cifs_io_parms {
|
||||
@ -1543,6 +1558,7 @@ struct cifsInodeInfo {
|
||||
struct list_head deferred_closes; /* list of deferred closes */
|
||||
spinlock_t deferred_lock; /* protection on deferred list */
|
||||
bool lease_granted; /* Flag to indicate whether lease or oplock is granted. */
|
||||
char *symlink_target;
|
||||
};
|
||||
|
||||
static inline struct cifsInodeInfo *
|
||||
@ -2111,4 +2127,14 @@ static inline size_t ntlmssp_workstation_name_size(const struct cifs_ses *ses)
|
||||
return sizeof(ses->workstation_name);
|
||||
}
|
||||
|
||||
static inline void move_cifs_info_to_smb2(struct smb2_file_all_info *dst, const FILE_ALL_INFO *src)
|
||||
{
|
||||
memcpy(dst, src, (size_t)((u8 *)&src->AccessFlags - (u8 *)src));
|
||||
dst->AccessFlags = src->AccessFlags;
|
||||
dst->CurrentByteOffset = src->CurrentByteOffset;
|
||||
dst->Mode = src->Mode;
|
||||
dst->AlignmentRequirement = src->AlignmentRequirement;
|
||||
dst->FileNameLength = src->FileNameLength;
|
||||
}
|
||||
|
||||
#endif /* _CIFS_GLOB_H */
|
||||
|
@ -182,10 +182,9 @@ extern int cifs_unlock_range(struct cifsFileInfo *cfile,
|
||||
extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile);
|
||||
|
||||
extern void cifs_down_write(struct rw_semaphore *sem);
|
||||
extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid,
|
||||
struct file *file,
|
||||
struct tcon_link *tlink,
|
||||
__u32 oplock);
|
||||
struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||
struct tcon_link *tlink, __u32 oplock,
|
||||
const char *symlink_target);
|
||||
extern int cifs_posix_open(const char *full_path, struct inode **inode,
|
||||
struct super_block *sb, int mode,
|
||||
unsigned int f_flags, __u32 *oplock, __u16 *netfid,
|
||||
@ -200,9 +199,9 @@ 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);
|
||||
|
||||
extern int cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
FILE_ALL_INFO *data, struct super_block *sb,
|
||||
int xid, const struct cifs_fid *fid);
|
||||
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);
|
||||
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
|
||||
struct super_block *sb, unsigned int xid);
|
||||
extern int cifs_get_inode_info_unix(struct inode **pinode,
|
||||
|
@ -2305,7 +2305,7 @@ int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon,
|
||||
remap);
|
||||
}
|
||||
rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
|
||||
count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
|
||||
count = sizeof(struct set_file_rename) + (2 * len_of_str);
|
||||
byte_count += count;
|
||||
pSMB->DataCount = cpu_to_le16(count);
|
||||
pSMB->TotalDataCount = pSMB->DataCount;
|
||||
|
@ -2832,9 +2832,12 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
|
||||
* sessinit is sent but no second negprot
|
||||
*/
|
||||
struct rfc1002_session_packet *ses_init_buf;
|
||||
unsigned int req_noscope_len;
|
||||
struct smb_hdr *smb_buf;
|
||||
|
||||
ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (ses_init_buf) {
|
||||
ses_init_buf->trailer.session_req.called_len = 32;
|
||||
|
||||
@ -2870,8 +2873,12 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
|
||||
ses_init_buf->trailer.session_req.scope2 = 0;
|
||||
smb_buf = (struct smb_hdr *)ses_init_buf;
|
||||
|
||||
/* sizeof RFC1002_SESSION_REQUEST with no scope */
|
||||
smb_buf->smb_buf_length = cpu_to_be32(0x81000044);
|
||||
/* sizeof RFC1002_SESSION_REQUEST with no scopes */
|
||||
req_noscope_len = sizeof(struct rfc1002_session_packet) - 2;
|
||||
|
||||
/* == cpu_to_be32(0x81000044) */
|
||||
smb_buf->smb_buf_length =
|
||||
cpu_to_be32((RFC1002_SESSION_REQUEST << 24) | req_noscope_len);
|
||||
rc = smb_send(server, smb_buf, 0x44);
|
||||
kfree(ses_init_buf);
|
||||
/*
|
||||
@ -3922,12 +3929,11 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
|
||||
pSMB->AndXCommand = 0xFF;
|
||||
pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO);
|
||||
bcc_ptr = &pSMB->Password[0];
|
||||
if (tcon->pipe || (ses->server->sec_mode & SECMODE_USER)) {
|
||||
pSMB->PasswordLength = cpu_to_le16(1); /* minimum */
|
||||
*bcc_ptr = 0; /* password is null byte */
|
||||
bcc_ptr++; /* skip password */
|
||||
/* already aligned so no need to do it below */
|
||||
}
|
||||
|
||||
pSMB->PasswordLength = cpu_to_le16(1); /* minimum */
|
||||
*bcc_ptr = 0; /* password is null byte */
|
||||
bcc_ptr++; /* skip password */
|
||||
/* already aligned so no need to do it below */
|
||||
|
||||
if (ses->server->sign)
|
||||
smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
|
||||
|
@ -165,10 +165,9 @@ check_name(struct dentry *direntry, struct cifs_tcon *tcon)
|
||||
|
||||
/* Inode operations in similar order to how they appear in Linux file fs.h */
|
||||
|
||||
static int
|
||||
cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
||||
struct tcon_link *tlink, unsigned oflags, umode_t mode,
|
||||
__u32 *oplock, struct cifs_fid *fid)
|
||||
static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
||||
struct tcon_link *tlink, unsigned int oflags, umode_t mode, __u32 *oplock,
|
||||
struct cifs_fid *fid, struct cifs_open_info_data *buf)
|
||||
{
|
||||
int rc = -ENOENT;
|
||||
int create_options = CREATE_NOT_DIR;
|
||||
@ -177,7 +176,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
||||
struct cifs_tcon *tcon = tlink_tcon(tlink);
|
||||
const char *full_path;
|
||||
void *page = alloc_dentry_path();
|
||||
FILE_ALL_INFO *buf = NULL;
|
||||
struct inode *newinode = NULL;
|
||||
int disposition;
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
@ -290,12 +288,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* if we're not using unix extensions, see if we need to set
|
||||
* ATTR_READONLY on the create call
|
||||
@ -364,8 +356,7 @@ cifs_create_get_file_info:
|
||||
{
|
||||
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
|
||||
/* TODO: Add support for calling POSIX query info here, but passing in fid */
|
||||
rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb,
|
||||
xid, fid);
|
||||
rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, fid);
|
||||
if (newinode) {
|
||||
if (server->ops->set_lease_key)
|
||||
server->ops->set_lease_key(newinode, fid);
|
||||
@ -402,7 +393,6 @@ cifs_create_set_dentry:
|
||||
d_add(direntry, newinode);
|
||||
|
||||
out:
|
||||
kfree(buf);
|
||||
free_dentry_path(page);
|
||||
return rc;
|
||||
|
||||
@ -423,10 +413,11 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
|
||||
struct tcon_link *tlink;
|
||||
struct cifs_tcon *tcon;
|
||||
struct TCP_Server_Info *server;
|
||||
struct cifs_fid fid;
|
||||
struct cifs_fid fid = {};
|
||||
struct cifs_pending_open open;
|
||||
__u32 oplock;
|
||||
struct cifsFileInfo *file_info;
|
||||
struct cifs_open_info_data buf = {};
|
||||
|
||||
if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb))))
|
||||
return -EIO;
|
||||
@ -484,8 +475,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
|
||||
cifs_add_pending_open(&fid, tlink, &open);
|
||||
|
||||
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
|
||||
&oplock, &fid);
|
||||
|
||||
&oplock, &fid, &buf);
|
||||
if (rc) {
|
||||
cifs_del_pending_open(&open);
|
||||
goto out;
|
||||
@ -510,7 +500,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
|
||||
file->f_op = &cifs_file_direct_ops;
|
||||
}
|
||||
|
||||
file_info = cifs_new_fileinfo(&fid, file, tlink, oplock);
|
||||
file_info = cifs_new_fileinfo(&fid, file, tlink, oplock, buf.symlink_target);
|
||||
if (file_info == NULL) {
|
||||
if (server->ops->close)
|
||||
server->ops->close(xid, tcon, &fid);
|
||||
@ -526,6 +516,7 @@ out:
|
||||
cifs_put_tlink(tlink);
|
||||
out_free_xid:
|
||||
free_xid(xid);
|
||||
cifs_free_open_info(&buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -547,6 +538,7 @@ int cifs_create(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
struct TCP_Server_Info *server;
|
||||
struct cifs_fid fid;
|
||||
__u32 oplock;
|
||||
struct cifs_open_info_data buf = {};
|
||||
|
||||
cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
|
||||
inode, direntry, direntry);
|
||||
@ -565,11 +557,11 @@ int cifs_create(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
if (server->ops->new_lease_key)
|
||||
server->ops->new_lease_key(&fid);
|
||||
|
||||
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
|
||||
&oplock, &fid);
|
||||
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, &oplock, &fid, &buf);
|
||||
if (!rc && server->ops->close)
|
||||
server->ops->close(xid, tcon, &fid);
|
||||
|
||||
cifs_free_open_info(&buf);
|
||||
cifs_put_tlink(tlink);
|
||||
out_free_xid:
|
||||
free_xid(xid);
|
||||
|
@ -209,16 +209,14 @@ posix_open_ret:
|
||||
}
|
||||
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
|
||||
|
||||
static int
|
||||
cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
|
||||
struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock,
|
||||
struct cifs_fid *fid, unsigned int xid)
|
||||
static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
|
||||
struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock,
|
||||
struct cifs_fid *fid, unsigned int xid, struct cifs_open_info_data *buf)
|
||||
{
|
||||
int rc;
|
||||
int desired_access;
|
||||
int disposition;
|
||||
int create_options = CREATE_NOT_DIR;
|
||||
FILE_ALL_INFO *buf;
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
struct cifs_open_parms oparms;
|
||||
|
||||
@ -255,10 +253,6 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci
|
||||
|
||||
/* BB pass O_SYNC flag through on file attributes .. BB */
|
||||
|
||||
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* O_SYNC also has bit for O_DSYNC so following check picks up either */
|
||||
if (f_flags & O_SYNC)
|
||||
create_options |= CREATE_WRITE_THROUGH;
|
||||
@ -276,9 +270,8 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci
|
||||
oparms.reconnect = false;
|
||||
|
||||
rc = server->ops->open(xid, &oparms, oplock, buf);
|
||||
|
||||
if (rc)
|
||||
goto out;
|
||||
return rc;
|
||||
|
||||
/* TODO: Add support for calling posix query info but with passing in fid */
|
||||
if (tcon->unix_ext)
|
||||
@ -294,8 +287,6 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci
|
||||
rc = -EOPENSTALE;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -325,9 +316,9 @@ cifs_down_write(struct rw_semaphore *sem)
|
||||
|
||||
static void cifsFileInfo_put_work(struct work_struct *work);
|
||||
|
||||
struct cifsFileInfo *
|
||||
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||
struct tcon_link *tlink, __u32 oplock)
|
||||
struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||
struct tcon_link *tlink, __u32 oplock,
|
||||
const char *symlink_target)
|
||||
{
|
||||
struct dentry *dentry = file_dentry(file);
|
||||
struct inode *inode = d_inode(dentry);
|
||||
@ -347,6 +338,15 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (symlink_target) {
|
||||
cfile->symlink_target = kstrdup(symlink_target, GFP_KERNEL);
|
||||
if (!cfile->symlink_target) {
|
||||
kfree(fdlocks);
|
||||
kfree(cfile);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&fdlocks->locks);
|
||||
fdlocks->cfile = cfile;
|
||||
cfile->llist = fdlocks;
|
||||
@ -440,6 +440,7 @@ static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file)
|
||||
cifs_put_tlink(cifs_file->tlink);
|
||||
dput(cifs_file->dentry);
|
||||
cifs_sb_deactive(sb);
|
||||
kfree(cifs_file->symlink_target);
|
||||
kfree(cifs_file);
|
||||
}
|
||||
|
||||
@ -488,7 +489,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
|
||||
struct cifsInodeInfo *cifsi = CIFS_I(inode);
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
struct cifs_fid fid;
|
||||
struct cifs_fid fid = {};
|
||||
struct cifs_pending_open open;
|
||||
bool oplock_break_cancelled;
|
||||
|
||||
@ -570,8 +571,9 @@ int cifs_open(struct inode *inode, struct file *file)
|
||||
void *page;
|
||||
const char *full_path;
|
||||
bool posix_open_ok = false;
|
||||
struct cifs_fid fid;
|
||||
struct cifs_fid fid = {};
|
||||
struct cifs_pending_open open;
|
||||
struct cifs_open_info_data data = {};
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
@ -662,15 +664,15 @@ int cifs_open(struct inode *inode, struct file *file)
|
||||
if (server->ops->get_lease_key)
|
||||
server->ops->get_lease_key(inode, &fid);
|
||||
|
||||
rc = cifs_nt_open(full_path, inode, cifs_sb, tcon,
|
||||
file->f_flags, &oplock, &fid, xid);
|
||||
rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, file->f_flags, &oplock, &fid,
|
||||
xid, &data);
|
||||
if (rc) {
|
||||
cifs_del_pending_open(&open);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
cfile = cifs_new_fileinfo(&fid, file, tlink, oplock);
|
||||
cfile = cifs_new_fileinfo(&fid, file, tlink, oplock, data.symlink_target);
|
||||
if (cfile == NULL) {
|
||||
if (server->ops->close)
|
||||
server->ops->close(xid, tcon, &fid);
|
||||
@ -712,6 +714,7 @@ out:
|
||||
free_dentry_path(page);
|
||||
free_xid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
cifs_free_open_info(&data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
176
fs/cifs/inode.c
176
fs/cifs/inode.c
@ -210,6 +210,17 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
|
||||
*/
|
||||
inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9;
|
||||
}
|
||||
|
||||
if (S_ISLNK(fattr->cf_mode)) {
|
||||
kfree(cifs_i->symlink_target);
|
||||
cifs_i->symlink_target = fattr->cf_symlink_target;
|
||||
fattr->cf_symlink_target = NULL;
|
||||
|
||||
if (unlikely(!cifs_i->symlink_target))
|
||||
inode->i_link = ERR_PTR(-EOPNOTSUPP);
|
||||
else
|
||||
inode->i_link = cifs_i->symlink_target;
|
||||
}
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
if (fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL)
|
||||
@ -347,13 +358,20 @@ cifs_get_file_info_unix(struct file *filp)
|
||||
int rc;
|
||||
unsigned int xid;
|
||||
FILE_UNIX_BASIC_INFO find_data;
|
||||
struct cifs_fattr fattr;
|
||||
struct cifs_fattr fattr = {};
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
struct cifsFileInfo *cfile = filp->private_data;
|
||||
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
if (cfile->symlink_target) {
|
||||
fattr.cf_symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
|
||||
if (!fattr.cf_symlink_target)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->fid.netfid, &find_data);
|
||||
if (!rc) {
|
||||
cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
|
||||
@ -378,6 +396,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
|
||||
FILE_UNIX_BASIC_INFO find_data;
|
||||
struct cifs_fattr fattr;
|
||||
struct cifs_tcon *tcon;
|
||||
struct TCP_Server_Info *server;
|
||||
struct tcon_link *tlink;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
|
||||
@ -387,10 +406,12 @@ int cifs_get_inode_info_unix(struct inode **pinode,
|
||||
if (IS_ERR(tlink))
|
||||
return PTR_ERR(tlink);
|
||||
tcon = tlink_tcon(tlink);
|
||||
server = tcon->ses->server;
|
||||
|
||||
/* could have done a find first instead but this returns more info */
|
||||
rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data,
|
||||
cifs_sb->local_nls, cifs_remap(cifs_sb));
|
||||
cifs_dbg(FYI, "%s: query path info: rc = %d\n", __func__, rc);
|
||||
cifs_put_tlink(tlink);
|
||||
|
||||
if (!rc) {
|
||||
@ -410,6 +431,17 @@ int cifs_get_inode_info_unix(struct inode **pinode,
|
||||
cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
|
||||
}
|
||||
|
||||
if (S_ISLNK(fattr.cf_mode) && !fattr.cf_symlink_target) {
|
||||
if (!server->ops->query_symlink)
|
||||
return -EOPNOTSUPP;
|
||||
rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
|
||||
&fattr.cf_symlink_target, false);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc);
|
||||
goto cgiiu_exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (*pinode == NULL) {
|
||||
/* get new inode */
|
||||
cifs_fill_uniqueid(sb, &fattr);
|
||||
@ -432,6 +464,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
|
||||
}
|
||||
|
||||
cgiiu_exit:
|
||||
kfree(fattr.cf_symlink_target);
|
||||
return rc;
|
||||
}
|
||||
#else
|
||||
@ -601,10 +634,10 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
|
||||
}
|
||||
|
||||
/* Fill a cifs_fattr struct with info from POSIX info struct */
|
||||
static void
|
||||
smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct smb311_posix_qinfo *info,
|
||||
struct super_block *sb, bool adjust_tz, bool symlink)
|
||||
static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data,
|
||||
struct super_block *sb, bool adjust_tz, bool symlink)
|
||||
{
|
||||
struct smb311_posix_qinfo *info = &data->posix_fi;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
|
||||
@ -639,6 +672,8 @@ smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct smb311_posix_qinfo *
|
||||
if (symlink) {
|
||||
fattr->cf_mode |= S_IFLNK;
|
||||
fattr->cf_dtype = DT_LNK;
|
||||
fattr->cf_symlink_target = data->symlink_target;
|
||||
data->symlink_target = NULL;
|
||||
} else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
|
||||
fattr->cf_mode |= S_IFDIR;
|
||||
fattr->cf_dtype = DT_DIR;
|
||||
@ -655,13 +690,11 @@ smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct smb311_posix_qinfo *
|
||||
fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
|
||||
}
|
||||
|
||||
|
||||
/* Fill a cifs_fattr struct with info from FILE_ALL_INFO */
|
||||
static void
|
||||
cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
|
||||
struct super_block *sb, bool adjust_tz,
|
||||
bool symlink, u32 reparse_tag)
|
||||
static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data,
|
||||
struct super_block *sb, bool adjust_tz, bool symlink,
|
||||
u32 reparse_tag)
|
||||
{
|
||||
struct smb2_file_all_info *info = &data->fi;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
|
||||
@ -703,7 +736,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
|
||||
} else if (reparse_tag == IO_REPARSE_TAG_LX_BLK) {
|
||||
fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_dtype = DT_BLK;
|
||||
} else if (symlink) { /* TODO add more reparse tag checks */
|
||||
} else if (symlink || reparse_tag == IO_REPARSE_TAG_SYMLINK ||
|
||||
reparse_tag == IO_REPARSE_TAG_NFS) {
|
||||
fattr->cf_mode = S_IFLNK;
|
||||
fattr->cf_dtype = DT_LNK;
|
||||
} else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
|
||||
@ -735,6 +769,11 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
|
||||
}
|
||||
}
|
||||
|
||||
if (S_ISLNK(fattr->cf_mode)) {
|
||||
fattr->cf_symlink_target = data->symlink_target;
|
||||
data->symlink_target = NULL;
|
||||
}
|
||||
|
||||
fattr->cf_uid = cifs_sb->ctx->linux_uid;
|
||||
fattr->cf_gid = cifs_sb->ctx->linux_gid;
|
||||
}
|
||||
@ -744,23 +783,28 @@ cifs_get_file_info(struct file *filp)
|
||||
{
|
||||
int rc;
|
||||
unsigned int xid;
|
||||
FILE_ALL_INFO find_data;
|
||||
struct cifs_open_info_data data = {};
|
||||
struct cifs_fattr fattr;
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct cifsFileInfo *cfile = filp->private_data;
|
||||
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
bool symlink = false;
|
||||
u32 tag = 0;
|
||||
|
||||
if (!server->ops->query_file_info)
|
||||
return -ENOSYS;
|
||||
|
||||
xid = get_xid();
|
||||
rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data);
|
||||
rc = server->ops->query_file_info(xid, tcon, cfile, &data);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
/* TODO: add support to query reparse tag */
|
||||
cifs_all_info_to_fattr(&fattr, &find_data, inode->i_sb, false,
|
||||
false, 0 /* no reparse tag */);
|
||||
if (data.symlink_target) {
|
||||
symlink = true;
|
||||
tag = IO_REPARSE_TAG_SYMLINK;
|
||||
}
|
||||
cifs_open_info_to_fattr(&fattr, &data, inode->i_sb, false, symlink, tag);
|
||||
break;
|
||||
case -EREMOTE:
|
||||
cifs_create_dfs_fattr(&fattr, inode->i_sb);
|
||||
@ -789,6 +833,7 @@ cifs_get_file_info(struct file *filp)
|
||||
/* if filetype is different, return error */
|
||||
rc = cifs_fattr_to_inode(inode, &fattr);
|
||||
cgfi_exit:
|
||||
cifs_free_open_info(&data);
|
||||
free_xid(xid);
|
||||
return rc;
|
||||
}
|
||||
@ -860,14 +905,9 @@ cifs_backup_query_path_info(int xid,
|
||||
}
|
||||
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
|
||||
|
||||
static void
|
||||
cifs_set_fattr_ino(int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct super_block *sb,
|
||||
struct inode **inode,
|
||||
const char *full_path,
|
||||
FILE_ALL_INFO *data,
|
||||
struct cifs_fattr *fattr)
|
||||
static void cifs_set_fattr_ino(int xid, struct cifs_tcon *tcon, struct super_block *sb,
|
||||
struct inode **inode, const char *full_path,
|
||||
struct cifs_open_info_data *data, struct cifs_fattr *fattr)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
@ -885,11 +925,8 @@ cifs_set_fattr_ino(int xid,
|
||||
* If we have an inode pass a NULL tcon to ensure we don't
|
||||
* make a round trip to the server. This only works for SMB2+.
|
||||
*/
|
||||
rc = server->ops->get_srv_inum(xid,
|
||||
*inode ? NULL : tcon,
|
||||
cifs_sb, full_path,
|
||||
&fattr->cf_uniqueid,
|
||||
data);
|
||||
rc = server->ops->get_srv_inum(xid, *inode ? NULL : tcon, cifs_sb, full_path,
|
||||
&fattr->cf_uniqueid, data);
|
||||
if (rc) {
|
||||
/*
|
||||
* If that fails reuse existing ino or generate one
|
||||
@ -923,14 +960,10 @@ static inline bool is_inode_cache_good(struct inode *ino)
|
||||
return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0;
|
||||
}
|
||||
|
||||
int
|
||||
cifs_get_inode_info(struct inode **inode,
|
||||
const char *full_path,
|
||||
FILE_ALL_INFO *in_data,
|
||||
struct super_block *sb, int xid,
|
||||
const struct cifs_fid *fid)
|
||||
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)
|
||||
{
|
||||
|
||||
struct cifs_tcon *tcon;
|
||||
struct TCP_Server_Info *server;
|
||||
struct tcon_link *tlink;
|
||||
@ -938,8 +971,7 @@ cifs_get_inode_info(struct inode **inode,
|
||||
bool adjust_tz = false;
|
||||
struct cifs_fattr fattr = {0};
|
||||
bool is_reparse_point = false;
|
||||
FILE_ALL_INFO *data = in_data;
|
||||
FILE_ALL_INFO *tmp_data = NULL;
|
||||
struct cifs_open_info_data tmp_data = {};
|
||||
void *smb1_backup_rsp_buf = NULL;
|
||||
int rc = 0;
|
||||
int tmprc = 0;
|
||||
@ -960,21 +992,15 @@ cifs_get_inode_info(struct inode **inode,
|
||||
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
|
||||
goto out;
|
||||
}
|
||||
tmp_data = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
||||
if (!tmp_data) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
rc = server->ops->query_path_info(xid, tcon, cifs_sb,
|
||||
full_path, tmp_data,
|
||||
&adjust_tz, &is_reparse_point);
|
||||
rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, &tmp_data,
|
||||
&adjust_tz, &is_reparse_point);
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
if (rc == -ENOENT && is_tcon_dfs(tcon))
|
||||
rc = cifs_dfs_query_info_nonascii_quirk(xid, tcon,
|
||||
cifs_sb,
|
||||
full_path);
|
||||
#endif
|
||||
data = tmp_data;
|
||||
data = &tmp_data;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -988,14 +1014,24 @@ cifs_get_inode_info(struct inode **inode,
|
||||
* since we have to check if its reparse tag matches a known
|
||||
* special file type e.g. symlink or fifo or char etc.
|
||||
*/
|
||||
if ((le32_to_cpu(data->Attributes) & ATTR_REPARSE) &&
|
||||
server->ops->query_reparse_tag) {
|
||||
rc = server->ops->query_reparse_tag(xid, tcon, cifs_sb,
|
||||
full_path, &reparse_tag);
|
||||
cifs_dbg(FYI, "reparse tag 0x%x\n", reparse_tag);
|
||||
if (is_reparse_point && data->symlink_target) {
|
||||
reparse_tag = IO_REPARSE_TAG_SYMLINK;
|
||||
} else if ((le32_to_cpu(data->fi.Attributes) & ATTR_REPARSE) &&
|
||||
server->ops->query_reparse_tag) {
|
||||
tmprc = server->ops->query_reparse_tag(xid, tcon, cifs_sb, full_path,
|
||||
&reparse_tag);
|
||||
if (tmprc)
|
||||
cifs_dbg(FYI, "%s: query_reparse_tag: rc = %d\n", __func__, tmprc);
|
||||
if (server->ops->query_symlink) {
|
||||
tmprc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
|
||||
&data->symlink_target,
|
||||
is_reparse_point);
|
||||
if (tmprc)
|
||||
cifs_dbg(FYI, "%s: query_symlink: rc = %d\n", __func__,
|
||||
tmprc);
|
||||
}
|
||||
}
|
||||
cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz,
|
||||
is_reparse_point, reparse_tag);
|
||||
cifs_open_info_to_fattr(&fattr, data, sb, adjust_tz, is_reparse_point, reparse_tag);
|
||||
break;
|
||||
case -EREMOTE:
|
||||
/* DFS link, no metadata available on this server */
|
||||
@ -1014,18 +1050,20 @@ cifs_get_inode_info(struct inode **inode,
|
||||
*/
|
||||
if (backup_cred(cifs_sb) && is_smb1_server(server)) {
|
||||
/* for easier reading */
|
||||
FILE_ALL_INFO *fi;
|
||||
FILE_DIRECTORY_INFO *fdi;
|
||||
SEARCH_ID_FULL_DIR_INFO *si;
|
||||
|
||||
rc = cifs_backup_query_path_info(xid, tcon, sb,
|
||||
full_path,
|
||||
&smb1_backup_rsp_buf,
|
||||
&data);
|
||||
&fi);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
fdi = (FILE_DIRECTORY_INFO *)data;
|
||||
si = (SEARCH_ID_FULL_DIR_INFO *)data;
|
||||
move_cifs_info_to_smb2(&data->fi, fi);
|
||||
fdi = (FILE_DIRECTORY_INFO *)fi;
|
||||
si = (SEARCH_ID_FULL_DIR_INFO *)fi;
|
||||
|
||||
cifs_dir_info_to_fattr(&fattr, fdi, cifs_sb);
|
||||
fattr.cf_uniqueid = le64_to_cpu(si->UniqueId);
|
||||
@ -1123,7 +1161,8 @@ handle_mnt_opt:
|
||||
out:
|
||||
cifs_buf_release(smb1_backup_rsp_buf);
|
||||
cifs_put_tlink(tlink);
|
||||
kfree(tmp_data);
|
||||
cifs_free_open_info(&tmp_data);
|
||||
kfree(fattr.cf_symlink_target);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -1138,7 +1177,7 @@ smb311_posix_get_inode_info(struct inode **inode,
|
||||
bool adjust_tz = false;
|
||||
struct cifs_fattr fattr = {0};
|
||||
bool symlink = false;
|
||||
struct smb311_posix_qinfo *data = NULL;
|
||||
struct cifs_open_info_data data = {};
|
||||
int rc = 0;
|
||||
int tmprc = 0;
|
||||
|
||||
@ -1155,15 +1194,9 @@ smb311_posix_get_inode_info(struct inode **inode,
|
||||
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
|
||||
goto out;
|
||||
}
|
||||
data = kmalloc(sizeof(struct smb311_posix_qinfo), GFP_KERNEL);
|
||||
if (!data) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
|
||||
full_path, data,
|
||||
&adjust_tz, &symlink);
|
||||
rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, full_path, &data, &adjust_tz,
|
||||
&symlink);
|
||||
|
||||
/*
|
||||
* 2. Convert it to internal cifs metadata (fattr)
|
||||
@ -1171,7 +1204,7 @@ smb311_posix_get_inode_info(struct inode **inode,
|
||||
|
||||
switch (rc) {
|
||||
case 0:
|
||||
smb311_posix_info_to_fattr(&fattr, data, sb, adjust_tz, symlink);
|
||||
smb311_posix_info_to_fattr(&fattr, &data, sb, adjust_tz, symlink);
|
||||
break;
|
||||
case -EREMOTE:
|
||||
/* DFS link, no metadata available on this server */
|
||||
@ -1228,7 +1261,8 @@ smb311_posix_get_inode_info(struct inode **inode,
|
||||
}
|
||||
out:
|
||||
cifs_put_tlink(tlink);
|
||||
kfree(data);
|
||||
cifs_free_open_info(&data);
|
||||
kfree(fattr.cf_symlink_target);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -2265,13 +2299,13 @@ cifs_dentry_needs_reval(struct dentry *dentry)
|
||||
return true;
|
||||
|
||||
if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) {
|
||||
mutex_lock(&cfid->fid_mutex);
|
||||
spin_lock(&cfid->fid_lock);
|
||||
if (cfid->time && cifs_i->time > cfid->time) {
|
||||
mutex_unlock(&cfid->fid_mutex);
|
||||
spin_unlock(&cfid->fid_lock);
|
||||
close_cached_dir(cfid);
|
||||
return false;
|
||||
}
|
||||
mutex_unlock(&cfid->fid_mutex);
|
||||
spin_unlock(&cfid->fid_lock);
|
||||
close_cached_dir(cfid);
|
||||
}
|
||||
/*
|
||||
|
@ -484,12 +484,35 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||
tcon = tlink_tcon(tlink);
|
||||
if (tcon && tcon->ses->server->ops->notify) {
|
||||
rc = tcon->ses->server->ops->notify(xid,
|
||||
filep, (void __user *)arg);
|
||||
filep, (void __user *)arg,
|
||||
false /* no ret data */);
|
||||
cifs_dbg(FYI, "ioctl notify rc %d\n", rc);
|
||||
} else
|
||||
rc = -EOPNOTSUPP;
|
||||
cifs_put_tlink(tlink);
|
||||
break;
|
||||
case CIFS_IOC_NOTIFY_INFO:
|
||||
if (!S_ISDIR(inode->i_mode)) {
|
||||
/* Notify can only be done on directories */
|
||||
rc = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
cifs_sb = CIFS_SB(inode->i_sb);
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_ERR(tlink)) {
|
||||
rc = PTR_ERR(tlink);
|
||||
break;
|
||||
}
|
||||
tcon = tlink_tcon(tlink);
|
||||
if (tcon && tcon->ses->server->ops->notify) {
|
||||
rc = tcon->ses->server->ops->notify(xid,
|
||||
filep, (void __user *)arg,
|
||||
true /* return details */);
|
||||
cifs_dbg(FYI, "ioctl notify info rc %d\n", rc);
|
||||
} else
|
||||
rc = -EOPNOTSUPP;
|
||||
cifs_put_tlink(tlink);
|
||||
break;
|
||||
case CIFS_IOC_SHUTDOWN:
|
||||
rc = cifs_shutdown(inode->i_sb, arg);
|
||||
break;
|
||||
|
107
fs/cifs/link.c
107
fs/cifs/link.c
@ -201,40 +201,6 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
query_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const unsigned char *path,
|
||||
char **symlinkinfo)
|
||||
{
|
||||
int rc;
|
||||
u8 *buf = NULL;
|
||||
unsigned int link_len = 0;
|
||||
unsigned int bytes_read = 0;
|
||||
|
||||
buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (tcon->ses->server->ops->query_mf_symlink)
|
||||
rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon,
|
||||
cifs_sb, path, buf, &bytes_read);
|
||||
else
|
||||
rc = -ENOSYS;
|
||||
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
if (bytes_read == 0) { /* not a symlink */
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = parse_mf_symlink(buf, bytes_read, &link_len, symlinkinfo);
|
||||
out:
|
||||
kfree(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
|
||||
@ -244,6 +210,7 @@ check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
|
||||
u8 *buf = NULL;
|
||||
unsigned int link_len = 0;
|
||||
unsigned int bytes_read = 0;
|
||||
char *symlink = NULL;
|
||||
|
||||
if (!couldbe_mf_symlink(fattr))
|
||||
/* it's not a symlink */
|
||||
@ -265,7 +232,7 @@ check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
|
||||
if (bytes_read == 0) /* not a symlink */
|
||||
goto out;
|
||||
|
||||
rc = parse_mf_symlink(buf, bytes_read, &link_len, NULL);
|
||||
rc = parse_mf_symlink(buf, bytes_read, &link_len, &symlink);
|
||||
if (rc == -EINVAL) {
|
||||
/* it's not a symlink */
|
||||
rc = 0;
|
||||
@ -280,6 +247,7 @@ check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
|
||||
fattr->cf_mode &= ~S_IFMT;
|
||||
fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
|
||||
fattr->cf_dtype = DT_LNK;
|
||||
fattr->cf_symlink_target = symlink;
|
||||
out:
|
||||
kfree(buf);
|
||||
return rc;
|
||||
@ -599,75 +567,6 @@ cifs_hl_exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
const char *
|
||||
cifs_get_link(struct dentry *direntry, struct inode *inode,
|
||||
struct delayed_call *done)
|
||||
{
|
||||
int rc = -ENOMEM;
|
||||
unsigned int xid;
|
||||
const char *full_path;
|
||||
void *page;
|
||||
char *target_path = NULL;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
struct tcon_link *tlink = NULL;
|
||||
struct cifs_tcon *tcon;
|
||||
struct TCP_Server_Info *server;
|
||||
|
||||
if (!direntry)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_ERR(tlink)) {
|
||||
free_xid(xid);
|
||||
return ERR_CAST(tlink);
|
||||
}
|
||||
tcon = tlink_tcon(tlink);
|
||||
server = tcon->ses->server;
|
||||
|
||||
page = alloc_dentry_path();
|
||||
full_path = build_path_from_dentry(direntry, page);
|
||||
if (IS_ERR(full_path)) {
|
||||
free_xid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
free_dentry_path(page);
|
||||
return ERR_CAST(full_path);
|
||||
}
|
||||
|
||||
cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", full_path, inode);
|
||||
|
||||
rc = -EACCES;
|
||||
/*
|
||||
* First try Minshall+French Symlinks, if configured
|
||||
* and fallback to UNIX Extensions Symlinks.
|
||||
*/
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
|
||||
rc = query_mf_symlink(xid, tcon, cifs_sb, full_path,
|
||||
&target_path);
|
||||
|
||||
if (rc != 0 && server->ops->query_symlink) {
|
||||
struct cifsInodeInfo *cifsi = CIFS_I(inode);
|
||||
bool reparse_point = false;
|
||||
|
||||
if (cifsi->cifsAttrs & ATTR_REPARSE)
|
||||
reparse_point = true;
|
||||
|
||||
rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
|
||||
&target_path, reparse_point);
|
||||
}
|
||||
|
||||
free_dentry_path(page);
|
||||
free_xid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
if (rc != 0) {
|
||||
kfree(target_path);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
set_delayed_call(done, kfree_link, target_path);
|
||||
return target_path;
|
||||
}
|
||||
|
||||
int
|
||||
cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
struct dentry *direntry, const char *symname)
|
||||
|
@ -844,17 +844,34 @@ static bool emit_cached_dirents(struct cached_dirents *cde,
|
||||
struct dir_context *ctx)
|
||||
{
|
||||
struct cached_dirent *dirent;
|
||||
int rc;
|
||||
bool rc;
|
||||
|
||||
list_for_each_entry(dirent, &cde->entries, entry) {
|
||||
if (ctx->pos >= dirent->pos)
|
||||
/*
|
||||
* Skip all early entries prior to the current lseek()
|
||||
* position.
|
||||
*/
|
||||
if (ctx->pos > dirent->pos)
|
||||
continue;
|
||||
/*
|
||||
* We recorded the current ->pos value for the dirent
|
||||
* when we stored it in the cache.
|
||||
* However, this sequence of ->pos values may have holes
|
||||
* in it, for example dot-dirs returned from the server
|
||||
* are suppressed.
|
||||
* Handle this bu forcing ctx->pos to be the same as the
|
||||
* ->pos of the current dirent we emit from the cache.
|
||||
* This means that when we emit these entries from the cache
|
||||
* we now emit them with the same ->pos value as in the
|
||||
* initial scan.
|
||||
*/
|
||||
ctx->pos = dirent->pos;
|
||||
rc = dir_emit(ctx, dirent->name, dirent->namelen,
|
||||
dirent->fattr.cf_uniqueid,
|
||||
dirent->fattr.cf_dtype);
|
||||
if (!rc)
|
||||
return rc;
|
||||
ctx->pos++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -994,6 +1011,8 @@ static int cifs_filldir(char *find_entry, struct file *file,
|
||||
cifs_unix_basic_to_fattr(&fattr,
|
||||
&((FILE_UNIX_INFO *)find_entry)->basic,
|
||||
cifs_sb);
|
||||
if (S_ISLNK(fattr.cf_mode))
|
||||
fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
|
||||
break;
|
||||
case SMB_FIND_FILE_INFO_STANDARD:
|
||||
cifs_std_info_to_fattr(&fattr,
|
||||
@ -1202,10 +1221,10 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
|
||||
ctx->pos, tmp_buf);
|
||||
cifs_save_resume_key(current_entry, cifsFile);
|
||||
break;
|
||||
} else
|
||||
current_entry =
|
||||
nxt_dir_entry(current_entry, end_of_smb,
|
||||
cifsFile->srch_inf.info_level);
|
||||
}
|
||||
current_entry =
|
||||
nxt_dir_entry(current_entry, end_of_smb,
|
||||
cifsFile->srch_inf.info_level);
|
||||
}
|
||||
kfree(tmp_buf);
|
||||
|
||||
|
@ -601,11 +601,6 @@ static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
|
||||
/* BB FIXME add check that strings total less
|
||||
than 335 or will need to send them as arrays */
|
||||
|
||||
/* unicode strings, must be word aligned before the call */
|
||||
/* if ((long) bcc_ptr % 2) {
|
||||
*bcc_ptr = 0;
|
||||
bcc_ptr++;
|
||||
} */
|
||||
/* copy user */
|
||||
if (ses->user_name == NULL) {
|
||||
/* null user mount */
|
||||
@ -1213,16 +1208,18 @@ out_free_smb_buf:
|
||||
static void
|
||||
sess_free_buffer(struct sess_data *sess_data)
|
||||
{
|
||||
int i;
|
||||
struct kvec *iov = sess_data->iov;
|
||||
|
||||
/* zero the session data before freeing, as it might contain sensitive info (keys, etc) */
|
||||
for (i = 0; i < 3; i++)
|
||||
if (sess_data->iov[i].iov_base)
|
||||
memzero_explicit(sess_data->iov[i].iov_base, sess_data->iov[i].iov_len);
|
||||
/*
|
||||
* Zero the session data before freeing, as it might contain sensitive info (keys, etc).
|
||||
* Note that iov[1] is already freed by caller.
|
||||
*/
|
||||
if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base)
|
||||
memzero_explicit(iov[0].iov_base, iov[0].iov_len);
|
||||
|
||||
free_rsp_buf(sess_data->buf0_type, sess_data->iov[0].iov_base);
|
||||
free_rsp_buf(sess_data->buf0_type, iov[0].iov_base);
|
||||
sess_data->buf0_type = CIFS_NO_BUFFER;
|
||||
kfree(sess_data->iov[2].iov_base);
|
||||
kfree_sensitive(iov[2].iov_base);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1324,7 +1321,7 @@ sess_auth_ntlmv2(struct sess_data *sess_data)
|
||||
}
|
||||
|
||||
if (ses->capabilities & CAP_UNICODE) {
|
||||
if (sess_data->iov[0].iov_len % 2) {
|
||||
if (!IS_ALIGNED(sess_data->iov[0].iov_len, 2)) {
|
||||
*bcc_ptr = 0;
|
||||
bcc_ptr++;
|
||||
}
|
||||
@ -1364,7 +1361,7 @@ sess_auth_ntlmv2(struct sess_data *sess_data)
|
||||
/* no string area to decode, do nothing */
|
||||
} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
|
||||
/* unicode string area must be word-aligned */
|
||||
if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) {
|
||||
if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
|
||||
++bcc_ptr;
|
||||
--bytes_remaining;
|
||||
}
|
||||
@ -1448,8 +1445,7 @@ sess_auth_kerberos(struct sess_data *sess_data)
|
||||
|
||||
if (ses->capabilities & CAP_UNICODE) {
|
||||
/* unicode strings must be word aligned */
|
||||
if ((sess_data->iov[0].iov_len
|
||||
+ sess_data->iov[1].iov_len) % 2) {
|
||||
if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) {
|
||||
*bcc_ptr = 0;
|
||||
bcc_ptr++;
|
||||
}
|
||||
@ -1500,7 +1496,7 @@ sess_auth_kerberos(struct sess_data *sess_data)
|
||||
/* no string area to decode, do nothing */
|
||||
} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
|
||||
/* unicode string area must be word-aligned */
|
||||
if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) {
|
||||
if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
|
||||
++bcc_ptr;
|
||||
--bytes_remaining;
|
||||
}
|
||||
@ -1552,7 +1548,7 @@ _sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data)
|
||||
|
||||
bcc_ptr = sess_data->iov[2].iov_base;
|
||||
/* unicode strings must be word aligned */
|
||||
if ((sess_data->iov[0].iov_len + sess_data->iov[1].iov_len) % 2) {
|
||||
if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) {
|
||||
*bcc_ptr = 0;
|
||||
bcc_ptr++;
|
||||
}
|
||||
@ -1753,7 +1749,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
|
||||
/* no string area to decode, do nothing */
|
||||
} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
|
||||
/* unicode string area must be word-aligned */
|
||||
if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) {
|
||||
if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
|
||||
++bcc_ptr;
|
||||
--bytes_remaining;
|
||||
}
|
||||
|
@ -542,31 +542,32 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
FILE_ALL_INFO *data, bool *adjustTZ, bool *symlink)
|
||||
static int cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
struct cifs_open_info_data *data, bool *adjustTZ, bool *symlink)
|
||||
{
|
||||
int rc;
|
||||
FILE_ALL_INFO fi = {};
|
||||
|
||||
*symlink = false;
|
||||
|
||||
/* could do find first instead but this returns more info */
|
||||
rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */,
|
||||
cifs_sb->local_nls, cifs_remap(cifs_sb));
|
||||
rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */, cifs_sb->local_nls,
|
||||
cifs_remap(cifs_sb));
|
||||
/*
|
||||
* BB optimize code so we do not make the above call when server claims
|
||||
* no NT SMB support and the above call failed at least once - set flag
|
||||
* in tcon or mount.
|
||||
*/
|
||||
if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
|
||||
rc = SMBQueryInformation(xid, tcon, full_path, data,
|
||||
cifs_sb->local_nls,
|
||||
rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls,
|
||||
cifs_remap(cifs_sb));
|
||||
if (!rc)
|
||||
move_cifs_info_to_smb2(&data->fi, &fi);
|
||||
*adjustTZ = true;
|
||||
}
|
||||
|
||||
if (!rc && (le32_to_cpu(data->Attributes) & ATTR_REPARSE)) {
|
||||
if (!rc && (le32_to_cpu(fi.Attributes) & ATTR_REPARSE)) {
|
||||
int tmprc;
|
||||
int oplock = 0;
|
||||
struct cifs_fid fid;
|
||||
@ -592,10 +593,9 @@ cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
u64 *uniqueid, FILE_ALL_INFO *data)
|
||||
static int cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
u64 *uniqueid, struct cifs_open_info_data *unused)
|
||||
{
|
||||
/*
|
||||
* We can not use the IndexNumber field by default from Windows or
|
||||
@ -613,11 +613,22 @@ cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
cifs_remap(cifs_sb));
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_fid *fid, FILE_ALL_INFO *data)
|
||||
static int cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile, struct cifs_open_info_data *data)
|
||||
{
|
||||
return CIFSSMBQFileInfo(xid, tcon, fid->netfid, data);
|
||||
int rc;
|
||||
FILE_ALL_INFO fi = {};
|
||||
|
||||
if (cfile->symlink_target) {
|
||||
data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
|
||||
if (!data->symlink_target)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rc = CIFSSMBQFileInfo(xid, tcon, cfile->fid.netfid, &fi);
|
||||
if (!rc)
|
||||
move_cifs_info_to_smb2(&data->fi, &fi);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -702,19 +713,20 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path,
|
||||
cifsInode->cifsAttrs = dosattrs;
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
|
||||
__u32 *oplock, FILE_ALL_INFO *buf)
|
||||
static int cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
|
||||
void *buf)
|
||||
{
|
||||
FILE_ALL_INFO *fi = buf;
|
||||
|
||||
if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS))
|
||||
return SMBLegacyOpen(xid, oparms->tcon, oparms->path,
|
||||
oparms->disposition,
|
||||
oparms->desired_access,
|
||||
oparms->create_options,
|
||||
&oparms->fid->netfid, oplock, buf,
|
||||
&oparms->fid->netfid, oplock, fi,
|
||||
oparms->cifs_sb->local_nls,
|
||||
cifs_remap(oparms->cifs_sb));
|
||||
return CIFS_open(xid, oparms, oplock, buf);
|
||||
return CIFS_open(xid, oparms, oplock, fi);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -20,40 +20,125 @@
|
||||
#include "cifs_unicode.h"
|
||||
#include "fscache.h"
|
||||
#include "smb2proto.h"
|
||||
#include "smb2status.h"
|
||||
|
||||
int
|
||||
smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
|
||||
__u32 *oplock, FILE_ALL_INFO *buf)
|
||||
static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
|
||||
{
|
||||
struct smb2_err_rsp *err = iov->iov_base;
|
||||
struct smb2_symlink_err_rsp *sym = ERR_PTR(-EINVAL);
|
||||
u32 len;
|
||||
|
||||
if (err->ErrorContextCount) {
|
||||
struct smb2_error_context_rsp *p, *end;
|
||||
|
||||
len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp,
|
||||
ErrorContextData) +
|
||||
sizeof(struct smb2_symlink_err_rsp));
|
||||
if (le32_to_cpu(err->ByteCount) < len || iov->iov_len < len + sizeof(*err))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
p = (struct smb2_error_context_rsp *)err->ErrorData;
|
||||
end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len);
|
||||
do {
|
||||
if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) {
|
||||
sym = (struct smb2_symlink_err_rsp *)&p->ErrorContextData;
|
||||
break;
|
||||
}
|
||||
cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n",
|
||||
__func__, le32_to_cpu(p->ErrorId));
|
||||
|
||||
len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
|
||||
p = (struct smb2_error_context_rsp *)((u8 *)&p->ErrorContextData + len);
|
||||
} while (p < end);
|
||||
} else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) &&
|
||||
iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) {
|
||||
sym = (struct smb2_symlink_err_rsp *)err->ErrorData;
|
||||
}
|
||||
|
||||
if (!IS_ERR(sym) && (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
|
||||
le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK))
|
||||
sym = ERR_PTR(-EINVAL);
|
||||
|
||||
return sym;
|
||||
}
|
||||
|
||||
int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path)
|
||||
{
|
||||
struct smb2_symlink_err_rsp *sym;
|
||||
unsigned int sub_offs, sub_len;
|
||||
unsigned int print_offs, print_len;
|
||||
char *s;
|
||||
|
||||
if (!cifs_sb || !iov || !iov->iov_base || !iov->iov_len || !path)
|
||||
return -EINVAL;
|
||||
|
||||
sym = symlink_data(iov);
|
||||
if (IS_ERR(sym))
|
||||
return PTR_ERR(sym);
|
||||
|
||||
sub_len = le16_to_cpu(sym->SubstituteNameLength);
|
||||
sub_offs = le16_to_cpu(sym->SubstituteNameOffset);
|
||||
print_len = le16_to_cpu(sym->PrintNameLength);
|
||||
print_offs = le16_to_cpu(sym->PrintNameOffset);
|
||||
|
||||
if (iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offs + sub_len ||
|
||||
iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len)
|
||||
return -EINVAL;
|
||||
|
||||
s = cifs_strndup_from_utf16((char *)sym->PathBuffer + sub_offs, sub_len, true,
|
||||
cifs_sb->local_nls);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
convert_delimiter(s, '/');
|
||||
cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, s);
|
||||
|
||||
*path = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, void *buf)
|
||||
{
|
||||
int rc;
|
||||
__le16 *smb2_path;
|
||||
struct smb2_file_all_info *smb2_data = NULL;
|
||||
__u8 smb2_oplock;
|
||||
struct cifs_open_info_data *data = buf;
|
||||
struct smb2_file_all_info file_info = {};
|
||||
struct smb2_file_all_info *smb2_data = data ? &file_info : NULL;
|
||||
struct kvec err_iov = {};
|
||||
int err_buftype = CIFS_NO_BUFFER;
|
||||
struct cifs_fid *fid = oparms->fid;
|
||||
struct network_resiliency_req nr_ioctl_req;
|
||||
|
||||
smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb);
|
||||
if (smb2_path == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
|
||||
GFP_KERNEL);
|
||||
if (smb2_data == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (smb2_path == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
oparms->desired_access |= FILE_READ_ATTRIBUTES;
|
||||
smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
|
||||
|
||||
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL,
|
||||
NULL, NULL);
|
||||
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
|
||||
&err_buftype);
|
||||
if (rc && data) {
|
||||
struct smb2_hdr *hdr = err_iov.iov_base;
|
||||
|
||||
if (unlikely(!err_iov.iov_base || err_buftype == CIFS_NO_BUFFER))
|
||||
rc = -ENOMEM;
|
||||
else if (hdr->Status == STATUS_STOPPED_ON_SYMLINK) {
|
||||
rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov,
|
||||
&data->symlink_target);
|
||||
if (!rc) {
|
||||
memset(smb2_data, 0, sizeof(*smb2_data));
|
||||
oparms->create_options |= OPEN_REPARSE_POINT;
|
||||
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data,
|
||||
NULL, NULL, NULL);
|
||||
oparms->create_options &= ~OPEN_REPARSE_POINT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
|
||||
if (oparms->tcon->use_resilient) {
|
||||
/* default timeout is 0, servers pick default (120 seconds) */
|
||||
nr_ioctl_req.Timeout =
|
||||
@ -73,7 +158,7 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
if (buf) {
|
||||
if (smb2_data) {
|
||||
/* if open response does not have IndexNumber field - get it */
|
||||
if (smb2_data->IndexNumber == 0) {
|
||||
rc = SMB2_get_srv_num(xid, oparms->tcon,
|
||||
@ -89,12 +174,12 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
move_smb2_info_to_cifs(buf, smb2_data);
|
||||
memcpy(&data->fi, smb2_data, sizeof(data->fi));
|
||||
}
|
||||
|
||||
*oplock = smb2_oplock;
|
||||
out:
|
||||
kfree(smb2_data);
|
||||
free_rsp_buf(err_buftype, err_iov.iov_base);
|
||||
kfree(smb2_path);
|
||||
return rc;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "smb2pdu.h"
|
||||
#include "smb2proto.h"
|
||||
#include "cached_dir.h"
|
||||
#include "smb2status.h"
|
||||
|
||||
static void
|
||||
free_set_inf_compound(struct smb_rqst *rqst)
|
||||
@ -50,13 +51,15 @@ struct cop_vars {
|
||||
/*
|
||||
* note: If cfile is passed, the reference to it is dropped here.
|
||||
* So make sure that you do not reuse cfile after return from this func.
|
||||
*
|
||||
* If passing @err_iov and @err_buftype, ensure to make them both large enough (>= 3) to hold all
|
||||
* error responses. Caller is also responsible for freeing them up.
|
||||
*/
|
||||
static int
|
||||
smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
__u32 desired_access, __u32 create_disposition,
|
||||
__u32 create_options, umode_t mode, void *ptr, int command,
|
||||
struct cifsFileInfo *cfile)
|
||||
static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
__u32 desired_access, __u32 create_disposition, __u32 create_options,
|
||||
umode_t mode, void *ptr, int command, struct cifsFileInfo *cfile,
|
||||
struct kvec *err_iov, int *err_buftype)
|
||||
{
|
||||
struct cop_vars *vars = NULL;
|
||||
struct kvec *rsp_iov;
|
||||
@ -70,6 +73,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
int num_rqst = 0;
|
||||
int resp_buftype[3];
|
||||
struct smb2_query_info_rsp *qi_rsp = NULL;
|
||||
struct cifs_open_info_data *idata;
|
||||
int flags = 0;
|
||||
__u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
|
||||
unsigned int size[2];
|
||||
@ -385,14 +389,19 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
switch (command) {
|
||||
case SMB2_OP_QUERY_INFO:
|
||||
idata = ptr;
|
||||
if (rc == 0 && cfile && cfile->symlink_target) {
|
||||
idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
|
||||
if (!idata->symlink_target)
|
||||
rc = -ENOMEM;
|
||||
}
|
||||
if (rc == 0) {
|
||||
qi_rsp = (struct smb2_query_info_rsp *)
|
||||
rsp_iov[1].iov_base;
|
||||
rc = smb2_validate_and_copy_iov(
|
||||
le16_to_cpu(qi_rsp->OutputBufferOffset),
|
||||
le32_to_cpu(qi_rsp->OutputBufferLength),
|
||||
&rsp_iov[1], sizeof(struct smb2_file_all_info),
|
||||
ptr);
|
||||
&rsp_iov[1], sizeof(idata->fi), (char *)&idata->fi);
|
||||
}
|
||||
if (rqst[1].rq_iov)
|
||||
SMB2_query_info_free(&rqst[1]);
|
||||
@ -406,13 +415,20 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
tcon->tid);
|
||||
break;
|
||||
case SMB2_OP_POSIX_QUERY_INFO:
|
||||
idata = ptr;
|
||||
if (rc == 0 && cfile && cfile->symlink_target) {
|
||||
idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
|
||||
if (!idata->symlink_target)
|
||||
rc = -ENOMEM;
|
||||
}
|
||||
if (rc == 0) {
|
||||
qi_rsp = (struct smb2_query_info_rsp *)
|
||||
rsp_iov[1].iov_base;
|
||||
rc = smb2_validate_and_copy_iov(
|
||||
le16_to_cpu(qi_rsp->OutputBufferOffset),
|
||||
le32_to_cpu(qi_rsp->OutputBufferLength),
|
||||
&rsp_iov[1], sizeof(struct smb311_posix_qinfo) /* add SIDs */, ptr);
|
||||
&rsp_iov[1], sizeof(idata->posix_fi) /* add SIDs */,
|
||||
(char *)&idata->posix_fi);
|
||||
}
|
||||
if (rqst[1].rq_iov)
|
||||
SMB2_query_info_free(&rqst[1]);
|
||||
@ -477,42 +493,33 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
free_set_inf_compound(rqst);
|
||||
break;
|
||||
}
|
||||
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
|
||||
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
|
||||
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
|
||||
|
||||
if (rc && err_iov && err_buftype) {
|
||||
memcpy(err_iov, rsp_iov, 3 * sizeof(*err_iov));
|
||||
memcpy(err_buftype, resp_buftype, 3 * sizeof(*err_buftype));
|
||||
} else {
|
||||
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
|
||||
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
|
||||
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
|
||||
}
|
||||
kfree(vars);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void
|
||||
move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)
|
||||
{
|
||||
memcpy(dst, src, (size_t)(&src->CurrentByteOffset) - (size_t)src);
|
||||
dst->CurrentByteOffset = src->CurrentByteOffset;
|
||||
dst->Mode = src->Mode;
|
||||
dst->AlignmentRequirement = src->AlignmentRequirement;
|
||||
dst->IndexNumber1 = 0; /* we don't use it */
|
||||
}
|
||||
|
||||
int
|
||||
smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
FILE_ALL_INFO *data, bool *adjust_tz, bool *reparse)
|
||||
int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse)
|
||||
{
|
||||
int rc;
|
||||
struct smb2_file_all_info *smb2_data;
|
||||
__u32 create_options = 0;
|
||||
struct cifsFileInfo *cfile;
|
||||
struct cached_fid *cfid = NULL;
|
||||
struct kvec err_iov[3] = {};
|
||||
int err_buftype[3] = {};
|
||||
|
||||
*adjust_tz = false;
|
||||
*reparse = false;
|
||||
|
||||
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
|
||||
GFP_KERNEL);
|
||||
if (smb2_data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (strcmp(full_path, ""))
|
||||
rc = -ENOENT;
|
||||
else
|
||||
@ -520,63 +527,58 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
/* If it is a root and its handle is cached then use it */
|
||||
if (!rc) {
|
||||
if (cfid->file_all_info_is_valid) {
|
||||
move_smb2_info_to_cifs(data,
|
||||
&cfid->file_all_info);
|
||||
memcpy(&data->fi, &cfid->file_all_info, sizeof(data->fi));
|
||||
} else {
|
||||
rc = SMB2_query_info(xid, tcon,
|
||||
cfid->fid.persistent_fid,
|
||||
cfid->fid.volatile_fid, smb2_data);
|
||||
if (!rc)
|
||||
move_smb2_info_to_cifs(data, smb2_data);
|
||||
rc = SMB2_query_info(xid, tcon, cfid->fid.persistent_fid,
|
||||
cfid->fid.volatile_fid, &data->fi);
|
||||
}
|
||||
close_cached_dir(cfid);
|
||||
goto out;
|
||||
return rc;
|
||||
}
|
||||
|
||||
cifs_get_readable_path(tcon, full_path, &cfile);
|
||||
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
|
||||
FILE_READ_ATTRIBUTES, FILE_OPEN, create_options,
|
||||
ACL_NO_MODE, smb2_data, SMB2_OP_QUERY_INFO, cfile);
|
||||
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN,
|
||||
create_options, ACL_NO_MODE, data, SMB2_OP_QUERY_INFO, cfile,
|
||||
err_iov, err_buftype);
|
||||
if (rc == -EOPNOTSUPP) {
|
||||
if (err_iov[0].iov_base && err_buftype[0] != CIFS_NO_BUFFER &&
|
||||
((struct smb2_hdr *)err_iov[0].iov_base)->Command == SMB2_CREATE &&
|
||||
((struct smb2_hdr *)err_iov[0].iov_base)->Status == STATUS_STOPPED_ON_SYMLINK) {
|
||||
rc = smb2_parse_symlink_response(cifs_sb, err_iov, &data->symlink_target);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
*reparse = true;
|
||||
create_options |= OPEN_REPARSE_POINT;
|
||||
|
||||
/* Failed on a symbolic link - query a reparse point info */
|
||||
cifs_get_readable_path(tcon, full_path, &cfile);
|
||||
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
|
||||
FILE_READ_ATTRIBUTES, FILE_OPEN,
|
||||
create_options, ACL_NO_MODE,
|
||||
smb2_data, SMB2_OP_QUERY_INFO, cfile);
|
||||
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES,
|
||||
FILE_OPEN, create_options, ACL_NO_MODE, data,
|
||||
SMB2_OP_QUERY_INFO, cfile, NULL, NULL);
|
||||
}
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
move_smb2_info_to_cifs(data, smb2_data);
|
||||
out:
|
||||
kfree(smb2_data);
|
||||
free_rsp_buf(err_buftype[0], err_iov[0].iov_base);
|
||||
free_rsp_buf(err_buftype[1], err_iov[1].iov_base);
|
||||
free_rsp_buf(err_buftype[2], err_iov[2].iov_base);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
struct smb311_posix_qinfo *data, bool *adjust_tz, bool *reparse)
|
||||
int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse)
|
||||
{
|
||||
int rc;
|
||||
__u32 create_options = 0;
|
||||
struct cifsFileInfo *cfile;
|
||||
struct smb311_posix_qinfo *smb2_data;
|
||||
struct kvec err_iov[3] = {};
|
||||
int err_buftype[3] = {};
|
||||
|
||||
*adjust_tz = false;
|
||||
*reparse = false;
|
||||
|
||||
/* BB TODO: Make struct larger when add support for parsing owner SIDs */
|
||||
smb2_data = kzalloc(sizeof(struct smb311_posix_qinfo),
|
||||
GFP_KERNEL);
|
||||
if (smb2_data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* BB TODO: Add support for using the cached root handle.
|
||||
* Create SMB2_query_posix_info worker function to do non-compounded query
|
||||
@ -585,29 +587,32 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
*/
|
||||
|
||||
cifs_get_readable_path(tcon, full_path, &cfile);
|
||||
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
|
||||
FILE_READ_ATTRIBUTES, FILE_OPEN, create_options,
|
||||
ACL_NO_MODE, smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile);
|
||||
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN,
|
||||
create_options, ACL_NO_MODE, data, SMB2_OP_POSIX_QUERY_INFO, cfile,
|
||||
err_iov, err_buftype);
|
||||
if (rc == -EOPNOTSUPP) {
|
||||
/* BB TODO: When support for special files added to Samba re-verify this path */
|
||||
if (err_iov[0].iov_base && err_buftype[0] != CIFS_NO_BUFFER &&
|
||||
((struct smb2_hdr *)err_iov[0].iov_base)->Command == SMB2_CREATE &&
|
||||
((struct smb2_hdr *)err_iov[0].iov_base)->Status == STATUS_STOPPED_ON_SYMLINK) {
|
||||
rc = smb2_parse_symlink_response(cifs_sb, err_iov, &data->symlink_target);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
*reparse = true;
|
||||
create_options |= OPEN_REPARSE_POINT;
|
||||
|
||||
/* Failed on a symbolic link - query a reparse point info */
|
||||
cifs_get_readable_path(tcon, full_path, &cfile);
|
||||
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
|
||||
FILE_READ_ATTRIBUTES, FILE_OPEN,
|
||||
create_options, ACL_NO_MODE,
|
||||
smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile);
|
||||
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES,
|
||||
FILE_OPEN, create_options, ACL_NO_MODE, data,
|
||||
SMB2_OP_POSIX_QUERY_INFO, cfile, NULL, NULL);
|
||||
}
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* TODO: will need to allow for the 2 SIDs when add support for getting owner UID/GID */
|
||||
memcpy(data, smb2_data, sizeof(struct smb311_posix_qinfo));
|
||||
|
||||
out:
|
||||
kfree(smb2_data);
|
||||
free_rsp_buf(err_buftype[0], err_iov[0].iov_base);
|
||||
free_rsp_buf(err_buftype[1], err_iov[1].iov_base);
|
||||
free_rsp_buf(err_buftype[2], err_iov[2].iov_base);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -619,7 +624,7 @@ smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
|
||||
return smb2_compound_op(xid, tcon, cifs_sb, name,
|
||||
FILE_WRITE_ATTRIBUTES, FILE_CREATE,
|
||||
CREATE_NOT_FILE, mode, NULL, SMB2_OP_MKDIR,
|
||||
NULL);
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
@ -641,7 +646,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
|
||||
tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
|
||||
FILE_WRITE_ATTRIBUTES, FILE_CREATE,
|
||||
CREATE_NOT_FILE, ACL_NO_MODE,
|
||||
&data, SMB2_OP_SET_INFO, cfile);
|
||||
&data, SMB2_OP_SET_INFO, cfile, NULL, NULL);
|
||||
if (tmprc == 0)
|
||||
cifs_i->cifsAttrs = dosattrs;
|
||||
}
|
||||
@ -652,7 +657,7 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
|
||||
{
|
||||
return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
|
||||
CREATE_NOT_FILE, ACL_NO_MODE,
|
||||
NULL, SMB2_OP_RMDIR, NULL);
|
||||
NULL, SMB2_OP_RMDIR, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
@ -661,7 +666,7 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
|
||||
{
|
||||
return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
|
||||
CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
|
||||
ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL);
|
||||
ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -680,7 +685,7 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
}
|
||||
rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
|
||||
FILE_OPEN, 0, ACL_NO_MODE, smb2_to_name,
|
||||
command, cfile);
|
||||
command, cfile, NULL, NULL);
|
||||
smb2_rename_path:
|
||||
kfree(smb2_to_name);
|
||||
return rc;
|
||||
@ -720,7 +725,7 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
|
||||
return smb2_compound_op(xid, tcon, cifs_sb, full_path,
|
||||
FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE,
|
||||
&eof, SMB2_OP_SET_EOF, cfile);
|
||||
&eof, SMB2_OP_SET_EOF, cfile, NULL, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
@ -746,7 +751,8 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
|
||||
cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
|
||||
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
|
||||
FILE_WRITE_ATTRIBUTES, FILE_OPEN,
|
||||
0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile);
|
||||
0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile,
|
||||
NULL, NULL);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
@ -248,7 +248,7 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
|
||||
* Some windows servers (win2016) will pad also the final
|
||||
* PDU in a compound to 8 bytes.
|
||||
*/
|
||||
if (((calc_len + 7) & ~7) == len)
|
||||
if (ALIGN(calc_len, 8) == len)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -550,7 +550,8 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
|
||||
/* avoid spamming logs every 10 minutes, so log only in mount */
|
||||
if ((ses->chan_max > 1) && in_mount)
|
||||
cifs_dbg(VFS,
|
||||
"empty network interface list returned by server %s\n",
|
||||
"multichannel not available\n"
|
||||
"Empty network interface list returned by server %s\n",
|
||||
ses->server->hostname);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
@ -800,7 +801,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid);
|
||||
if (!rc) {
|
||||
if (cfid->is_valid) {
|
||||
if (cfid->has_lease) {
|
||||
close_cached_dir(cfid);
|
||||
return 0;
|
||||
}
|
||||
@ -830,33 +831,25 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
u64 *uniqueid, FILE_ALL_INFO *data)
|
||||
static int smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
u64 *uniqueid, struct cifs_open_info_data *data)
|
||||
{
|
||||
*uniqueid = le64_to_cpu(data->IndexNumber);
|
||||
*uniqueid = le64_to_cpu(data->fi.IndexNumber);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_fid *fid, FILE_ALL_INFO *data)
|
||||
static int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile, struct cifs_open_info_data *data)
|
||||
{
|
||||
int rc;
|
||||
struct smb2_file_all_info *smb2_data;
|
||||
struct cifs_fid *fid = &cfile->fid;
|
||||
|
||||
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
|
||||
GFP_KERNEL);
|
||||
if (smb2_data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid,
|
||||
smb2_data);
|
||||
if (!rc)
|
||||
move_smb2_info_to_cifs(data, smb2_data);
|
||||
kfree(smb2_data);
|
||||
return rc;
|
||||
if (cfile->symlink_target) {
|
||||
data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
|
||||
if (!data->symlink_target)
|
||||
return -ENOMEM;
|
||||
}
|
||||
return SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, &data->fi);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_XATTR
|
||||
@ -2025,9 +2018,10 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
static int
|
||||
smb3_notify(const unsigned int xid, struct file *pfile,
|
||||
void __user *ioc_buf)
|
||||
void __user *ioc_buf, bool return_changes)
|
||||
{
|
||||
struct smb3_notify notify;
|
||||
struct smb3_notify_info notify;
|
||||
struct smb3_notify_info __user *pnotify_buf;
|
||||
struct dentry *dentry = pfile->f_path.dentry;
|
||||
struct inode *inode = file_inode(pfile);
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
@ -2035,10 +2029,12 @@ smb3_notify(const unsigned int xid, struct file *pfile,
|
||||
struct cifs_fid fid;
|
||||
struct cifs_tcon *tcon;
|
||||
const unsigned char *path;
|
||||
char *returned_ioctl_info = NULL;
|
||||
void *page = alloc_dentry_path();
|
||||
__le16 *utf16_path = NULL;
|
||||
u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||
int rc = 0;
|
||||
__u32 ret_len = 0;
|
||||
|
||||
path = build_path_from_dentry(dentry, page);
|
||||
if (IS_ERR(path)) {
|
||||
@ -2052,9 +2048,17 @@ smb3_notify(const unsigned int xid, struct file *pfile,
|
||||
goto notify_exit;
|
||||
}
|
||||
|
||||
if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify))) {
|
||||
rc = -EFAULT;
|
||||
goto notify_exit;
|
||||
if (return_changes) {
|
||||
if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify_info))) {
|
||||
rc = -EFAULT;
|
||||
goto notify_exit;
|
||||
}
|
||||
} else {
|
||||
if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify))) {
|
||||
rc = -EFAULT;
|
||||
goto notify_exit;
|
||||
}
|
||||
notify.data_len = 0;
|
||||
}
|
||||
|
||||
tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
@ -2071,12 +2075,22 @@ smb3_notify(const unsigned int xid, struct file *pfile,
|
||||
goto notify_exit;
|
||||
|
||||
rc = SMB2_change_notify(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
||||
notify.watch_tree, notify.completion_filter);
|
||||
notify.watch_tree, notify.completion_filter,
|
||||
notify.data_len, &returned_ioctl_info, &ret_len);
|
||||
|
||||
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
|
||||
|
||||
cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc);
|
||||
|
||||
if (return_changes && (ret_len > 0) && (notify.data_len > 0)) {
|
||||
if (ret_len > notify.data_len)
|
||||
ret_len = notify.data_len;
|
||||
pnotify_buf = (struct smb3_notify_info __user *)ioc_buf;
|
||||
if (copy_to_user(pnotify_buf->notify_data, returned_ioctl_info, ret_len))
|
||||
rc = -EFAULT;
|
||||
else if (copy_to_user(&pnotify_buf->data_len, &ret_len, sizeof(ret_len)))
|
||||
rc = -EFAULT;
|
||||
}
|
||||
kfree(returned_ioctl_info);
|
||||
notify_exit:
|
||||
free_dentry_path(page);
|
||||
kfree(utf16_path);
|
||||
@ -2827,9 +2841,6 @@ parse_reparse_point(struct reparse_data_buffer *buf,
|
||||
}
|
||||
}
|
||||
|
||||
#define SMB2_SYMLINK_STRUCT_SIZE \
|
||||
(sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
|
||||
|
||||
static int
|
||||
smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
@ -2841,13 +2852,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_open_parms oparms;
|
||||
struct cifs_fid fid;
|
||||
struct kvec err_iov = {NULL, 0};
|
||||
struct smb2_err_rsp *err_buf = NULL;
|
||||
struct smb2_symlink_err_rsp *symlink;
|
||||
struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
|
||||
unsigned int sub_len;
|
||||
unsigned int sub_offset;
|
||||
unsigned int print_len;
|
||||
unsigned int print_offset;
|
||||
int flags = CIFS_CP_CREATE_CLOSE_OP;
|
||||
struct smb_rqst rqst[3];
|
||||
int resp_buftype[3];
|
||||
@ -2964,47 +2969,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
goto querty_exit;
|
||||
}
|
||||
|
||||
err_buf = err_iov.iov_base;
|
||||
if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) ||
|
||||
err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE) {
|
||||
rc = -EINVAL;
|
||||
goto querty_exit;
|
||||
}
|
||||
|
||||
symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData;
|
||||
if (le32_to_cpu(symlink->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
|
||||
le32_to_cpu(symlink->ReparseTag) != IO_REPARSE_TAG_SYMLINK) {
|
||||
rc = -EINVAL;
|
||||
goto querty_exit;
|
||||
}
|
||||
|
||||
/* open must fail on symlink - reset rc */
|
||||
rc = 0;
|
||||
sub_len = le16_to_cpu(symlink->SubstituteNameLength);
|
||||
sub_offset = le16_to_cpu(symlink->SubstituteNameOffset);
|
||||
print_len = le16_to_cpu(symlink->PrintNameLength);
|
||||
print_offset = le16_to_cpu(symlink->PrintNameOffset);
|
||||
|
||||
if (err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) {
|
||||
rc = -EINVAL;
|
||||
goto querty_exit;
|
||||
}
|
||||
|
||||
if (err_iov.iov_len <
|
||||
SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) {
|
||||
rc = -EINVAL;
|
||||
goto querty_exit;
|
||||
}
|
||||
|
||||
*target_path = cifs_strndup_from_utf16(
|
||||
(char *)symlink->PathBuffer + sub_offset,
|
||||
sub_len, true, cifs_sb->local_nls);
|
||||
if (!(*target_path)) {
|
||||
rc = -ENOMEM;
|
||||
goto querty_exit;
|
||||
}
|
||||
convert_delimiter(*target_path, '/');
|
||||
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
|
||||
rc = smb2_parse_symlink_response(cifs_sb, &err_iov, target_path);
|
||||
|
||||
querty_exit:
|
||||
cifs_dbg(FYI, "query symlink rc %d\n", rc);
|
||||
@ -5114,7 +5079,7 @@ smb2_make_node(unsigned int xid, struct inode *inode,
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
int rc = -EPERM;
|
||||
FILE_ALL_INFO *buf = NULL;
|
||||
struct cifs_open_info_data buf = {};
|
||||
struct cifs_io_parms io_parms = {0};
|
||||
__u32 oplock = 0;
|
||||
struct cifs_fid fid;
|
||||
@ -5130,7 +5095,7 @@ smb2_make_node(unsigned int xid, struct inode *inode,
|
||||
* and was used by default in earlier versions of Windows
|
||||
*/
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
|
||||
goto out;
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* TODO: Add ability to create instead via reparse point. Windows (e.g.
|
||||
@ -5139,16 +5104,10 @@ smb2_make_node(unsigned int xid, struct inode *inode,
|
||||
*/
|
||||
|
||||
if (!S_ISCHR(mode) && !S_ISBLK(mode))
|
||||
goto out;
|
||||
return rc;
|
||||
|
||||
cifs_dbg(FYI, "sfu compat create special file\n");
|
||||
|
||||
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
oparms.tcon = tcon;
|
||||
oparms.cifs_sb = cifs_sb;
|
||||
oparms.desired_access = GENERIC_WRITE;
|
||||
@ -5163,21 +5122,21 @@ smb2_make_node(unsigned int xid, struct inode *inode,
|
||||
oplock = REQ_OPLOCK;
|
||||
else
|
||||
oplock = 0;
|
||||
rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf);
|
||||
rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf);
|
||||
if (rc)
|
||||
goto out;
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* BB Do not bother to decode buf since no local inode yet to put
|
||||
* timestamps in, but we can reuse it safely.
|
||||
*/
|
||||
|
||||
pdev = (struct win_dev *)buf;
|
||||
pdev = (struct win_dev *)&buf.fi;
|
||||
io_parms.pid = current->tgid;
|
||||
io_parms.tcon = tcon;
|
||||
io_parms.offset = 0;
|
||||
io_parms.length = sizeof(struct win_dev);
|
||||
iov[1].iov_base = buf;
|
||||
iov[1].iov_base = &buf.fi;
|
||||
iov[1].iov_len = sizeof(struct win_dev);
|
||||
if (S_ISCHR(mode)) {
|
||||
memcpy(pdev->type, "IntxCHR", 8);
|
||||
@ -5196,8 +5155,8 @@ smb2_make_node(unsigned int xid, struct inode *inode,
|
||||
d_drop(dentry);
|
||||
|
||||
/* FIXME: add code here to set EAs */
|
||||
out:
|
||||
kfree(buf);
|
||||
|
||||
cifs_free_open_info(&buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -466,15 +466,14 @@ build_signing_ctxt(struct smb2_signing_capabilities *pneg_ctxt)
|
||||
/*
|
||||
* Context Data length must be rounded to multiple of 8 for some servers
|
||||
*/
|
||||
pneg_ctxt->DataLength = cpu_to_le16(DIV_ROUND_UP(
|
||||
sizeof(struct smb2_signing_capabilities) -
|
||||
sizeof(struct smb2_neg_context) +
|
||||
(num_algs * 2 /* sizeof u16 */), 8) * 8);
|
||||
pneg_ctxt->DataLength = cpu_to_le16(ALIGN(sizeof(struct smb2_signing_capabilities) -
|
||||
sizeof(struct smb2_neg_context) +
|
||||
(num_algs * sizeof(u16)), 8));
|
||||
pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(num_algs);
|
||||
pneg_ctxt->SigningAlgorithms[0] = cpu_to_le16(SIGNING_ALG_AES_CMAC);
|
||||
|
||||
ctxt_len += 2 /* sizeof le16 */ * num_algs;
|
||||
ctxt_len = DIV_ROUND_UP(ctxt_len, 8) * 8;
|
||||
ctxt_len += sizeof(__le16) * num_algs;
|
||||
ctxt_len = ALIGN(ctxt_len, 8);
|
||||
return ctxt_len;
|
||||
/* TBD add SIGNING_ALG_AES_GMAC and/or SIGNING_ALG_HMAC_SHA256 */
|
||||
}
|
||||
@ -511,8 +510,7 @@ build_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname)
|
||||
/* copy up to max of first 100 bytes of server name to NetName field */
|
||||
pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp));
|
||||
/* context size is DataLength + minimal smb2_neg_context */
|
||||
return DIV_ROUND_UP(le16_to_cpu(pneg_ctxt->DataLength) +
|
||||
sizeof(struct smb2_neg_context), 8) * 8;
|
||||
return ALIGN(le16_to_cpu(pneg_ctxt->DataLength) + sizeof(struct smb2_neg_context), 8);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -557,18 +555,18 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
|
||||
* round up total_len of fixed part of SMB3 negotiate request to 8
|
||||
* byte boundary before adding negotiate contexts
|
||||
*/
|
||||
*total_len = roundup(*total_len, 8);
|
||||
*total_len = ALIGN(*total_len, 8);
|
||||
|
||||
pneg_ctxt = (*total_len) + (char *)req;
|
||||
req->NegotiateContextOffset = cpu_to_le32(*total_len);
|
||||
|
||||
build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt);
|
||||
ctxt_len = DIV_ROUND_UP(sizeof(struct smb2_preauth_neg_context), 8) * 8;
|
||||
ctxt_len = ALIGN(sizeof(struct smb2_preauth_neg_context), 8);
|
||||
*total_len += ctxt_len;
|
||||
pneg_ctxt += ctxt_len;
|
||||
|
||||
build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt);
|
||||
ctxt_len = DIV_ROUND_UP(sizeof(struct smb2_encryption_neg_context), 8) * 8;
|
||||
ctxt_len = ALIGN(sizeof(struct smb2_encryption_neg_context), 8);
|
||||
*total_len += ctxt_len;
|
||||
pneg_ctxt += ctxt_len;
|
||||
|
||||
@ -595,9 +593,7 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
|
||||
if (server->compress_algorithm) {
|
||||
build_compression_ctxt((struct smb2_compression_capabilities_context *)
|
||||
pneg_ctxt);
|
||||
ctxt_len = DIV_ROUND_UP(
|
||||
sizeof(struct smb2_compression_capabilities_context),
|
||||
8) * 8;
|
||||
ctxt_len = ALIGN(sizeof(struct smb2_compression_capabilities_context), 8);
|
||||
*total_len += ctxt_len;
|
||||
pneg_ctxt += ctxt_len;
|
||||
neg_context_count++;
|
||||
@ -780,7 +776,7 @@ static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp,
|
||||
if (rc)
|
||||
break;
|
||||
/* offsets must be 8 byte aligned */
|
||||
clen = (clen + 7) & ~0x7;
|
||||
clen = ALIGN(clen, 8);
|
||||
offset += clen + sizeof(struct smb2_neg_context);
|
||||
len_of_ctxts -= clen;
|
||||
}
|
||||
@ -2424,9 +2420,9 @@ create_sd_buf(umode_t mode, bool set_owner, unsigned int *len)
|
||||
unsigned int acelen, acl_size, ace_count;
|
||||
unsigned int owner_offset = 0;
|
||||
unsigned int group_offset = 0;
|
||||
struct smb3_acl acl;
|
||||
struct smb3_acl acl = {};
|
||||
|
||||
*len = roundup(sizeof(struct crt_sd_ctxt) + (sizeof(struct cifs_ace) * 4), 8);
|
||||
*len = round_up(sizeof(struct crt_sd_ctxt) + (sizeof(struct cifs_ace) * 4), 8);
|
||||
|
||||
if (set_owner) {
|
||||
/* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */
|
||||
@ -2497,10 +2493,11 @@ create_sd_buf(umode_t mode, bool set_owner, unsigned int *len)
|
||||
acl.AclRevision = ACL_REVISION; /* See 2.4.4.1 of MS-DTYP */
|
||||
acl.AclSize = cpu_to_le16(acl_size);
|
||||
acl.AceCount = cpu_to_le16(ace_count);
|
||||
/* acl.Sbz1 and Sbz2 MBZ so are not set here, but initialized above */
|
||||
memcpy(aclptr, &acl, sizeof(struct smb3_acl));
|
||||
|
||||
buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd);
|
||||
*len = roundup(ptr - (__u8 *)buf, 8);
|
||||
*len = round_up((unsigned int)(ptr - (__u8 *)buf), 8);
|
||||
|
||||
return buf;
|
||||
}
|
||||
@ -2594,7 +2591,7 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
|
||||
* final path needs to be 8-byte aligned as specified in
|
||||
* MS-SMB2 2.2.13 SMB2 CREATE Request.
|
||||
*/
|
||||
*out_size = roundup(*out_len * sizeof(__le16), 8);
|
||||
*out_size = round_up(*out_len * sizeof(__le16), 8);
|
||||
*out_path = kzalloc(*out_size + sizeof(__le16) /* null */, GFP_KERNEL);
|
||||
if (!*out_path)
|
||||
return -ENOMEM;
|
||||
@ -2839,9 +2836,7 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
|
||||
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
|
||||
/* MUST set path len (NameLength) to 0 opening root of share */
|
||||
req->NameLength = cpu_to_le16(uni_path_len - 2);
|
||||
copy_size = uni_path_len;
|
||||
if (copy_size % 8 != 0)
|
||||
copy_size = roundup(copy_size, 8);
|
||||
copy_size = round_up(uni_path_len, 8);
|
||||
copy_path = kzalloc(copy_size, GFP_KERNEL);
|
||||
if (!copy_path)
|
||||
return -ENOMEM;
|
||||
@ -3485,7 +3480,7 @@ smb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
memcpy(data, begin_of_buf, buffer_length);
|
||||
memcpy(data, begin_of_buf, minbufsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3609,7 +3604,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset),
|
||||
le32_to_cpu(rsp->OutputBufferLength),
|
||||
&rsp_iov, min_len, *data);
|
||||
&rsp_iov, dlen ? *dlen : min_len, *data);
|
||||
if (rc && allocated) {
|
||||
kfree(*data);
|
||||
*data = NULL;
|
||||
@ -3715,11 +3710,13 @@ SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst,
|
||||
int
|
||||
SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid, bool watch_tree,
|
||||
u32 completion_filter)
|
||||
u32 completion_filter, u32 max_out_data_len, char **out_data,
|
||||
u32 *plen /* returned data len */)
|
||||
{
|
||||
struct cifs_ses *ses = tcon->ses;
|
||||
struct TCP_Server_Info *server = cifs_pick_channel(ses);
|
||||
struct smb_rqst rqst;
|
||||
struct smb2_change_notify_rsp *smb_rsp;
|
||||
struct kvec iov[1];
|
||||
struct kvec rsp_iov = {NULL, 0};
|
||||
int resp_buftype = CIFS_NO_BUFFER;
|
||||
@ -3735,6 +3732,9 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
memset(&rqst, 0, sizeof(struct smb_rqst));
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
if (plen)
|
||||
*plen = 0;
|
||||
|
||||
rqst.rq_iov = iov;
|
||||
rqst.rq_nvec = 1;
|
||||
|
||||
@ -3753,9 +3753,28 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE);
|
||||
trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid,
|
||||
(u8)watch_tree, completion_filter, rc);
|
||||
} else
|
||||
} else {
|
||||
trace_smb3_notify_done(xid, persistent_fid, tcon->tid,
|
||||
ses->Suid, (u8)watch_tree, completion_filter);
|
||||
ses->Suid, (u8)watch_tree, completion_filter);
|
||||
/* validate that notify information is plausible */
|
||||
if ((rsp_iov.iov_base == NULL) ||
|
||||
(rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp)))
|
||||
goto cnotify_exit;
|
||||
|
||||
smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base;
|
||||
|
||||
smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset),
|
||||
le32_to_cpu(smb_rsp->OutputBufferLength), &rsp_iov,
|
||||
sizeof(struct file_notify_information));
|
||||
|
||||
*out_data = kmemdup((char *)smb_rsp + le16_to_cpu(smb_rsp->OutputBufferOffset),
|
||||
le32_to_cpu(smb_rsp->OutputBufferLength), GFP_KERNEL);
|
||||
if (*out_data == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto cnotify_exit;
|
||||
} else
|
||||
*plen = le32_to_cpu(smb_rsp->OutputBufferLength);
|
||||
}
|
||||
|
||||
cnotify_exit:
|
||||
if (rqst.rq_iov)
|
||||
@ -4103,7 +4122,7 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
|
||||
if (request_type & CHAINED_REQUEST) {
|
||||
if (!(request_type & END_OF_CHAIN)) {
|
||||
/* next 8-byte aligned request */
|
||||
*total_len = DIV_ROUND_UP(*total_len, 8) * 8;
|
||||
*total_len = ALIGN(*total_len, 8);
|
||||
shdr->NextCommand = cpu_to_le32(*total_len);
|
||||
} else /* END_OF_CHAIN */
|
||||
shdr->NextCommand = 0;
|
||||
|
@ -56,6 +56,9 @@ struct smb2_rdma_crypto_transform {
|
||||
|
||||
#define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL
|
||||
|
||||
#define SMB2_SYMLINK_STRUCT_SIZE \
|
||||
(sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
|
||||
|
||||
#define SYMLINK_ERROR_TAG 0x4c4d5953
|
||||
|
||||
struct smb2_symlink_err_rsp {
|
||||
|
@ -53,16 +53,12 @@ extern bool smb2_is_valid_oplock_break(char *buffer,
|
||||
struct TCP_Server_Info *srv);
|
||||
extern int smb3_handle_read_data(struct TCP_Server_Info *server,
|
||||
struct mid_q_entry *mid);
|
||||
|
||||
extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
|
||||
struct smb2_file_all_info *src);
|
||||
extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *path,
|
||||
__u32 *reparse_tag);
|
||||
extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb,
|
||||
const char *full_path, FILE_ALL_INFO *data,
|
||||
bool *adjust_tz, bool *symlink);
|
||||
int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse);
|
||||
extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *full_path, __u64 size,
|
||||
struct cifs_sb_info *cifs_sb, bool set_alloc);
|
||||
@ -95,9 +91,9 @@ extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb,
|
||||
const unsigned char *path, char *pbuf,
|
||||
unsigned int *pbytes_read);
|
||||
extern int smb2_open_file(const unsigned int xid,
|
||||
struct cifs_open_parms *oparms,
|
||||
__u32 *oplock, FILE_ALL_INFO *buf);
|
||||
int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path);
|
||||
int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
|
||||
void *buf);
|
||||
extern int smb2_unlock_range(struct cifsFileInfo *cfile,
|
||||
struct file_lock *flock, const unsigned int xid);
|
||||
extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
|
||||
@ -148,7 +144,8 @@ extern int SMB2_ioctl_init(struct cifs_tcon *tcon,
|
||||
extern void SMB2_ioctl_free(struct smb_rqst *rqst);
|
||||
extern int SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid, bool watch_tree,
|
||||
u32 completion_filter);
|
||||
u32 completion_filter, u32 max_out_data_len,
|
||||
char **out_data, u32 *plen /* returned data len */);
|
||||
|
||||
extern int __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid,
|
||||
@ -278,9 +275,9 @@ extern int smb2_query_info_compound(const unsigned int xid,
|
||||
struct kvec *rsp, int *buftype,
|
||||
struct cifs_sb_info *cifs_sb);
|
||||
/* query path info from the server using SMB311 POSIX extensions*/
|
||||
extern int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *sb, const char *path, struct smb311_posix_qinfo *qinf,
|
||||
bool *adjust_tx, bool *symlink);
|
||||
int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse);
|
||||
int posix_info_parse(const void *beg, const void *end,
|
||||
struct smb2_posix_info_parsed *out);
|
||||
int posix_info_sid_size(const void *beg, const void *end);
|
||||
|
Loading…
Reference in New Issue
Block a user