Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
Pull cifs fixes from Steve French: "Including: - nine bug fixes for stable. Some of these we found at the recent two weeks of SMB3 test events/plugfests. - significant improvements in reconnection (e.g. if server or network crashes) especially when mounted with "persistenthandles" or to server which advertises Continuous Availability on the share. - a new mount option "idsfromsid" which improves POSIX compatibility in some cases (when winbind not configured e.g.) by better (and faster) fetching uid/gid from acl (when "cifsacl" mount option is enabled). NB: we are almost complete work on "cifsacl" (querying mode/uid/gid from ACL) for SMB3, but SMB3 support for cifsacl is not included in this set. - improved handling for SMB3 "credits" (even if server is buggy) Still working on two sets of changes: - cifsacl enablement for SMB3 - cleanup of RFC1001 length calculation (so we can handle encryption and multichannel and RDMA) And a couple of new bugs were reported recently (unrelated to above) so will probably have another merge request next week" * 'for-next' of git://git.samba.org/sfrench/cifs-2.6: (21 commits) CIFS: Retrieve uid and gid from special sid if enabled CIFS: Add new mount option to set owner uid and gid from special sids in acl CIFS: Reset read oplock to NONE if we have mandatory locks after reopen CIFS: Fix persistent handles re-opening on reconnect SMB2: Separate RawNTLMSSP authentication from SMB2_sess_setup SMB2: Separate Kerberos authentication from SMB2_sess_setup Expose cifs module parameters in sysfs Cleanup missing frees on some ioctls Enable previous version support Do not send SMB3 SET_INFO request if nothing is changing SMB3: Add mount parameter to allow user to override max credits fs/cifs: reopen persistent handles on reconnect Clarify locking of cifs file and tcon structures and make more granular Fix regression which breaks DFS mounting fs/cifs: keep guid when assigning fid to fileinfo SMB3: GUIDs should be constructed as random but valid uuids Set previous session id correctly on SMB3 reconnect cifs: Limit the overall credit acquired Display number of credits available Add way to query creation time of file via cifs xattr ...
This commit is contained in:
commit
87dbe42a16
|
@ -152,6 +152,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
|||
list_for_each(tmp1, &cifs_tcp_ses_list) {
|
||||
server = list_entry(tmp1, struct TCP_Server_Info,
|
||||
tcp_ses_list);
|
||||
seq_printf(m, "\nNumber of credits: %d", server->credits);
|
||||
i++;
|
||||
list_for_each(tmp2, &server->smb_ses_list) {
|
||||
ses = list_entry(tmp2, struct cifs_ses,
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible
|
||||
* root mountable
|
||||
*/
|
||||
#define CIFS_MOUNT_UID_FROM_ACL 0x2000000 /* try to get UID via special SID */
|
||||
|
||||
struct cifs_sb_info {
|
||||
struct rb_root tlink_tree;
|
||||
|
|
|
@ -36,7 +36,15 @@ struct smb_mnt_fs_info {
|
|||
__u64 cifs_posix_caps;
|
||||
} __packed;
|
||||
|
||||
struct smb_snapshot_array {
|
||||
__u32 number_of_snapshots;
|
||||
__u32 number_of_snapshots_returned;
|
||||
__u32 snapshot_array_size;
|
||||
/* snapshots[]; */
|
||||
} __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)
|
||||
#define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info)
|
||||
#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array)
|
||||
|
|
|
@ -42,6 +42,35 @@ static const struct cifs_sid sid_authusers = {
|
|||
/* group users */
|
||||
static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
|
||||
|
||||
/* S-1-22-1 Unmapped Unix users */
|
||||
static const struct cifs_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22},
|
||||
{cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
|
||||
|
||||
/* S-1-22-2 Unmapped Unix groups */
|
||||
static const struct cifs_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22},
|
||||
{cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
|
||||
|
||||
/*
|
||||
* See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx
|
||||
*/
|
||||
|
||||
/* S-1-5-88 MS NFS and Apple style UID/GID/mode */
|
||||
|
||||
/* S-1-5-88-1 Unix uid */
|
||||
static const struct cifs_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5},
|
||||
{cpu_to_le32(88),
|
||||
cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
|
||||
|
||||
/* S-1-5-88-2 Unix gid */
|
||||
static const struct cifs_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5},
|
||||
{cpu_to_le32(88),
|
||||
cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
|
||||
|
||||
/* S-1-5-88-3 Unix mode */
|
||||
static const struct cifs_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5},
|
||||
{cpu_to_le32(88),
|
||||
cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
|
||||
|
||||
static const struct cred *root_cred;
|
||||
|
||||
static int
|
||||
|
@ -183,6 +212,62 @@ compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
|
|||
return 0; /* sids compare/match */
|
||||
}
|
||||
|
||||
static bool
|
||||
is_well_known_sid(const struct cifs_sid *psid, uint32_t *puid, bool is_group)
|
||||
{
|
||||
int i;
|
||||
int num_subauth;
|
||||
const struct cifs_sid *pwell_known_sid;
|
||||
|
||||
if (!psid || (puid == NULL))
|
||||
return false;
|
||||
|
||||
num_subauth = psid->num_subauth;
|
||||
|
||||
/* check if Mac (or Windows NFS) vs. Samba format for Unix owner SID */
|
||||
if (num_subauth == 2) {
|
||||
if (is_group)
|
||||
pwell_known_sid = &sid_unix_groups;
|
||||
else
|
||||
pwell_known_sid = &sid_unix_users;
|
||||
} else if (num_subauth == 3) {
|
||||
if (is_group)
|
||||
pwell_known_sid = &sid_unix_NFS_groups;
|
||||
else
|
||||
pwell_known_sid = &sid_unix_NFS_users;
|
||||
} else
|
||||
return false;
|
||||
|
||||
/* compare the revision */
|
||||
if (psid->revision != pwell_known_sid->revision)
|
||||
return false;
|
||||
|
||||
/* compare all of the six auth values */
|
||||
for (i = 0; i < NUM_AUTHS; ++i) {
|
||||
if (psid->authority[i] != pwell_known_sid->authority[i]) {
|
||||
cifs_dbg(FYI, "auth %d did not match\n", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_subauth == 2) {
|
||||
if (psid->sub_auth[0] != pwell_known_sid->sub_auth[0])
|
||||
return false;
|
||||
|
||||
*puid = le32_to_cpu(psid->sub_auth[1]);
|
||||
} else /* 3 subauths, ie Windows/Mac style */ {
|
||||
*puid = le32_to_cpu(psid->sub_auth[0]);
|
||||
if ((psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) ||
|
||||
(psid->sub_auth[1] != pwell_known_sid->sub_auth[1]))
|
||||
return false;
|
||||
|
||||
*puid = le32_to_cpu(psid->sub_auth[2]);
|
||||
}
|
||||
|
||||
cifs_dbg(FYI, "Unix UID %d returned from SID\n", *puid);
|
||||
return true; /* well known sid found, uid returned */
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src)
|
||||
{
|
||||
|
@ -276,6 +361,43 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) {
|
||||
uint32_t unix_id;
|
||||
bool is_group;
|
||||
|
||||
if (sidtype != SIDOWNER)
|
||||
is_group = true;
|
||||
else
|
||||
is_group = false;
|
||||
|
||||
if (is_well_known_sid(psid, &unix_id, is_group) == false)
|
||||
goto try_upcall_to_get_id;
|
||||
|
||||
if (is_group) {
|
||||
kgid_t gid;
|
||||
gid_t id;
|
||||
|
||||
id = (gid_t)unix_id;
|
||||
gid = make_kgid(&init_user_ns, id);
|
||||
if (gid_valid(gid)) {
|
||||
fgid = gid;
|
||||
goto got_valid_id;
|
||||
}
|
||||
} else {
|
||||
kuid_t uid;
|
||||
uid_t id;
|
||||
|
||||
id = (uid_t)unix_id;
|
||||
uid = make_kuid(&init_user_ns, id);
|
||||
if (uid_valid(uid)) {
|
||||
fuid = uid;
|
||||
goto got_valid_id;
|
||||
}
|
||||
}
|
||||
/* If unable to find uid/gid easily from SID try via upcall */
|
||||
}
|
||||
|
||||
try_upcall_to_get_id:
|
||||
sidstr = sid_to_key_str(psid, sidtype);
|
||||
if (!sidstr)
|
||||
return -ENOMEM;
|
||||
|
@ -329,6 +451,7 @@ out_revert_creds:
|
|||
* Note that we return 0 here unconditionally. If the mapping
|
||||
* fails then we just fall back to using the mnt_uid/mnt_gid.
|
||||
*/
|
||||
got_valid_id:
|
||||
if (sidtype == SIDOWNER)
|
||||
fattr->cf_uid = fuid;
|
||||
else
|
||||
|
|
|
@ -64,15 +64,15 @@ unsigned int global_secflags = CIFSSEC_DEF;
|
|||
unsigned int sign_CIFS_PDUs = 1;
|
||||
static const struct super_operations cifs_super_ops;
|
||||
unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
|
||||
module_param(CIFSMaxBufSize, uint, 0);
|
||||
module_param(CIFSMaxBufSize, uint, 0444);
|
||||
MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header). "
|
||||
"Default: 16384 Range: 8192 to 130048");
|
||||
unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL;
|
||||
module_param(cifs_min_rcv, uint, 0);
|
||||
module_param(cifs_min_rcv, uint, 0444);
|
||||
MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: "
|
||||
"1 to 64");
|
||||
unsigned int cifs_min_small = 30;
|
||||
module_param(cifs_min_small, uint, 0);
|
||||
module_param(cifs_min_small, uint, 0444);
|
||||
MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 "
|
||||
"Range: 2 to 256");
|
||||
unsigned int cifs_max_pending = CIFS_MAX_REQ;
|
||||
|
@ -271,7 +271,7 @@ cifs_alloc_inode(struct super_block *sb)
|
|||
cifs_inode->createtime = 0;
|
||||
cifs_inode->epoch = 0;
|
||||
#ifdef CONFIG_CIFS_SMB2
|
||||
get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||
generate_random_uuid(cifs_inode->lease_key);
|
||||
#endif
|
||||
/*
|
||||
* Can not set i_flags here - they get immediately overwritten to zero
|
||||
|
@ -469,6 +469,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
|
|||
seq_puts(s, ",posixpaths");
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)
|
||||
seq_puts(s, ",setuids");
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL)
|
||||
seq_puts(s, ",idsfromsid");
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
|
||||
seq_puts(s, ",serverino");
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
|
||||
|
@ -1262,7 +1264,6 @@ init_cifs(void)
|
|||
GlobalTotalActiveXid = 0;
|
||||
GlobalMaxActiveXid = 0;
|
||||
spin_lock_init(&cifs_tcp_ses_lock);
|
||||
spin_lock_init(&cifs_file_list_lock);
|
||||
spin_lock_init(&GlobalMid_Lock);
|
||||
|
||||
get_random_bytes(&cifs_lock_secret, sizeof(cifs_lock_secret));
|
||||
|
|
|
@ -75,6 +75,18 @@
|
|||
#define SMB_ECHO_INTERVAL_MAX 600
|
||||
#define SMB_ECHO_INTERVAL_DEFAULT 60
|
||||
|
||||
/*
|
||||
* Default number of credits to keep available for SMB3.
|
||||
* This value is chosen somewhat arbitrarily. The Windows client
|
||||
* defaults to 128 credits, the Windows server allows clients up to
|
||||
* 512 credits (or 8K for later versions), and the NetApp server
|
||||
* does not limit clients at all. Choose a high enough default value
|
||||
* such that the client shouldn't limit performance, but allow mount
|
||||
* to override (until you approach 64K, where we limit credits to 65000
|
||||
* to reduce possibility of seeing more server credit overflow bugs.
|
||||
*/
|
||||
#define SMB2_MAX_CREDITS_AVAILABLE 32000
|
||||
|
||||
#include "cifspdu.h"
|
||||
|
||||
#ifndef XATTR_DOS_ATTRIB
|
||||
|
@ -376,6 +388,8 @@ struct smb_version_operations {
|
|||
int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *);
|
||||
int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *src_file);
|
||||
int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *src_file, void __user *);
|
||||
int (*query_mf_symlink)(unsigned int, struct cifs_tcon *,
|
||||
struct cifs_sb_info *, const unsigned char *,
|
||||
char *, unsigned int *);
|
||||
|
@ -464,6 +478,7 @@ struct smb_vol {
|
|||
bool retry:1;
|
||||
bool intr:1;
|
||||
bool setuids:1;
|
||||
bool setuidfromacl:1;
|
||||
bool override_uid:1;
|
||||
bool override_gid:1;
|
||||
bool dynperm:1;
|
||||
|
@ -510,6 +525,7 @@ struct smb_vol {
|
|||
struct sockaddr_storage srcaddr; /* allow binding to a local IP */
|
||||
struct nls_table *local_nls;
|
||||
unsigned int echo_interval; /* echo interval in secs */
|
||||
unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
|
||||
};
|
||||
|
||||
#define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \
|
||||
|
@ -567,7 +583,8 @@ struct TCP_Server_Info {
|
|||
bool noblocksnd; /* use blocking sendmsg */
|
||||
bool noautotune; /* do not autotune send buf sizes */
|
||||
bool tcp_nodelay;
|
||||
int credits; /* send no more requests at once */
|
||||
unsigned int credits; /* send no more requests at once */
|
||||
unsigned int max_credits; /* can override large 32000 default at mnt */
|
||||
unsigned int in_flight; /* number of requests on the wire to server */
|
||||
spinlock_t req_lock; /* protect the two values above */
|
||||
struct mutex srv_mutex;
|
||||
|
@ -833,6 +850,7 @@ struct cifs_tcon {
|
|||
struct list_head tcon_list;
|
||||
int tc_count;
|
||||
struct list_head openFileList;
|
||||
spinlock_t open_file_lock; /* protects list above */
|
||||
struct cifs_ses *ses; /* pointer to session associated with */
|
||||
char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
|
||||
char *nativeFileSystem;
|
||||
|
@ -889,7 +907,7 @@ struct cifs_tcon {
|
|||
#endif /* CONFIG_CIFS_STATS2 */
|
||||
__u64 bytes_read;
|
||||
__u64 bytes_written;
|
||||
spinlock_t stat_lock;
|
||||
spinlock_t stat_lock; /* protects the two fields above */
|
||||
#endif /* CONFIG_CIFS_STATS */
|
||||
FILE_SYSTEM_DEVICE_INFO fsDevInfo;
|
||||
FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */
|
||||
|
@ -1040,20 +1058,24 @@ struct cifs_fid_locks {
|
|||
};
|
||||
|
||||
struct cifsFileInfo {
|
||||
/* following two lists are protected by tcon->open_file_lock */
|
||||
struct list_head tlist; /* pointer to next fid owned by tcon */
|
||||
struct list_head flist; /* next fid (file instance) for this inode */
|
||||
/* lock list below protected by cifsi->lock_sem */
|
||||
struct cifs_fid_locks *llist; /* brlocks held by this fid */
|
||||
kuid_t uid; /* allows finding which FileInfo structure */
|
||||
__u32 pid; /* process id who opened file */
|
||||
struct cifs_fid fid; /* file id from remote */
|
||||
struct list_head rlist; /* reconnect list */
|
||||
/* BB add lock scope info here if needed */ ;
|
||||
/* lock scope id (0 if none) */
|
||||
struct dentry *dentry;
|
||||
unsigned int f_flags;
|
||||
struct tcon_link *tlink;
|
||||
unsigned int f_flags;
|
||||
bool invalidHandle:1; /* file closed via session abend */
|
||||
bool oplock_break_cancelled:1;
|
||||
int count; /* refcount protected by cifs_file_list_lock */
|
||||
int count;
|
||||
spinlock_t file_info_lock; /* protects four flag/count fields above */
|
||||
struct mutex fh_mutex; /* prevents reopen race after dead ses*/
|
||||
struct cifs_search_info srch_inf;
|
||||
struct work_struct oplock_break; /* work for oplock breaks */
|
||||
|
@ -1120,7 +1142,7 @@ struct cifs_writedata {
|
|||
|
||||
/*
|
||||
* Take a reference on the file private data. Must be called with
|
||||
* cifs_file_list_lock held.
|
||||
* cfile->file_info_lock held.
|
||||
*/
|
||||
static inline void
|
||||
cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
|
||||
|
@ -1514,8 +1536,10 @@ require use of the stronger protocol */
|
|||
* GlobalMid_Lock protects:
|
||||
* list operations on pending_mid_q and oplockQ
|
||||
* updates to XID counters, multiplex id and SMB sequence numbers
|
||||
* cifs_file_list_lock protects:
|
||||
* list operations on tcp and SMB session lists and tCon lists
|
||||
* tcp_ses_lock protects:
|
||||
* list operations on tcp and SMB session lists
|
||||
* tcon->open_file_lock protects the list of open files hanging off the tcon
|
||||
* cfile->file_info_lock protects counters and fields in cifs file struct
|
||||
* f_owner.lock protects certain per file struct operations
|
||||
* mapping->page_lock protects certain per page operations
|
||||
*
|
||||
|
@ -1547,18 +1571,12 @@ GLOBAL_EXTERN struct list_head cifs_tcp_ses_list;
|
|||
* tcp session, and the list of tcon's per smb session. It also protects
|
||||
* the reference counters for the server, smb session, and tcon. Finally,
|
||||
* changes to the tcon->tidStatus should be done while holding this lock.
|
||||
* generally the locks should be taken in order tcp_ses_lock before
|
||||
* tcon->open_file_lock and that before file->file_info_lock since the
|
||||
* structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file
|
||||
*/
|
||||
GLOBAL_EXTERN spinlock_t cifs_tcp_ses_lock;
|
||||
|
||||
/*
|
||||
* This lock protects the cifs_file->llist and cifs_file->flist
|
||||
* list operations, and updates to some flags (cifs_file->invalidHandle)
|
||||
* It will be moved to either use the tcon->stat_lock or equivalent later.
|
||||
* If cifs_tcp_ses_lock and the lock below are both needed to be held, then
|
||||
* the cifs_tcp_ses_lock must be grabbed first and released last.
|
||||
*/
|
||||
GLOBAL_EXTERN spinlock_t cifs_file_list_lock;
|
||||
|
||||
#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */
|
||||
/* Outstanding dir notify requests */
|
||||
GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
|
||||
|
|
|
@ -193,6 +193,8 @@ extern struct smb_vol *cifs_get_volume_info(char *mount_data,
|
|||
extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *);
|
||||
extern void cifs_umount(struct cifs_sb_info *);
|
||||
extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
|
||||
extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon);
|
||||
|
||||
extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset,
|
||||
__u64 length, __u8 type,
|
||||
struct cifsLockInfo **conf_lock,
|
||||
|
|
|
@ -98,13 +98,13 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
|
|||
struct list_head *tmp1;
|
||||
|
||||
/* list all files open on tree connection and mark them invalid */
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
list_for_each_safe(tmp, tmp1, &tcon->openFileList) {
|
||||
open_file = list_entry(tmp, struct cifsFileInfo, tlist);
|
||||
open_file->invalidHandle = true;
|
||||
open_file->oplock_break_cancelled = true;
|
||||
}
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
/*
|
||||
* BB Add call to invalidate_inodes(sb) for all superblocks mounted
|
||||
* to this tcon.
|
||||
|
|
|
@ -63,7 +63,6 @@ extern mempool_t *cifs_req_poolp;
|
|||
#define TLINK_IDLE_EXPIRE (600 * HZ)
|
||||
|
||||
enum {
|
||||
|
||||
/* Mount options that take no arguments */
|
||||
Opt_user_xattr, Opt_nouser_xattr,
|
||||
Opt_forceuid, Opt_noforceuid,
|
||||
|
@ -76,7 +75,7 @@ enum {
|
|||
Opt_noposixpaths, Opt_nounix,
|
||||
Opt_nocase,
|
||||
Opt_brl, Opt_nobrl,
|
||||
Opt_forcemandatorylock, Opt_setuids,
|
||||
Opt_forcemandatorylock, Opt_setuidfromacl, Opt_setuids,
|
||||
Opt_nosetuids, Opt_dynperm, Opt_nodynperm,
|
||||
Opt_nohard, Opt_nosoft,
|
||||
Opt_nointr, Opt_intr,
|
||||
|
@ -95,7 +94,7 @@ enum {
|
|||
Opt_cruid, Opt_gid, Opt_file_mode,
|
||||
Opt_dirmode, Opt_port,
|
||||
Opt_rsize, Opt_wsize, Opt_actimeo,
|
||||
Opt_echo_interval,
|
||||
Opt_echo_interval, Opt_max_credits,
|
||||
|
||||
/* Mount options which take string value */
|
||||
Opt_user, Opt_pass, Opt_ip,
|
||||
|
@ -148,6 +147,7 @@ static const match_table_t cifs_mount_option_tokens = {
|
|||
{ Opt_forcemandatorylock, "forcemand" },
|
||||
{ Opt_setuids, "setuids" },
|
||||
{ Opt_nosetuids, "nosetuids" },
|
||||
{ Opt_setuidfromacl, "idsfromsid" },
|
||||
{ Opt_dynperm, "dynperm" },
|
||||
{ Opt_nodynperm, "nodynperm" },
|
||||
{ Opt_nohard, "nohard" },
|
||||
|
@ -190,6 +190,7 @@ static const match_table_t cifs_mount_option_tokens = {
|
|||
{ Opt_wsize, "wsize=%s" },
|
||||
{ Opt_actimeo, "actimeo=%s" },
|
||||
{ Opt_echo_interval, "echo_interval=%s" },
|
||||
{ Opt_max_credits, "max_credits=%s" },
|
||||
|
||||
{ Opt_blank_user, "user=" },
|
||||
{ Opt_blank_user, "username=" },
|
||||
|
@ -1376,6 +1377,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
|||
case Opt_nosetuids:
|
||||
vol->setuids = 0;
|
||||
break;
|
||||
case Opt_setuidfromacl:
|
||||
vol->setuidfromacl = 1;
|
||||
break;
|
||||
case Opt_dynperm:
|
||||
vol->dynperm = true;
|
||||
break;
|
||||
|
@ -1586,6 +1590,15 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
|||
}
|
||||
vol->echo_interval = option;
|
||||
break;
|
||||
case Opt_max_credits:
|
||||
if (get_option_ul(args, &option) || (option < 20) ||
|
||||
(option > 60000)) {
|
||||
cifs_dbg(VFS, "%s: Invalid max_credits value\n",
|
||||
__func__);
|
||||
goto cifs_parse_mount_err;
|
||||
}
|
||||
vol->max_credits = option;
|
||||
break;
|
||||
|
||||
/* String Arguments */
|
||||
|
||||
|
@ -2163,7 +2176,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
|
|||
memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
|
||||
sizeof(tcp_ses->dstaddr));
|
||||
#ifdef CONFIG_CIFS_SMB2
|
||||
get_random_bytes(tcp_ses->client_guid, SMB2_CLIENT_GUID_SIZE);
|
||||
generate_random_uuid(tcp_ses->client_guid);
|
||||
#endif
|
||||
/*
|
||||
* at this point we are the only ones with the pointer
|
||||
|
@ -3270,6 +3283,8 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
|
|||
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
|
||||
if (pvolume_info->setuids)
|
||||
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
|
||||
if (pvolume_info->setuidfromacl)
|
||||
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UID_FROM_ACL;
|
||||
if (pvolume_info->server_ino)
|
||||
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
|
||||
if (pvolume_info->remap)
|
||||
|
@ -3598,7 +3613,11 @@ try_mount_again:
|
|||
bdi_destroy(&cifs_sb->bdi);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((volume_info->max_credits < 20) ||
|
||||
(volume_info->max_credits > 60000))
|
||||
server->max_credits = SMB2_MAX_CREDITS_AVAILABLE;
|
||||
else
|
||||
server->max_credits = volume_info->max_credits;
|
||||
/* get a reference to a SMB session */
|
||||
ses = cifs_get_smb_ses(server, volume_info);
|
||||
if (IS_ERR(ses)) {
|
||||
|
@ -3688,14 +3707,16 @@ remote_path_check:
|
|||
goto mount_fail_check;
|
||||
}
|
||||
|
||||
rc = cifs_are_all_path_components_accessible(server,
|
||||
if (rc != -EREMOTE) {
|
||||
rc = cifs_are_all_path_components_accessible(server,
|
||||
xid, tcon, cifs_sb,
|
||||
full_path);
|
||||
if (rc != 0) {
|
||||
cifs_dbg(VFS, "cannot query dirs between root and final path, "
|
||||
"enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
|
||||
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
|
||||
rc = 0;
|
||||
if (rc != 0) {
|
||||
cifs_dbg(VFS, "cannot query dirs between root and final path, "
|
||||
"enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
|
||||
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
kfree(full_path);
|
||||
}
|
||||
|
|
105
fs/cifs/file.c
105
fs/cifs/file.c
|
@ -305,6 +305,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
|||
cfile->tlink = cifs_get_tlink(tlink);
|
||||
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
|
||||
mutex_init(&cfile->fh_mutex);
|
||||
spin_lock_init(&cfile->file_info_lock);
|
||||
|
||||
cifs_sb_active(inode->i_sb);
|
||||
|
||||
|
@ -317,7 +318,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
|||
oplock = 0;
|
||||
}
|
||||
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock)
|
||||
oplock = fid->pending_open->oplock;
|
||||
list_del(&fid->pending_open->olist);
|
||||
|
@ -326,12 +327,13 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
|||
server->ops->set_fid(cfile, fid, oplock);
|
||||
|
||||
list_add(&cfile->tlist, &tcon->openFileList);
|
||||
|
||||
/* if readable file instance put first in list*/
|
||||
if (file->f_mode & FMODE_READ)
|
||||
list_add(&cfile->flist, &cinode->openFileList);
|
||||
else
|
||||
list_add_tail(&cfile->flist, &cinode->openFileList);
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
|
||||
if (fid->purge_cache)
|
||||
cifs_zap_mapping(inode);
|
||||
|
@ -343,16 +345,16 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
|||
struct cifsFileInfo *
|
||||
cifsFileInfo_get(struct cifsFileInfo *cifs_file)
|
||||
{
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&cifs_file->file_info_lock);
|
||||
cifsFileInfo_get_locked(cifs_file);
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&cifs_file->file_info_lock);
|
||||
return cifs_file;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release a reference on the file private data. This may involve closing
|
||||
* the filehandle out on the server. Must be called without holding
|
||||
* cifs_file_list_lock.
|
||||
* tcon->open_file_lock and cifs_file->file_info_lock.
|
||||
*/
|
||||
void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
|
||||
{
|
||||
|
@ -367,11 +369,15 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
|
|||
struct cifs_pending_open open;
|
||||
bool oplock_break_cancelled;
|
||||
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
|
||||
spin_lock(&cifs_file->file_info_lock);
|
||||
if (--cifs_file->count > 0) {
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&cifs_file->file_info_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
return;
|
||||
}
|
||||
spin_unlock(&cifs_file->file_info_lock);
|
||||
|
||||
if (server->ops->get_lease_key)
|
||||
server->ops->get_lease_key(inode, &fid);
|
||||
|
@ -395,7 +401,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
|
|||
set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags);
|
||||
cifs_set_oplock_level(cifsi, 0);
|
||||
}
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
|
||||
oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break);
|
||||
|
||||
|
@ -732,6 +739,15 @@ reopen_success:
|
|||
* to the server to get the new inode info.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If the server returned a read oplock and we have mandatory brlocks,
|
||||
* set oplock level to None.
|
||||
*/
|
||||
if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) {
|
||||
cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n");
|
||||
oplock = 0;
|
||||
}
|
||||
|
||||
server->ops->set_fid(cfile, &cfile->fid, oplock);
|
||||
if (oparms.reconnect)
|
||||
cifs_relock_file(cfile);
|
||||
|
@ -753,6 +769,36 @@ int cifs_close(struct inode *inode, struct file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
cifs_reopen_persistent_handles(struct cifs_tcon *tcon)
|
||||
{
|
||||
struct cifsFileInfo *open_file;
|
||||
struct list_head *tmp;
|
||||
struct list_head *tmp1;
|
||||
struct list_head tmp_list;
|
||||
|
||||
cifs_dbg(FYI, "Reopen persistent handles");
|
||||
INIT_LIST_HEAD(&tmp_list);
|
||||
|
||||
/* list all files open on tree connection, reopen resilient handles */
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
list_for_each(tmp, &tcon->openFileList) {
|
||||
open_file = list_entry(tmp, struct cifsFileInfo, tlist);
|
||||
if (!open_file->invalidHandle)
|
||||
continue;
|
||||
cifsFileInfo_get(open_file);
|
||||
list_add_tail(&open_file->rlist, &tmp_list);
|
||||
}
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
|
||||
list_for_each_safe(tmp, tmp1, &tmp_list) {
|
||||
open_file = list_entry(tmp, struct cifsFileInfo, rlist);
|
||||
cifs_reopen_file(open_file, false /* do not flush */);
|
||||
list_del_init(&open_file->rlist);
|
||||
cifsFileInfo_put(open_file);
|
||||
}
|
||||
}
|
||||
|
||||
int cifs_closedir(struct inode *inode, struct file *file)
|
||||
{
|
||||
int rc = 0;
|
||||
|
@ -772,10 +818,10 @@ int cifs_closedir(struct inode *inode, struct file *file)
|
|||
server = tcon->ses->server;
|
||||
|
||||
cifs_dbg(FYI, "Freeing private data in close dir\n");
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&cfile->file_info_lock);
|
||||
if (server->ops->dir_needs_close(cfile)) {
|
||||
cfile->invalidHandle = true;
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&cfile->file_info_lock);
|
||||
if (server->ops->close_dir)
|
||||
rc = server->ops->close_dir(xid, tcon, &cfile->fid);
|
||||
else
|
||||
|
@ -784,7 +830,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
|
|||
/* not much we can do if it fails anyway, ignore rc */
|
||||
rc = 0;
|
||||
} else
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&cfile->file_info_lock);
|
||||
|
||||
buf = cfile->srch_inf.ntwrk_buf_start;
|
||||
if (buf) {
|
||||
|
@ -1728,12 +1774,13 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
|
|||
{
|
||||
struct cifsFileInfo *open_file = NULL;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
|
||||
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
|
||||
/* only filter by fsuid on multiuser mounts */
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
|
||||
fsuid_only = false;
|
||||
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
/* we could simply get the first_list_entry since write-only entries
|
||||
are always at the end of the list but since the first entry might
|
||||
have a close pending, we go through the whole list */
|
||||
|
@ -1744,8 +1791,8 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
|
|||
if (!open_file->invalidHandle) {
|
||||
/* found a good file */
|
||||
/* lock it so it will not be closed on us */
|
||||
cifsFileInfo_get_locked(open_file);
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
cifsFileInfo_get(open_file);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
return open_file;
|
||||
} /* else might as well continue, and look for
|
||||
another, or simply have the caller reopen it
|
||||
|
@ -1753,7 +1800,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
|
|||
} else /* write only file */
|
||||
break; /* write only files are last so must be done */
|
||||
}
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1762,6 +1809,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
|
|||
{
|
||||
struct cifsFileInfo *open_file, *inv_file = NULL;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct cifs_tcon *tcon;
|
||||
bool any_available = false;
|
||||
int rc;
|
||||
unsigned int refind = 0;
|
||||
|
@ -1777,15 +1825,16 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
|
|||
}
|
||||
|
||||
cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
|
||||
tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
|
||||
/* only filter by fsuid on multiuser mounts */
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
|
||||
fsuid_only = false;
|
||||
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
refind_writable:
|
||||
if (refind > MAX_REOPEN_ATT) {
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
return NULL;
|
||||
}
|
||||
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
|
||||
|
@ -1796,8 +1845,8 @@ refind_writable:
|
|||
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
|
||||
if (!open_file->invalidHandle) {
|
||||
/* found a good writable file */
|
||||
cifsFileInfo_get_locked(open_file);
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
cifsFileInfo_get(open_file);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
return open_file;
|
||||
} else {
|
||||
if (!inv_file)
|
||||
|
@ -1813,24 +1862,24 @@ refind_writable:
|
|||
|
||||
if (inv_file) {
|
||||
any_available = false;
|
||||
cifsFileInfo_get_locked(inv_file);
|
||||
cifsFileInfo_get(inv_file);
|
||||
}
|
||||
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
|
||||
if (inv_file) {
|
||||
rc = cifs_reopen_file(inv_file, false);
|
||||
if (!rc)
|
||||
return inv_file;
|
||||
else {
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
list_move_tail(&inv_file->flist,
|
||||
&cifs_inode->openFileList);
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
cifsFileInfo_put(inv_file);
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
++refind;
|
||||
inv_file = NULL;
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
goto refind_writable;
|
||||
}
|
||||
}
|
||||
|
@ -3612,15 +3661,17 @@ static int cifs_readpage(struct file *file, struct page *page)
|
|||
static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
|
||||
{
|
||||
struct cifsFileInfo *open_file;
|
||||
struct cifs_tcon *tcon =
|
||||
cifs_sb_master_tcon(CIFS_SB(cifs_inode->vfs_inode.i_sb));
|
||||
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
|
||||
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
|||
xid = get_xid();
|
||||
|
||||
cifs_sb = CIFS_SB(inode->i_sb);
|
||||
|
||||
cifs_dbg(VFS, "cifs ioctl 0x%x\n", command);
|
||||
switch (command) {
|
||||
case FS_IOC_GETFLAGS:
|
||||
if (pSMBFile == NULL)
|
||||
|
@ -267,11 +267,23 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
|||
tcon = tlink_tcon(pSMBFile->tlink);
|
||||
rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
|
||||
break;
|
||||
case CIFS_ENUMERATE_SNAPSHOTS:
|
||||
if (arg == 0) {
|
||||
rc = -EINVAL;
|
||||
goto cifs_ioc_exit;
|
||||
}
|
||||
tcon = tlink_tcon(pSMBFile->tlink);
|
||||
if (tcon->ses->server->ops->enum_snapshots)
|
||||
rc = tcon->ses->server->ops->enum_snapshots(xid, tcon,
|
||||
pSMBFile, (void __user *)arg);
|
||||
else
|
||||
rc = -EOPNOTSUPP;
|
||||
break;
|
||||
default:
|
||||
cifs_dbg(FYI, "unsupported ioctl\n");
|
||||
break;
|
||||
}
|
||||
|
||||
cifs_ioc_exit:
|
||||
free_xid(xid);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@ tconInfoAlloc(void)
|
|||
++ret_buf->tc_count;
|
||||
INIT_LIST_HEAD(&ret_buf->openFileList);
|
||||
INIT_LIST_HEAD(&ret_buf->tcon_list);
|
||||
spin_lock_init(&ret_buf->open_file_lock);
|
||||
#ifdef CONFIG_CIFS_STATS
|
||||
spin_lock_init(&ret_buf->stat_lock);
|
||||
#endif
|
||||
|
@ -465,7 +466,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
|
|||
continue;
|
||||
|
||||
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
list_for_each(tmp2, &tcon->openFileList) {
|
||||
netfile = list_entry(tmp2, struct cifsFileInfo,
|
||||
tlist);
|
||||
|
@ -495,11 +496,11 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
|
|||
&netfile->oplock_break);
|
||||
netfile->oplock_break_cancelled = false;
|
||||
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return true;
|
||||
}
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
cifs_dbg(FYI, "No matching file for oplock break\n");
|
||||
return true;
|
||||
|
@ -613,9 +614,9 @@ backup_cred(struct cifs_sb_info *cifs_sb)
|
|||
void
|
||||
cifs_del_pending_open(struct cifs_pending_open *open)
|
||||
{
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&tlink_tcon(open->tlink)->open_file_lock);
|
||||
list_del(&open->olist);
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -635,7 +636,7 @@ void
|
|||
cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
|
||||
struct cifs_pending_open *open)
|
||||
{
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&tlink_tcon(tlink)->open_file_lock);
|
||||
cifs_add_pending_open_locked(fid, tlink, open);
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
|
||||
}
|
||||
|
|
|
@ -597,14 +597,14 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
|
|||
is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) {
|
||||
/* close and restart search */
|
||||
cifs_dbg(FYI, "search backing up - close and restart search\n");
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&cfile->file_info_lock);
|
||||
if (server->ops->dir_needs_close(cfile)) {
|
||||
cfile->invalidHandle = true;
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&cfile->file_info_lock);
|
||||
if (server->ops->close_dir)
|
||||
server->ops->close_dir(xid, tcon, &cfile->fid);
|
||||
} else
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&cfile->file_info_lock);
|
||||
if (cfile->srch_inf.ntwrk_buf_start) {
|
||||
cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n");
|
||||
if (cfile->srch_inf.smallBuf)
|
||||
|
|
|
@ -266,9 +266,15 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
|
|||
struct tcon_link *tlink;
|
||||
int rc;
|
||||
|
||||
if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
|
||||
(buf->LastWriteTime == 0) && (buf->ChangeTime) &&
|
||||
(buf->Attributes == 0))
|
||||
return 0; /* would be a no op, no sense sending this */
|
||||
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_ERR(tlink))
|
||||
return PTR_ERR(tlink);
|
||||
|
||||
rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path,
|
||||
FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf,
|
||||
SMB2_OP_SET_INFO);
|
||||
|
|
|
@ -549,19 +549,19 @@ smb2_is_valid_lease_break(char *buffer)
|
|||
list_for_each(tmp1, &server->smb_ses_list) {
|
||||
ses = list_entry(tmp1, struct cifs_ses, smb_ses_list);
|
||||
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
list_for_each(tmp2, &ses->tcon_list) {
|
||||
tcon = list_entry(tmp2, struct cifs_tcon,
|
||||
tcon_list);
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
cifs_stats_inc(
|
||||
&tcon->stats.cifs_stats.num_oplock_brks);
|
||||
if (smb2_tcon_has_lease(tcon, rsp, lw)) {
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return true;
|
||||
}
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
}
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
}
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
@ -603,7 +603,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
|
|||
tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
|
||||
|
||||
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
list_for_each(tmp2, &tcon->openFileList) {
|
||||
cfile = list_entry(tmp2, struct cifsFileInfo,
|
||||
tlist);
|
||||
|
@ -615,7 +615,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
|
|||
|
||||
cifs_dbg(FYI, "file id match, oplock break\n");
|
||||
cinode = CIFS_I(d_inode(cfile->dentry));
|
||||
|
||||
spin_lock(&cfile->file_info_lock);
|
||||
if (!CIFS_CACHE_WRITE(cinode) &&
|
||||
rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE)
|
||||
cfile->oplock_break_cancelled = true;
|
||||
|
@ -637,14 +637,14 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
|
|||
clear_bit(
|
||||
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
||||
&cinode->flags);
|
||||
|
||||
spin_unlock(&cfile->file_info_lock);
|
||||
queue_work(cifsiod_wq, &cfile->oplock_break);
|
||||
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return true;
|
||||
}
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
cifs_dbg(FYI, "No matching file for oplock break\n");
|
||||
return true;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "cifs_unicode.h"
|
||||
#include "smb2status.h"
|
||||
#include "smb2glob.h"
|
||||
#include "cifs_ioctl.h"
|
||||
|
||||
static int
|
||||
change_conf(struct TCP_Server_Info *server)
|
||||
|
@ -70,6 +71,10 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
|
|||
spin_lock(&server->req_lock);
|
||||
val = server->ops->get_credits_field(server, optype);
|
||||
*val += add;
|
||||
if (*val > 65000) {
|
||||
*val = 65000; /* Don't get near 64K credits, avoid srv bugs */
|
||||
printk_once(KERN_WARNING "server overflowed SMB3 credits\n");
|
||||
}
|
||||
server->in_flight--;
|
||||
if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP)
|
||||
rc = change_conf(server);
|
||||
|
@ -287,7 +292,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
|
|||
cifs_dbg(FYI, "Link Speed %lld\n",
|
||||
le64_to_cpu(out_buf->LinkSpeed));
|
||||
}
|
||||
|
||||
kfree(out_buf);
|
||||
return rc;
|
||||
}
|
||||
#endif /* STATS2 */
|
||||
|
@ -541,6 +546,7 @@ smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
|
|||
server->ops->set_oplock_level(cinode, oplock, fid->epoch,
|
||||
&fid->purge_cache);
|
||||
cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode);
|
||||
memcpy(cfile->fid.create_guid, fid->create_guid, 16);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -699,6 +705,7 @@ smb2_clone_range(const unsigned int xid,
|
|||
|
||||
cchunk_out:
|
||||
kfree(pcchunk);
|
||||
kfree(retbuf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -823,7 +830,6 @@ smb2_duplicate_extents(const unsigned int xid,
|
|||
{
|
||||
int rc;
|
||||
unsigned int ret_data_len;
|
||||
char *retbuf = NULL;
|
||||
struct duplicate_extents_to_file dup_ext_buf;
|
||||
struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
|
||||
|
||||
|
@ -849,7 +855,7 @@ smb2_duplicate_extents(const unsigned int xid,
|
|||
FSCTL_DUPLICATE_EXTENTS_TO_FILE,
|
||||
true /* is_fsctl */, (char *)&dup_ext_buf,
|
||||
sizeof(struct duplicate_extents_to_file),
|
||||
(char **)&retbuf,
|
||||
NULL,
|
||||
&ret_data_len);
|
||||
|
||||
if (ret_data_len > 0)
|
||||
|
@ -872,7 +878,6 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
struct cifsFileInfo *cfile)
|
||||
{
|
||||
struct fsctl_set_integrity_information_req integr_info;
|
||||
char *retbuf = NULL;
|
||||
unsigned int ret_data_len;
|
||||
|
||||
integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED);
|
||||
|
@ -884,11 +889,55 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
FSCTL_SET_INTEGRITY_INFORMATION,
|
||||
true /* is_fsctl */, (char *)&integr_info,
|
||||
sizeof(struct fsctl_set_integrity_information_req),
|
||||
(char **)&retbuf,
|
||||
NULL,
|
||||
&ret_data_len);
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile, void __user *ioc_buf)
|
||||
{
|
||||
char *retbuf = NULL;
|
||||
unsigned int ret_data_len = 0;
|
||||
int rc;
|
||||
struct smb_snapshot_array snapshot_in;
|
||||
|
||||
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid,
|
||||
FSCTL_SRV_ENUMERATE_SNAPSHOTS,
|
||||
true /* is_fsctl */, NULL, 0 /* no input data */,
|
||||
(char **)&retbuf,
|
||||
&ret_data_len);
|
||||
cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n",
|
||||
rc, ret_data_len);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (ret_data_len && (ioc_buf != NULL) && (retbuf != NULL)) {
|
||||
/* Fixup buffer */
|
||||
if (copy_from_user(&snapshot_in, ioc_buf,
|
||||
sizeof(struct smb_snapshot_array))) {
|
||||
rc = -EFAULT;
|
||||
kfree(retbuf);
|
||||
return rc;
|
||||
}
|
||||
if (snapshot_in.snapshot_array_size < sizeof(struct smb_snapshot_array)) {
|
||||
rc = -ERANGE;
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (ret_data_len > snapshot_in.snapshot_array_size)
|
||||
ret_data_len = snapshot_in.snapshot_array_size;
|
||||
|
||||
if (copy_to_user(ioc_buf, retbuf, ret_data_len))
|
||||
rc = -EFAULT;
|
||||
}
|
||||
|
||||
kfree(retbuf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *path, struct cifs_sb_info *cifs_sb,
|
||||
|
@ -1041,7 +1090,7 @@ smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid)
|
|||
static void
|
||||
smb2_new_lease_key(struct cifs_fid *fid)
|
||||
{
|
||||
get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||
generate_random_uuid(fid->lease_key);
|
||||
}
|
||||
|
||||
#define SMB2_SYMLINK_STRUCT_SIZE \
|
||||
|
@ -1654,6 +1703,7 @@ struct smb_version_operations smb21_operations = {
|
|||
.clone_range = smb2_clone_range,
|
||||
.wp_retry_size = smb2_wp_retry_size,
|
||||
.dir_needs_close = smb2_dir_needs_close,
|
||||
.enum_snapshots = smb3_enum_snapshots,
|
||||
};
|
||||
|
||||
struct smb_version_operations smb30_operations = {
|
||||
|
@ -1740,6 +1790,7 @@ struct smb_version_operations smb30_operations = {
|
|||
.wp_retry_size = smb2_wp_retry_size,
|
||||
.dir_needs_close = smb2_dir_needs_close,
|
||||
.fallocate = smb3_fallocate,
|
||||
.enum_snapshots = smb3_enum_snapshots,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_CIFS_SMB311
|
||||
|
@ -1827,6 +1878,7 @@ struct smb_version_operations smb311_operations = {
|
|||
.wp_retry_size = smb2_wp_retry_size,
|
||||
.dir_needs_close = smb2_dir_needs_close,
|
||||
.fallocate = smb3_fallocate,
|
||||
.enum_snapshots = smb3_enum_snapshots,
|
||||
};
|
||||
#endif /* CIFS_SMB311 */
|
||||
|
||||
|
|
|
@ -100,7 +100,21 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
|
|||
hdr->ProtocolId = SMB2_PROTO_NUMBER;
|
||||
hdr->StructureSize = cpu_to_le16(64);
|
||||
hdr->Command = smb2_cmd;
|
||||
hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */
|
||||
if (tcon && tcon->ses && tcon->ses->server) {
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
|
||||
spin_lock(&server->req_lock);
|
||||
/* Request up to 2 credits but don't go over the limit. */
|
||||
if (server->credits >= server->max_credits)
|
||||
hdr->CreditRequest = cpu_to_le16(0);
|
||||
else
|
||||
hdr->CreditRequest = cpu_to_le16(
|
||||
min_t(int, server->max_credits -
|
||||
server->credits, 2));
|
||||
spin_unlock(&server->req_lock);
|
||||
} else {
|
||||
hdr->CreditRequest = cpu_to_le16(2);
|
||||
}
|
||||
hdr->ProcessId = cpu_to_le32((__u16)current->tgid);
|
||||
|
||||
if (!tcon)
|
||||
|
@ -236,8 +250,13 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
|
|||
}
|
||||
|
||||
cifs_mark_open_files_invalid(tcon);
|
||||
|
||||
rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage);
|
||||
mutex_unlock(&tcon->ses->session_mutex);
|
||||
|
||||
if (tcon->use_persistent)
|
||||
cifs_reopen_persistent_handles(tcon);
|
||||
|
||||
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
@ -574,59 +593,42 @@ vneg_out:
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
int
|
||||
SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
|
||||
const struct nls_table *nls_cp)
|
||||
{
|
||||
struct smb2_sess_setup_req *req;
|
||||
struct smb2_sess_setup_rsp *rsp = NULL;
|
||||
struct SMB2_sess_data {
|
||||
unsigned int xid;
|
||||
struct cifs_ses *ses;
|
||||
struct nls_table *nls_cp;
|
||||
void (*func)(struct SMB2_sess_data *);
|
||||
int result;
|
||||
u64 previous_session;
|
||||
|
||||
/* we will send the SMB in three pieces:
|
||||
* a fixed length beginning part, an optional
|
||||
* SPNEGO blob (which can be zero length), and a
|
||||
* last part which will include the strings
|
||||
* and rest of bcc area. This allows us to avoid
|
||||
* a large buffer 17K allocation
|
||||
*/
|
||||
int buf0_type;
|
||||
struct kvec iov[2];
|
||||
int rc = 0;
|
||||
int resp_buftype = CIFS_NO_BUFFER;
|
||||
__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
|
||||
};
|
||||
|
||||
static int
|
||||
SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int rc;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct smb2_sess_setup_req *req;
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
u16 blob_length = 0;
|
||||
struct key *spnego_key = NULL;
|
||||
char *security_blob = NULL;
|
||||
unsigned char *ntlmssp_blob = NULL;
|
||||
bool use_spnego = false; /* else use raw ntlmssp */
|
||||
|
||||
cifs_dbg(FYI, "Session Setup\n");
|
||||
|
||||
if (!server) {
|
||||
WARN(1, "%s: server is NULL!\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are here due to reconnect, free per-smb session key
|
||||
* in case signing was required.
|
||||
*/
|
||||
kfree(ses->auth_key.response);
|
||||
ses->auth_key.response = NULL;
|
||||
|
||||
/*
|
||||
* If memory allocation is successful, caller of this function
|
||||
* frees it.
|
||||
*/
|
||||
ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
|
||||
if (!ses->ntlmssp)
|
||||
return -ENOMEM;
|
||||
ses->ntlmssp->sesskey_per_smbsess = true;
|
||||
|
||||
/* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */
|
||||
if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP)
|
||||
ses->sectype = RawNTLMSSP;
|
||||
|
||||
ssetup_ntlmssp_authenticate:
|
||||
if (phase == NtLmChallenge)
|
||||
phase = NtLmAuthenticate; /* if ntlmssp, now final phase */
|
||||
|
||||
rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
req->hdr.SessionId = 0; /* First session, not a reauthenticate */
|
||||
|
||||
/* if reconnect, we need to send previous sess id, otherwise it is 0 */
|
||||
req->PreviousSessionId = sess_data->previous_session;
|
||||
|
||||
req->Flags = 0; /* MBZ */
|
||||
/* to enable echos and oplocks */
|
||||
req->hdr.CreditRequest = cpu_to_le16(3);
|
||||
|
@ -642,199 +644,368 @@ ssetup_ntlmssp_authenticate:
|
|||
req->Capabilities = 0;
|
||||
req->Channel = 0; /* MBZ */
|
||||
|
||||
iov[0].iov_base = (char *)req;
|
||||
sess_data->iov[0].iov_base = (char *)req;
|
||||
/* 4 for rfc1002 length field and 1 for pad */
|
||||
iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
|
||||
sess_data->iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
|
||||
/*
|
||||
* This variable will be used to clear the buffer
|
||||
* allocated above in case of any error in the calling function.
|
||||
*/
|
||||
sess_data->buf0_type = CIFS_SMALL_BUFFER;
|
||||
|
||||
if (ses->sectype == Kerberos) {
|
||||
#ifdef CONFIG_CIFS_UPCALL
|
||||
struct cifs_spnego_msg *msg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
spnego_key = cifs_get_spnego_key(ses);
|
||||
if (IS_ERR(spnego_key)) {
|
||||
rc = PTR_ERR(spnego_key);
|
||||
spnego_key = NULL;
|
||||
goto ssetup_exit;
|
||||
}
|
||||
static void
|
||||
SMB2_sess_free_buffer(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
free_rsp_buf(sess_data->buf0_type, sess_data->iov[0].iov_base);
|
||||
sess_data->buf0_type = CIFS_NO_BUFFER;
|
||||
}
|
||||
|
||||
msg = spnego_key->payload.data[0];
|
||||
/*
|
||||
* check version field to make sure that cifs.upcall is
|
||||
* sending us a response in an expected form
|
||||
*/
|
||||
if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
|
||||
cifs_dbg(VFS,
|
||||
"bad cifs.upcall version. Expected %d got %d",
|
||||
CIFS_SPNEGO_UPCALL_VERSION, msg->version);
|
||||
rc = -EKEYREJECTED;
|
||||
goto ssetup_exit;
|
||||
}
|
||||
ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
|
||||
GFP_KERNEL);
|
||||
if (!ses->auth_key.response) {
|
||||
cifs_dbg(VFS,
|
||||
"Kerberos can't allocate (%u bytes) memory",
|
||||
msg->sesskey_len);
|
||||
rc = -ENOMEM;
|
||||
goto ssetup_exit;
|
||||
}
|
||||
ses->auth_key.len = msg->sesskey_len;
|
||||
blob_length = msg->secblob_len;
|
||||
iov[1].iov_base = msg->data + msg->sesskey_len;
|
||||
iov[1].iov_len = blob_length;
|
||||
#else
|
||||
rc = -EOPNOTSUPP;
|
||||
goto ssetup_exit;
|
||||
#endif /* CONFIG_CIFS_UPCALL */
|
||||
} else if (phase == NtLmNegotiate) { /* if not krb5 must be ntlmssp */
|
||||
ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
|
||||
GFP_KERNEL);
|
||||
if (ntlmssp_blob == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto ssetup_exit;
|
||||
}
|
||||
build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
|
||||
if (use_spnego) {
|
||||
/* blob_length = build_spnego_ntlmssp_blob(
|
||||
&security_blob,
|
||||
sizeof(struct _NEGOTIATE_MESSAGE),
|
||||
ntlmssp_blob); */
|
||||
/* BB eventually need to add this */
|
||||
cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
|
||||
rc = -EOPNOTSUPP;
|
||||
kfree(ntlmssp_blob);
|
||||
goto ssetup_exit;
|
||||
} else {
|
||||
blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
|
||||
/* with raw NTLMSSP we don't encapsulate in SPNEGO */
|
||||
security_blob = ntlmssp_blob;
|
||||
}
|
||||
iov[1].iov_base = security_blob;
|
||||
iov[1].iov_len = blob_length;
|
||||
} else if (phase == NtLmAuthenticate) {
|
||||
req->hdr.SessionId = ses->Suid;
|
||||
rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
|
||||
nls_cp);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n",
|
||||
rc);
|
||||
goto ssetup_exit; /* BB double check error handling */
|
||||
}
|
||||
if (use_spnego) {
|
||||
/* blob_length = build_spnego_ntlmssp_blob(
|
||||
&security_blob,
|
||||
blob_length,
|
||||
ntlmssp_blob); */
|
||||
cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
|
||||
rc = -EOPNOTSUPP;
|
||||
kfree(ntlmssp_blob);
|
||||
goto ssetup_exit;
|
||||
} else {
|
||||
security_blob = ntlmssp_blob;
|
||||
}
|
||||
iov[1].iov_base = security_blob;
|
||||
iov[1].iov_len = blob_length;
|
||||
} else {
|
||||
cifs_dbg(VFS, "illegal ntlmssp phase\n");
|
||||
rc = -EIO;
|
||||
goto ssetup_exit;
|
||||
}
|
||||
static int
|
||||
SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int rc;
|
||||
struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base;
|
||||
|
||||
/* Testing shows that buffer offset must be at location of Buffer[0] */
|
||||
req->SecurityBufferOffset =
|
||||
cpu_to_le16(sizeof(struct smb2_sess_setup_req) -
|
||||
1 /* pad */ - 4 /* rfc1001 len */);
|
||||
req->SecurityBufferLength = cpu_to_le16(blob_length);
|
||||
cpu_to_le16(sizeof(struct smb2_sess_setup_req) -
|
||||
1 /* pad */ - 4 /* rfc1001 len */);
|
||||
req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len);
|
||||
|
||||
inc_rfc1001_len(req, blob_length - 1 /* pad */);
|
||||
inc_rfc1001_len(req, sess_data->iov[1].iov_len - 1 /* pad */);
|
||||
|
||||
/* BB add code to build os and lm fields */
|
||||
|
||||
rc = SendReceive2(xid, ses, iov, 2, &resp_buftype,
|
||||
CIFS_LOG_ERROR | CIFS_NEG_OP);
|
||||
rc = SendReceive2(sess_data->xid, sess_data->ses,
|
||||
sess_data->iov, 2,
|
||||
&sess_data->buf0_type,
|
||||
CIFS_LOG_ERROR | CIFS_NEG_OP);
|
||||
|
||||
kfree(security_blob);
|
||||
rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base;
|
||||
ses->Suid = rsp->hdr.SessionId;
|
||||
if (resp_buftype != CIFS_NO_BUFFER &&
|
||||
rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
||||
if (phase != NtLmNegotiate) {
|
||||
cifs_dbg(VFS, "Unexpected more processing error\n");
|
||||
goto ssetup_exit;
|
||||
}
|
||||
if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 !=
|
||||
le16_to_cpu(rsp->SecurityBufferOffset)) {
|
||||
cifs_dbg(VFS, "Invalid security buffer offset %d\n",
|
||||
le16_to_cpu(rsp->SecurityBufferOffset));
|
||||
rc = -EIO;
|
||||
goto ssetup_exit;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* NTLMSSP Negotiate sent now processing challenge (response) */
|
||||
phase = NtLmChallenge; /* process ntlmssp challenge */
|
||||
rc = 0; /* MORE_PROCESSING is not an error here but expected */
|
||||
rc = decode_ntlmssp_challenge(rsp->Buffer,
|
||||
le16_to_cpu(rsp->SecurityBufferLength), ses);
|
||||
static int
|
||||
SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int rc = 0;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
|
||||
mutex_lock(&ses->server->srv_mutex);
|
||||
if (ses->server->sign && ses->server->ops->generate_signingkey) {
|
||||
rc = ses->server->ops->generate_signingkey(ses);
|
||||
kfree(ses->auth_key.response);
|
||||
ses->auth_key.response = NULL;
|
||||
if (rc) {
|
||||
cifs_dbg(FYI,
|
||||
"SMB3 session key generation failed\n");
|
||||
mutex_unlock(&ses->server->srv_mutex);
|
||||
goto keygen_exit;
|
||||
}
|
||||
}
|
||||
if (!ses->server->session_estab) {
|
||||
ses->server->sequence_number = 0x2;
|
||||
ses->server->session_estab = true;
|
||||
}
|
||||
mutex_unlock(&ses->server->srv_mutex);
|
||||
|
||||
cifs_dbg(FYI, "SMB2/3 session established successfully\n");
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
ses->status = CifsGood;
|
||||
ses->need_reconnect = false;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
|
||||
keygen_exit:
|
||||
if (!ses->server->sign) {
|
||||
kfree(ses->auth_key.response);
|
||||
ses->auth_key.response = NULL;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_UPCALL
|
||||
static void
|
||||
SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int rc;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct cifs_spnego_msg *msg;
|
||||
struct key *spnego_key = NULL;
|
||||
struct smb2_sess_setup_rsp *rsp = NULL;
|
||||
|
||||
rc = SMB2_sess_alloc_buffer(sess_data);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
spnego_key = cifs_get_spnego_key(ses);
|
||||
if (IS_ERR(spnego_key)) {
|
||||
rc = PTR_ERR(spnego_key);
|
||||
spnego_key = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
msg = spnego_key->payload.data[0];
|
||||
/*
|
||||
* BB eventually add code for SPNEGO decoding of NtlmChallenge blob,
|
||||
* but at least the raw NTLMSSP case works.
|
||||
* check version field to make sure that cifs.upcall is
|
||||
* sending us a response in an expected form
|
||||
*/
|
||||
/*
|
||||
* No tcon so can't do
|
||||
* cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
|
||||
*/
|
||||
if (rc != 0)
|
||||
goto ssetup_exit;
|
||||
if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
|
||||
cifs_dbg(VFS,
|
||||
"bad cifs.upcall version. Expected %d got %d",
|
||||
CIFS_SPNEGO_UPCALL_VERSION, msg->version);
|
||||
rc = -EKEYREJECTED;
|
||||
goto out_put_spnego_key;
|
||||
}
|
||||
|
||||
ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
|
||||
GFP_KERNEL);
|
||||
if (!ses->auth_key.response) {
|
||||
cifs_dbg(VFS,
|
||||
"Kerberos can't allocate (%u bytes) memory",
|
||||
msg->sesskey_len);
|
||||
rc = -ENOMEM;
|
||||
goto out_put_spnego_key;
|
||||
}
|
||||
ses->auth_key.len = msg->sesskey_len;
|
||||
|
||||
sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
|
||||
sess_data->iov[1].iov_len = msg->secblob_len;
|
||||
|
||||
rc = SMB2_sess_sendreceive(sess_data);
|
||||
if (rc)
|
||||
goto out_put_spnego_key;
|
||||
|
||||
rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
|
||||
ses->Suid = rsp->hdr.SessionId;
|
||||
|
||||
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
||||
if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
|
||||
cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
|
||||
ssetup_exit:
|
||||
free_rsp_buf(resp_buftype, rsp);
|
||||
|
||||
/* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */
|
||||
if ((phase == NtLmChallenge) && (rc == 0))
|
||||
goto ssetup_ntlmssp_authenticate;
|
||||
rc = SMB2_sess_establish_session(sess_data);
|
||||
out_put_spnego_key:
|
||||
key_invalidate(spnego_key);
|
||||
key_put(spnego_key);
|
||||
out:
|
||||
sess_data->result = rc;
|
||||
sess_data->func = NULL;
|
||||
SMB2_sess_free_buffer(sess_data);
|
||||
}
|
||||
#else
|
||||
static void
|
||||
SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
|
||||
sess_data->result = -EOPNOTSUPP;
|
||||
sess_data->func = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data);
|
||||
|
||||
static void
|
||||
SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int rc;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct smb2_sess_setup_rsp *rsp = NULL;
|
||||
char *ntlmssp_blob = NULL;
|
||||
bool use_spnego = false; /* else use raw ntlmssp */
|
||||
u16 blob_length = 0;
|
||||
|
||||
/*
|
||||
* If memory allocation is successful, caller of this function
|
||||
* frees it.
|
||||
*/
|
||||
ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
|
||||
if (!ses->ntlmssp) {
|
||||
rc = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
ses->ntlmssp->sesskey_per_smbsess = true;
|
||||
|
||||
rc = SMB2_sess_alloc_buffer(sess_data);
|
||||
if (rc)
|
||||
goto out_err;
|
||||
|
||||
ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
|
||||
GFP_KERNEL);
|
||||
if (ntlmssp_blob == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
|
||||
if (use_spnego) {
|
||||
/* BB eventually need to add this */
|
||||
cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
} else {
|
||||
blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
|
||||
/* with raw NTLMSSP we don't encapsulate in SPNEGO */
|
||||
}
|
||||
sess_data->iov[1].iov_base = ntlmssp_blob;
|
||||
sess_data->iov[1].iov_len = blob_length;
|
||||
|
||||
rc = SMB2_sess_sendreceive(sess_data);
|
||||
rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
|
||||
|
||||
/* If true, rc here is expected and not an error */
|
||||
if (sess_data->buf0_type != CIFS_NO_BUFFER &&
|
||||
rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED)
|
||||
rc = 0;
|
||||
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 !=
|
||||
le16_to_cpu(rsp->SecurityBufferOffset)) {
|
||||
cifs_dbg(VFS, "Invalid security buffer offset %d\n",
|
||||
le16_to_cpu(rsp->SecurityBufferOffset));
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
rc = decode_ntlmssp_challenge(rsp->Buffer,
|
||||
le16_to_cpu(rsp->SecurityBufferLength), ses);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
|
||||
|
||||
|
||||
ses->Suid = rsp->hdr.SessionId;
|
||||
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
||||
if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
|
||||
cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
|
||||
|
||||
out:
|
||||
kfree(ntlmssp_blob);
|
||||
SMB2_sess_free_buffer(sess_data);
|
||||
if (!rc) {
|
||||
mutex_lock(&server->srv_mutex);
|
||||
if (server->sign && server->ops->generate_signingkey) {
|
||||
rc = server->ops->generate_signingkey(ses);
|
||||
kfree(ses->auth_key.response);
|
||||
ses->auth_key.response = NULL;
|
||||
if (rc) {
|
||||
cifs_dbg(FYI,
|
||||
"SMB3 session key generation failed\n");
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
goto keygen_exit;
|
||||
}
|
||||
}
|
||||
if (!server->session_estab) {
|
||||
server->sequence_number = 0x2;
|
||||
server->session_estab = true;
|
||||
}
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
|
||||
cifs_dbg(FYI, "SMB2/3 session established successfully\n");
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
ses->status = CifsGood;
|
||||
ses->need_reconnect = false;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
|
||||
keygen_exit:
|
||||
if (!server->sign) {
|
||||
kfree(ses->auth_key.response);
|
||||
ses->auth_key.response = NULL;
|
||||
}
|
||||
if (spnego_key) {
|
||||
key_invalidate(spnego_key);
|
||||
key_put(spnego_key);
|
||||
sess_data->result = 0;
|
||||
sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate;
|
||||
return;
|
||||
}
|
||||
out_err:
|
||||
kfree(ses->ntlmssp);
|
||||
ses->ntlmssp = NULL;
|
||||
sess_data->result = rc;
|
||||
sess_data->func = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int rc;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct smb2_sess_setup_req *req;
|
||||
struct smb2_sess_setup_rsp *rsp = NULL;
|
||||
unsigned char *ntlmssp_blob = NULL;
|
||||
bool use_spnego = false; /* else use raw ntlmssp */
|
||||
u16 blob_length = 0;
|
||||
|
||||
rc = SMB2_sess_alloc_buffer(sess_data);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base;
|
||||
req->hdr.SessionId = ses->Suid;
|
||||
|
||||
rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
|
||||
sess_data->nls_cp);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (use_spnego) {
|
||||
/* BB eventually need to add this */
|
||||
cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
sess_data->iov[1].iov_base = ntlmssp_blob;
|
||||
sess_data->iov[1].iov_len = blob_length;
|
||||
|
||||
rc = SMB2_sess_sendreceive(sess_data);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
|
||||
|
||||
ses->Suid = rsp->hdr.SessionId;
|
||||
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
||||
if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
|
||||
cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
|
||||
|
||||
rc = SMB2_sess_establish_session(sess_data);
|
||||
out:
|
||||
kfree(ntlmssp_blob);
|
||||
SMB2_sess_free_buffer(sess_data);
|
||||
kfree(ses->ntlmssp);
|
||||
ses->ntlmssp = NULL;
|
||||
sess_data->result = rc;
|
||||
sess_data->func = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP)
|
||||
ses->sectype = RawNTLMSSP;
|
||||
|
||||
switch (ses->sectype) {
|
||||
case Kerberos:
|
||||
sess_data->func = SMB2_auth_kerberos;
|
||||
break;
|
||||
case RawNTLMSSP:
|
||||
sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate;
|
||||
break;
|
||||
default:
|
||||
cifs_dbg(VFS, "secType %d not supported!\n", ses->sectype);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
|
||||
const struct nls_table *nls_cp)
|
||||
{
|
||||
int rc = 0;
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
struct SMB2_sess_data *sess_data;
|
||||
|
||||
cifs_dbg(FYI, "Session Setup\n");
|
||||
|
||||
if (!server) {
|
||||
WARN(1, "%s: server is NULL!\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL);
|
||||
if (!sess_data)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = SMB2_select_sec(ses, sess_data);
|
||||
if (rc)
|
||||
goto out;
|
||||
sess_data->xid = xid;
|
||||
sess_data->ses = ses;
|
||||
sess_data->buf0_type = CIFS_NO_BUFFER;
|
||||
sess_data->nls_cp = (struct nls_table *) nls_cp;
|
||||
|
||||
while (sess_data->func)
|
||||
sess_data->func(sess_data);
|
||||
|
||||
rc = sess_data->result;
|
||||
out:
|
||||
kfree(sess_data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1164,7 +1335,7 @@ create_durable_v2_buf(struct cifs_fid *pfid)
|
|||
|
||||
buf->dcontext.Timeout = 0; /* Should this be configurable by workload */
|
||||
buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
|
||||
get_random_bytes(buf->dcontext.CreateGuid, 16);
|
||||
generate_random_uuid(buf->dcontext.CreateGuid);
|
||||
memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
|
||||
|
||||
/* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */
|
||||
|
@ -2057,6 +2228,7 @@ smb2_async_readv(struct cifs_readdata *rdata)
|
|||
if (rdata->credits) {
|
||||
buf->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
|
||||
SMB2_MAX_BUFFER_SIZE));
|
||||
buf->CreditRequest = buf->CreditCharge;
|
||||
spin_lock(&server->req_lock);
|
||||
server->credits += rdata->credits -
|
||||
le16_to_cpu(buf->CreditCharge);
|
||||
|
@ -2243,6 +2415,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
|
|||
if (wdata->credits) {
|
||||
req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
|
||||
SMB2_MAX_BUFFER_SIZE));
|
||||
req->hdr.CreditRequest = req->hdr.CreditCharge;
|
||||
spin_lock(&server->req_lock);
|
||||
server->credits += wdata->credits -
|
||||
le16_to_cpu(req->hdr.CreditCharge);
|
||||
|
|
|
@ -276,7 +276,7 @@ struct smb2_sess_setup_req {
|
|||
__le32 Channel;
|
||||
__le16 SecurityBufferOffset;
|
||||
__le16 SecurityBufferLength;
|
||||
__le64 PreviousSessionId;
|
||||
__u64 PreviousSessionId;
|
||||
__u8 Buffer[1]; /* variable length GSS security buffer */
|
||||
} __packed;
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@
|
|||
|
||||
#define MAX_EA_VALUE_SIZE 65535
|
||||
#define CIFS_XATTR_CIFS_ACL "system.cifs_acl"
|
||||
|
||||
#define CIFS_XATTR_ATTRIB "cifs.dosattrib" /* full name: user.cifs.dosattrib */
|
||||
#define CIFS_XATTR_CREATETIME "cifs.creationtime" /* user.cifs.creationtime */
|
||||
/* BB need to add server (Samba e.g) support for security and trusted prefix */
|
||||
|
||||
enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT };
|
||||
|
@ -144,6 +145,54 @@ out:
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int cifs_attrib_get(struct dentry *dentry,
|
||||
struct inode *inode, void *value,
|
||||
size_t size)
|
||||
{
|
||||
ssize_t rc;
|
||||
__u32 *pattribute;
|
||||
|
||||
rc = cifs_revalidate_dentry_attr(dentry);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if ((value == NULL) || (size == 0))
|
||||
return sizeof(__u32);
|
||||
else if (size < sizeof(__u32))
|
||||
return -ERANGE;
|
||||
|
||||
/* return dos attributes as pseudo xattr */
|
||||
pattribute = (__u32 *)value;
|
||||
*pattribute = CIFS_I(inode)->cifsAttrs;
|
||||
|
||||
return sizeof(__u32);
|
||||
}
|
||||
|
||||
static int cifs_creation_time_get(struct dentry *dentry, struct inode *inode,
|
||||
void *value, size_t size)
|
||||
{
|
||||
ssize_t rc;
|
||||
__u64 * pcreatetime;
|
||||
|
||||
rc = cifs_revalidate_dentry_attr(dentry);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if ((value == NULL) || (size == 0))
|
||||
return sizeof(__u64);
|
||||
else if (size < sizeof(__u64))
|
||||
return -ERANGE;
|
||||
|
||||
/* return dos attributes as pseudo xattr */
|
||||
pcreatetime = (__u64 *)value;
|
||||
*pcreatetime = CIFS_I(inode)->createtime;
|
||||
return sizeof(__u64);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int cifs_xattr_get(const struct xattr_handler *handler,
|
||||
struct dentry *dentry, struct inode *inode,
|
||||
const char *name, void *value, size_t size)
|
||||
|
@ -168,10 +217,19 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
|
|||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
/* return dos attributes as pseudo xattr */
|
||||
|
||||
/* return alt name if available as pseudo attr */
|
||||
switch (handler->flags) {
|
||||
case XATTR_USER:
|
||||
cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name);
|
||||
if (strcmp(name, CIFS_XATTR_ATTRIB) == 0) {
|
||||
rc = cifs_attrib_get(dentry, inode, value, size);
|
||||
break;
|
||||
} else if (strcmp(name, CIFS_XATTR_CREATETIME) == 0) {
|
||||
rc = cifs_creation_time_get(dentry, inode, value, size);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
|
||||
goto out;
|
||||
|
||||
|
|
Loading…
Reference in New Issue