22 smb3/cifs client fixes and two related changes (for unicode mapping)

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmTvk44ACgkQiiy9cAdy
 T1GT+wwAkiM+BFVoW0QhLK9ztPptYofGiTKr1AySV09AWos9Fwdhv0aS4LDKhauW
 PVsORfnFcLdyAHtgX2DlhJHMpLWDz3Z51KWiUSo7AAZjIp/4K0yEarg4WKPtUPN0
 PMET2OuqAfIfYLCxSZYFjiGK6xgSJEz+xIhX0qJPRZsyJp50WlFlyZRUfFa+6hXt
 pguatCVw4qhP9hkdcklCY8rwlFDdWEHj9wD/PB2Qschw4gzxDUMwOJjDgT6PNxjA
 SAC6J+NQVtMcnASd5pn0+Mbc+vNfKZ0PM+KZcDrJphcBz+arY6Hu57v3/yu2y++L
 DqRI6QtEwVmHzytM51x5JaWFE0Asj/NsH69LVm4bXVIkkcXBut6lLhrd/KVSP+xN
 LY4EcYEoufAAaecrQrMO4x2Tm10f+GMi1Fh9NvpLZVRrUXy4rdxLP2aC+q+i3uY3
 34FaAbpjQ7NJq2yZTL8xDOdCvi8E3t58DsBv4jA9Y/SYGWYao8Kw0vxhCt0SVZPc
 HaoMfkxl
 =CtQO
 -----END PGP SIGNATURE-----

Merge tag '6.6-rc-smb3-client-fixes-part1' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client updates from Steve French:

 - fixes for excessive stack usage

 - multichannel reconnect improvements

 - DFS fix and cleanup patches

 - move UCS-2 conversion code to fs/nls and update cifs and jfs to use
   them

 - cleanup patch for compounding, one to fix confusing function name

 - inode number collision fix

 - reparse point fixes (including avoiding an extra unneeded query on
   symlinks) and a minor cleanup

 - directory lease (caching) improvement

* tag '6.6-rc-smb3-client-fixes-part1' of git://git.samba.org/sfrench/cifs-2.6: (24 commits)
  fs/jfs: Use common ucs2 upper case table
  fs/smb/client: Use common code in client
  fs/smb: Swing unicode common code from smb->NLS
  fs/smb: Remove unicode 'lower' tables
  SMB3: rename macro CIFS_SERVER_IS_CHAN to avoid confusion
  [SMB3] send channel sequence number in SMB3 requests after reconnects
  cifs: update desired access while requesting for directory lease
  smb: client: reduce stack usage in smb2_query_reparse_point()
  smb: client: reduce stack usage in smb2_query_info_compound()
  smb: client: reduce stack usage in smb2_set_ea()
  smb: client: reduce stack usage in smb_send_rqst()
  smb: client: reduce stack usage in cifs_demultiplex_thread()
  smb: client: reduce stack usage in cifs_try_adding_channels()
  smb: cilent: set reparse mount points as automounts
  smb: client: query reparse points in older dialects
  smb: client: do not query reparse points twice on symlinks
  smb: client: parse reparse point flag in create response
  smb: client: get rid of dfs code dep in namespace.c
  smb: client: get rid of dfs naming in automount code
  smb: client: rename cifs_dfs_ref.c to namespace.c
  ...
This commit is contained in:
Linus Torvalds 2023-08-30 21:01:40 -07:00
commit b97d64c722
43 changed files with 1384 additions and 2028 deletions

View File

@ -3,6 +3,7 @@ config JFS_FS
tristate "JFS filesystem support" tristate "JFS filesystem support"
select BUFFER_HEAD select BUFFER_HEAD
select NLS select NLS
select NLS_UCS2_UTILS
select CRC32 select CRC32
select LEGACY_DIRECT_IO select LEGACY_DIRECT_IO
help help

View File

@ -9,7 +9,7 @@ jfs-y := super.o file.o inode.o namei.o jfs_mount.o jfs_umount.o \
jfs_xtree.o jfs_imap.o jfs_debug.o jfs_dmap.o \ jfs_xtree.o jfs_imap.o jfs_debug.o jfs_dmap.o \
jfs_unicode.o jfs_dtree.o jfs_inode.o jfs_discard.o \ jfs_unicode.o jfs_dtree.o jfs_inode.o jfs_discard.o \
jfs_extent.o symlink.o jfs_metapage.o \ jfs_extent.o symlink.o jfs_metapage.o \
jfs_logmgr.o jfs_txnmgr.o jfs_uniupr.o \ jfs_logmgr.o jfs_txnmgr.o \
resize.o xattr.o ioctl.o resize.o xattr.o ioctl.o
jfs-$(CONFIG_JFS_POSIX_ACL) += acl.o jfs-$(CONFIG_JFS_POSIX_ACL) += acl.o

View File

@ -8,16 +8,9 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include "../nls/nls_ucs2_data.h"
#include "jfs_types.h" #include "jfs_types.h"
typedef struct {
wchar_t start;
wchar_t end;
signed char *table;
} UNICASERANGE;
extern signed char UniUpperTable[512];
extern UNICASERANGE UniUpperRange[];
extern int get_UCSname(struct component_name *, struct dentry *); extern int get_UCSname(struct component_name *, struct dentry *);
extern int jfs_strfromUCS_le(char *, const __le16 *, int, struct nls_table *); extern int jfs_strfromUCS_le(char *, const __le16 *, int, struct nls_table *);
@ -107,12 +100,12 @@ static inline wchar_t *UniStrncpy_from_le(wchar_t * ucs1, const __le16 * ucs2,
*/ */
static inline wchar_t UniToupper(wchar_t uc) static inline wchar_t UniToupper(wchar_t uc)
{ {
UNICASERANGE *rp; const struct UniCaseRange *rp;
if (uc < sizeof(UniUpperTable)) { /* Latin characters */ if (uc < sizeof(NlsUniUpperTable)) { /* Latin characters */
return uc + UniUpperTable[uc]; /* Use base tables */ return uc + NlsUniUpperTable[uc]; /* Use base tables */
} else { } else {
rp = UniUpperRange; /* Use range tables */ rp = NlsUniUpperRange; /* Use range tables */
while (rp->start) { while (rp->start) {
if (uc < rp->start) /* Before start of range */ if (uc < rp->start) /* Before start of range */
return uc; /* Uppercase = input */ return uc; /* Uppercase = input */

View File

@ -1,121 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) International Business Machines Corp., 2000-2002
*/
#include <linux/fs.h>
#include "jfs_unicode.h"
/*
* Latin upper case
*/
signed char UniUpperTable[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */
0,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 060-06f */
-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, 0, 0, 0, 0, 0, /* 070-07f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */
-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 0e0-0ef */
-32,-32,-32,-32,-32,-32,-32, 0,-32,-32,-32,-32,-32,-32,-32,121, /* 0f0-0ff */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */
0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */
-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */
0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */
0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */
0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */
0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */
-1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */
0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */
-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1,-79, 0, -1, /* 1d0-1df */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */
0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */
};
/* Upper case range - Greek */
static signed char UniCaseRangeU03a0[47] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-38,-37,-37,-37, /* 3a0-3af */
0,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 3b0-3bf */
-32,-32,-31,-32,-32,-32,-32,-32,-32,-32,-32,-32,-64,-63,-63,
};
/* Upper case range - Cyrillic */
static signed char UniCaseRangeU0430[48] = {
-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 430-43f */
-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 440-44f */
0,-80,-80,-80,-80,-80,-80,-80,-80,-80,-80,-80,-80, 0,-80,-80, /* 450-45f */
};
/* Upper case range - Extended cyrillic */
static signed char UniCaseRangeU0490[61] = {
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */
0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1,
};
/* Upper case range - Extended latin and greek */
static signed char UniCaseRangeU1e00[509] = {
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */
0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0,-59, 0, -1, 0, -1, /* 1e90-1e9f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */
8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */
8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */
0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */
74, 74, 86, 86, 86, 86,100,100, 0, 0,112,112,126,126, 0, 0, /* 1f70-1f7f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */
8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */
0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */
8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */
8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */
0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
/* Upper case range - Wide latin */
static signed char UniCaseRangeUff40[27] = {
0,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* ff40-ff4f */
-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,
};
/*
* Upper Case Range
*/
UNICASERANGE UniUpperRange[] = {
{ 0x03a0, 0x03ce, UniCaseRangeU03a0 },
{ 0x0430, 0x045f, UniCaseRangeU0430 },
{ 0x0490, 0x04cc, UniCaseRangeU0490 },
{ 0x1e00, 0x1ffc, UniCaseRangeU1e00 },
{ 0xff40, 0xff5a, UniCaseRangeUff40 },
{ 0 }
};

View File

@ -617,4 +617,12 @@ config NLS_UTF8
input/output character sets. Say Y here for the UTF-8 encoding of input/output character sets. Say Y here for the UTF-8 encoding of
the Unicode/ISO9646 universal character set. the Unicode/ISO9646 universal character set.
config NLS_UCS2_UTILS
tristate "NLS UCS-2 UTILS"
help
Set of older UCS-2 conversion utilities and tables used by some
filesystems including SMB/CIFS. This includes upper case conversion
tables. This will automatically be selected when the filesystem
that uses it is selected.
endif # NLS endif # NLS

View File

@ -54,3 +54,4 @@ obj-$(CONFIG_NLS_MAC_INUIT) += mac-inuit.o
obj-$(CONFIG_NLS_MAC_ROMANIAN) += mac-romanian.o obj-$(CONFIG_NLS_MAC_ROMANIAN) += mac-romanian.o
obj-$(CONFIG_NLS_MAC_ROMAN) += mac-roman.o obj-$(CONFIG_NLS_MAC_ROMAN) += mac-roman.o
obj-$(CONFIG_NLS_MAC_TURKISH) += mac-turkish.o obj-$(CONFIG_NLS_MAC_TURKISH) += mac-turkish.o
obj-$(CONFIG_NLS_UCS2_UTILS) += nls_ucs2_utils.o

15
fs/nls/nls_ucs2_data.h Normal file
View File

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _NLS_UCS2_DATA_H
#define _NLS_UCS2_DATA_H
struct UniCaseRange {
wchar_t start;
wchar_t end;
signed char *table;
};
extern signed char NlsUniUpperTable[512];
extern const struct UniCaseRange NlsUniUpperRange[];
#endif /* _NLS_UCS2_DATA_H */

View File

@ -1,19 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */ // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Some of the source code in this file came from fs/cifs/uniupr.h * Some of the source code in this file came from fs/cifs/uniupr.h
* Copyright (c) International Business Machines Corp., 2000,2002 * Copyright (c) International Business Machines Corp., 2000,2002
* *
* uniupr.h - Unicode compressed case ranges * Some of the source code in this file came from fs/cifs/cifs_unicode.c
*
* Copyright (c) International Business Machines Corp., 2000,2009
* Modified by Steve French (sfrench@us.ibm.com)
* Modified by Namjae Jeon (linkinjeon@kernel.org)
* *
*/ */
#ifndef __KSMBD_UNIUPR_H #include <linux/fs.h>
#define __KSMBD_UNIUPR_H #include <linux/module.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include "nls_ucs2_utils.h"
MODULE_LICENSE("GPL");
#ifndef UNIUPR_NOUPPER
/* /*
* Latin upper case * Latin upper case
*/ */
signed char SmbUniUpperTable[512] = { signed char NlsUniUpperTable[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */
@ -51,6 +59,7 @@ signed char SmbUniUpperTable[512] = {
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */
0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */
}; };
EXPORT_SYMBOL_GPL(NlsUniUpperTable);
/* Upper case range - Greek */ /* Upper case range - Greek */
static signed char UniCaseRangeU03a0[47] = { static signed char UniCaseRangeU03a0[47] = {
@ -126,7 +135,7 @@ static signed char UniCaseRangeUff40[27] = {
/* /*
* Upper Case Range * Upper Case Range
*/ */
const struct UniCaseRange SmbUniUpperRange[] = { const struct UniCaseRange NlsUniUpperRange[] = {
{0x03a0, 0x03ce, UniCaseRangeU03a0}, {0x03a0, 0x03ce, UniCaseRangeU03a0},
{0x0430, 0x045f, UniCaseRangeU0430}, {0x0430, 0x045f, UniCaseRangeU0430},
{0x0490, 0x04cc, UniCaseRangeU0490}, {0x0490, 0x04cc, UniCaseRangeU0490},
@ -134,135 +143,4 @@ const struct UniCaseRange SmbUniUpperRange[] = {
{0xff40, 0xff5a, UniCaseRangeUff40}, {0xff40, 0xff5a, UniCaseRangeUff40},
{0} {0}
}; };
#endif EXPORT_SYMBOL_GPL(NlsUniUpperRange);
#ifndef UNIUPR_NOLOWER
/*
* Latin lower case
*/
signed char CifsUniLowerTable[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */
0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, /* 040-04f */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0,
0, 0, 0, /* 050-05f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, /* 0c0-0cf */
32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32,
32, 32, 32, 0, /* 0d0-0df */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */
0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */
0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */
1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0,
0, /* 170-17f */
0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79,
0, /* 180-18f */
0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */
1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */
0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */
0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */
0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */
};
/* Lower case range - Greek */
static signed char UniCaseRangeL0380[44] = {
0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */
0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, /* 390-39f */
32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32,
};
/* Lower case range - Cyrillic */
static signed char UniCaseRangeL0400[48] = {
0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
0, 80, 80, /* 400-40f */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, /* 410-41f */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, /* 420-42f */
};
/* Lower case range - Extended cyrillic */
static signed char UniCaseRangeL0490[60] = {
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */
0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1,
};
/* Lower case range - Extended latin and greek */
static signed char UniCaseRangeL1e00[504] = {
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */
1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */
0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */
0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0,
0, 0, /* 1fc0-1fcf */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0,
0, 0, /* 1fe0-1fef */
0, 0, 0, 0, 0, 0, 0, 0,
};
/* Lower case range - Wide latin */
static signed char UniCaseRangeLff20[27] = {
0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, /* ff20-ff2f */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
};
/*
* Lower Case Range
*/
const struct UniCaseRange CifsUniLowerRange[] = {
{0x0380, 0x03ab, UniCaseRangeL0380},
{0x0400, 0x042f, UniCaseRangeL0400},
{0x0490, 0x04cb, UniCaseRangeL0490},
{0x1e00, 0x1ff7, UniCaseRangeL1e00},
{0xff20, 0xff3a, UniCaseRangeLff20},
{0}
};
#endif
#endif /* __KSMBD_UNIUPR_H */

285
fs/nls/nls_ucs2_utils.h Normal file
View File

@ -0,0 +1,285 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Some of the source code in this file came from fs/cifs/cifs_unicode.c
* and then via server/unicode.c
* cifs_unicode: Unicode kernel case support
*
* Function:
* Convert a unicode character to upper or lower case using
* compressed tables.
*
* Copyright (c) International Business Machines Corp., 2000,2009
*
*
* Notes:
* These APIs are based on the C library functions. The semantics
* should match the C functions but with expanded size operands.
*
* The upper/lower functions are based on a table created by mkupr.
* This is a compressed table of upper and lower case conversion.
*
*/
#ifndef _NLS_UCS2_UTILS_H
#define _NLS_UCS2_UTILS_H
#include <asm/byteorder.h>
#include <linux/types.h>
#include <linux/nls.h>
#include <linux/unicode.h>
#include "nls_ucs2_data.h"
/*
* Windows maps these to the user defined 16 bit Unicode range since they are
* reserved symbols (along with \ and /), otherwise illegal to store
* in filenames in NTFS
*/
#define UNI_ASTERISK ((__u16)('*' + 0xF000))
#define UNI_QUESTION ((__u16)('?' + 0xF000))
#define UNI_COLON ((__u16)(':' + 0xF000))
#define UNI_GRTRTHAN ((__u16)('>' + 0xF000))
#define UNI_LESSTHAN ((__u16)('<' + 0xF000))
#define UNI_PIPE ((__u16)('|' + 0xF000))
#define UNI_SLASH ((__u16)('\\' + 0xF000))
/*
* UniStrcat: Concatenate the second string to the first
*
* Returns:
* Address of the first string
*/
static inline wchar_t *UniStrcat(wchar_t *ucs1, const wchar_t *ucs2)
{
wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */
while (*ucs1++)
/*NULL*/; /* To end of first string */
ucs1--; /* Return to the null */
while ((*ucs1++ = *ucs2++))
/*NULL*/; /* copy string 2 over */
return anchor;
}
/*
* UniStrchr: Find a character in a string
*
* Returns:
* Address of first occurrence of character in string
* or NULL if the character is not in the string
*/
static inline wchar_t *UniStrchr(const wchar_t *ucs, wchar_t uc)
{
while ((*ucs != uc) && *ucs)
ucs++;
if (*ucs == uc)
return (wchar_t *)ucs;
return NULL;
}
/*
* UniStrcmp: Compare two strings
*
* Returns:
* < 0: First string is less than second
* = 0: Strings are equal
* > 0: First string is greater than second
*/
static inline int UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2)
{
while ((*ucs1 == *ucs2) && *ucs1) {
ucs1++;
ucs2++;
}
return (int)*ucs1 - (int)*ucs2;
}
/*
* UniStrcpy: Copy a string
*/
static inline wchar_t *UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2)
{
wchar_t *anchor = ucs1; /* save the start of result string */
while ((*ucs1++ = *ucs2++))
/*NULL*/;
return anchor;
}
/*
* UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes)
*/
static inline size_t UniStrlen(const wchar_t *ucs1)
{
int i = 0;
while (*ucs1++)
i++;
return i;
}
/*
* UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a
* string (length limited)
*/
static inline size_t UniStrnlen(const wchar_t *ucs1, int maxlen)
{
int i = 0;
while (*ucs1++) {
i++;
if (i >= maxlen)
break;
}
return i;
}
/*
* UniStrncat: Concatenate length limited string
*/
static inline wchar_t *UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
wchar_t *anchor = ucs1; /* save pointer to string 1 */
while (*ucs1++)
/*NULL*/;
ucs1--; /* point to null terminator of s1 */
while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */
ucs1++;
ucs2++;
}
*ucs1 = 0; /* Null terminate the result */
return anchor;
}
/*
* UniStrncmp: Compare length limited string
*/
static inline int UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
if (!n)
return 0; /* Null strings are equal */
while ((*ucs1 == *ucs2) && *ucs1 && --n) {
ucs1++;
ucs2++;
}
return (int)*ucs1 - (int)*ucs2;
}
/*
* UniStrncmp_le: Compare length limited string - native to little-endian
*/
static inline int
UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
if (!n)
return 0; /* Null strings are equal */
while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) {
ucs1++;
ucs2++;
}
return (int)*ucs1 - (int)__le16_to_cpu(*ucs2);
}
/*
* UniStrncpy: Copy length limited string with pad
*/
static inline wchar_t *UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
wchar_t *anchor = ucs1;
while (n-- && *ucs2) /* Copy the strings */
*ucs1++ = *ucs2++;
n++;
while (n--) /* Pad with nulls */
*ucs1++ = 0;
return anchor;
}
/*
* UniStrncpy_le: Copy length limited string with pad to little-endian
*/
static inline wchar_t *UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
wchar_t *anchor = ucs1;
while (n-- && *ucs2) /* Copy the strings */
*ucs1++ = __le16_to_cpu(*ucs2++);
n++;
while (n--) /* Pad with nulls */
*ucs1++ = 0;
return anchor;
}
/*
* UniStrstr: Find a string in a string
*
* Returns:
* Address of first match found
* NULL if no matching string is found
*/
static inline wchar_t *UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2)
{
const wchar_t *anchor1 = ucs1;
const wchar_t *anchor2 = ucs2;
while (*ucs1) {
if (*ucs1 == *ucs2) {
/* Partial match found */
ucs1++;
ucs2++;
} else {
if (!*ucs2) /* Match found */
return (wchar_t *)anchor1;
ucs1 = ++anchor1; /* No match */
ucs2 = anchor2;
}
}
if (!*ucs2) /* Both end together */
return (wchar_t *)anchor1; /* Match found */
return NULL; /* No match */
}
#ifndef UNIUPR_NOUPPER
/*
* UniToupper: Convert a unicode character to upper case
*/
static inline wchar_t UniToupper(register wchar_t uc)
{
register const struct UniCaseRange *rp;
if (uc < sizeof(NlsUniUpperTable)) {
/* Latin characters */
return uc + NlsUniUpperTable[uc]; /* Use base tables */
}
rp = NlsUniUpperRange; /* Use range tables */
while (rp->start) {
if (uc < rp->start) /* Before start of range */
return uc; /* Uppercase = input */
if (uc <= rp->end) /* In range */
return uc + rp->table[uc - rp->start];
rp++; /* Try next range */
}
return uc; /* Past last range */
}
/*
* UniStrupr: Upper case a unicode string
*/
static inline __le16 *UniStrupr(register __le16 *upin)
{
register __le16 *up;
up = upin;
while (*up) { /* For all characters */
*up = cpu_to_le16(UniToupper(le16_to_cpu(*up)));
up++;
}
return upin; /* Return input pointer */
}
#endif /* UNIUPR_NOUPPER */
#endif /* _NLS_UCS2_UTILS_H */

View File

@ -3,6 +3,7 @@ config CIFS
tristate "SMB3 and CIFS support (advanced network filesystem)" tristate "SMB3 and CIFS support (advanced network filesystem)"
depends on INET depends on INET
select NLS select NLS
select NLS_UCS2_UTILS
select CRYPTO select CRYPTO
select CRYPTO_MD5 select CRYPTO_MD5
select CRYPTO_SHA256 select CRYPTO_SHA256

View File

@ -11,7 +11,8 @@ cifs-y := trace.o cifsfs.o cifs_debug.o connect.o dir.o file.o \
readdir.o ioctl.o sess.o export.o unc.o winucase.o \ readdir.o ioctl.o sess.o export.o unc.o winucase.o \
smb2ops.o smb2maperror.o smb2transport.o \ smb2ops.o smb2maperror.o smb2transport.o \
smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \ smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \
dns_resolve.o cifs_spnego_negtokeninit.asn1.o asn1.o dns_resolve.o cifs_spnego_negtokeninit.asn1.o asn1.o \
namespace.o
$(obj)/asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.h $(obj)/asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.h
@ -21,7 +22,7 @@ cifs-$(CONFIG_CIFS_XATTR) += xattr.o
cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
cifs-$(CONFIG_CIFS_DFS_UPCALL) += cifs_dfs_ref.o dfs_cache.o dfs.o cifs-$(CONFIG_CIFS_DFS_UPCALL) += dfs_cache.o dfs.o
cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o

View File

@ -218,7 +218,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
.tcon = tcon, .tcon = tcon,
.path = path, .path = path,
.create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE), .create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE),
.desired_access = FILE_READ_ATTRIBUTES, .desired_access = FILE_READ_DATA | FILE_READ_ATTRIBUTES,
.disposition = FILE_OPEN, .disposition = FILE_OPEN,
.fid = pfid, .fid = pfid,
}; };

View File

@ -331,7 +331,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
/* channel info will be printed as a part of sessions below */ /* channel info will be printed as a part of sessions below */
if (CIFS_SERVER_IS_CHAN(server)) if (SERVER_IS_CHAN(server))
continue; continue;
c++; c++;

View File

@ -8,7 +8,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include "cifs_fs_sb.h" #include "cifs_fs_sb.h"
#include "cifs_unicode.h" #include "cifs_unicode.h"
#include "cifs_uniupr.h"
#include "cifspdu.h" #include "cifspdu.h"
#include "cifsglob.h" #include "cifsglob.h"
#include "cifs_debug.h" #include "cifs_debug.h"

View File

@ -21,21 +21,7 @@
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/nls.h> #include <linux/nls.h>
#include "../../nls/nls_ucs2_utils.h"
#define UNIUPR_NOLOWER /* Example to not expand lower case tables */
/*
* Windows maps these to the user defined 16 bit Unicode range since they are
* reserved symbols (along with \ and /), otherwise illegal to store
* in filenames in NTFS
*/
#define UNI_ASTERISK (__u16) ('*' + 0xF000)
#define UNI_QUESTION (__u16) ('?' + 0xF000)
#define UNI_COLON (__u16) (':' + 0xF000)
#define UNI_GRTRTHAN (__u16) ('>' + 0xF000)
#define UNI_LESSTHAN (__u16) ('<' + 0xF000)
#define UNI_PIPE (__u16) ('|' + 0xF000)
#define UNI_SLASH (__u16) ('\\' + 0xF000)
/* /*
* Macs use an older "SFM" mapping of the symbols above. Fortunately it does * Macs use an older "SFM" mapping of the symbols above. Fortunately it does
@ -68,27 +54,6 @@
#define SFM_MAP_UNI_RSVD 1 #define SFM_MAP_UNI_RSVD 1
#define SFU_MAP_UNI_RSVD 2 #define SFU_MAP_UNI_RSVD 2
/* Just define what we want from uniupr.h. We don't want to define the tables
* in each source file.
*/
#ifndef UNICASERANGE_DEFINED
struct UniCaseRange {
wchar_t start;
wchar_t end;
signed char *table;
};
#endif /* UNICASERANGE_DEFINED */
#ifndef UNIUPR_NOUPPER
extern signed char CifsUniUpperTable[512];
extern const struct UniCaseRange CifsUniUpperRange[];
#endif /* UNIUPR_NOUPPER */
#ifndef UNIUPR_NOLOWER
extern signed char CifsUniLowerTable[512];
extern const struct UniCaseRange CifsUniLowerRange[];
#endif /* UNIUPR_NOLOWER */
#ifdef __KERNEL__ #ifdef __KERNEL__
int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
const struct nls_table *cp, int map_type); const struct nls_table *cp, int map_type);
@ -108,297 +73,4 @@ extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen,
wchar_t cifs_toupper(wchar_t in); wchar_t cifs_toupper(wchar_t in);
/*
* UniStrcat: Concatenate the second string to the first
*
* Returns:
* Address of the first string
*/
static inline __le16 *
UniStrcat(__le16 *ucs1, const __le16 *ucs2)
{
__le16 *anchor = ucs1; /* save a pointer to start of ucs1 */
while (*ucs1++) ; /* To end of first string */
ucs1--; /* Return to the null */
while ((*ucs1++ = *ucs2++)) ; /* copy string 2 over */
return anchor;
}
/*
* UniStrchr: Find a character in a string
*
* Returns:
* Address of first occurrence of character in string
* or NULL if the character is not in the string
*/
static inline wchar_t *
UniStrchr(const wchar_t *ucs, wchar_t uc)
{
while ((*ucs != uc) && *ucs)
ucs++;
if (*ucs == uc)
return (wchar_t *) ucs;
return NULL;
}
/*
* UniStrcmp: Compare two strings
*
* Returns:
* < 0: First string is less than second
* = 0: Strings are equal
* > 0: First string is greater than second
*/
static inline int
UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2)
{
while ((*ucs1 == *ucs2) && *ucs1) {
ucs1++;
ucs2++;
}
return (int) *ucs1 - (int) *ucs2;
}
/*
* UniStrcpy: Copy a string
*/
static inline wchar_t *
UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2)
{
wchar_t *anchor = ucs1; /* save the start of result string */
while ((*ucs1++ = *ucs2++)) ;
return anchor;
}
/*
* UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes)
*/
static inline size_t
UniStrlen(const wchar_t *ucs1)
{
int i = 0;
while (*ucs1++)
i++;
return i;
}
/*
* UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a
* string (length limited)
*/
static inline size_t
UniStrnlen(const wchar_t *ucs1, int maxlen)
{
int i = 0;
while (*ucs1++) {
i++;
if (i >= maxlen)
break;
}
return i;
}
/*
* UniStrncat: Concatenate length limited string
*/
static inline wchar_t *
UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
wchar_t *anchor = ucs1; /* save pointer to string 1 */
while (*ucs1++) ;
ucs1--; /* point to null terminator of s1 */
while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */
ucs1++;
ucs2++;
}
*ucs1 = 0; /* Null terminate the result */
return (anchor);
}
/*
* UniStrncmp: Compare length limited string
*/
static inline int
UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
if (!n)
return 0; /* Null strings are equal */
while ((*ucs1 == *ucs2) && *ucs1 && --n) {
ucs1++;
ucs2++;
}
return (int) *ucs1 - (int) *ucs2;
}
/*
* UniStrncmp_le: Compare length limited string - native to little-endian
*/
static inline int
UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
if (!n)
return 0; /* Null strings are equal */
while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) {
ucs1++;
ucs2++;
}
return (int) *ucs1 - (int) __le16_to_cpu(*ucs2);
}
/*
* UniStrncpy: Copy length limited string with pad
*/
static inline wchar_t *
UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
wchar_t *anchor = ucs1;
while (n-- && *ucs2) /* Copy the strings */
*ucs1++ = *ucs2++;
n++;
while (n--) /* Pad with nulls */
*ucs1++ = 0;
return anchor;
}
/*
* UniStrncpy_le: Copy length limited string with pad to little-endian
*/
static inline wchar_t *
UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
wchar_t *anchor = ucs1;
while (n-- && *ucs2) /* Copy the strings */
*ucs1++ = __le16_to_cpu(*ucs2++);
n++;
while (n--) /* Pad with nulls */
*ucs1++ = 0;
return anchor;
}
/*
* UniStrstr: Find a string in a string
*
* Returns:
* Address of first match found
* NULL if no matching string is found
*/
static inline wchar_t *
UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2)
{
const wchar_t *anchor1 = ucs1;
const wchar_t *anchor2 = ucs2;
while (*ucs1) {
if (*ucs1 == *ucs2) {
/* Partial match found */
ucs1++;
ucs2++;
} else {
if (!*ucs2) /* Match found */
return (wchar_t *) anchor1;
ucs1 = ++anchor1; /* No match */
ucs2 = anchor2;
}
}
if (!*ucs2) /* Both end together */
return (wchar_t *) anchor1; /* Match found */
return NULL; /* No match */
}
#ifndef UNIUPR_NOUPPER
/*
* UniToupper: Convert a unicode character to upper case
*/
static inline wchar_t
UniToupper(register wchar_t uc)
{
register const struct UniCaseRange *rp;
if (uc < sizeof(CifsUniUpperTable)) {
/* Latin characters */
return uc + CifsUniUpperTable[uc]; /* Use base tables */
} else {
rp = CifsUniUpperRange; /* Use range tables */
while (rp->start) {
if (uc < rp->start) /* Before start of range */
return uc; /* Uppercase = input */
if (uc <= rp->end) /* In range */
return uc + rp->table[uc - rp->start];
rp++; /* Try next range */
}
}
return uc; /* Past last range */
}
/*
* UniStrupr: Upper case a unicode string
*/
static inline __le16 *
UniStrupr(register __le16 *upin)
{
register __le16 *up;
up = upin;
while (*up) { /* For all characters */
*up = cpu_to_le16(UniToupper(le16_to_cpu(*up)));
up++;
}
return upin; /* Return input pointer */
}
#endif /* UNIUPR_NOUPPER */
#ifndef UNIUPR_NOLOWER
/*
* UniTolower: Convert a unicode character to lower case
*/
static inline wchar_t
UniTolower(register wchar_t uc)
{
register const struct UniCaseRange *rp;
if (uc < sizeof(CifsUniLowerTable)) {
/* Latin characters */
return uc + CifsUniLowerTable[uc]; /* Use base tables */
} else {
rp = CifsUniLowerRange; /* Use range tables */
while (rp->start) {
if (uc < rp->start) /* Before start of range */
return uc; /* Uppercase = input */
if (uc <= rp->end) /* In range */
return uc + rp->table[uc - rp->start];
rp++; /* Try next range */
}
}
return uc; /* Past last range */
}
/*
* UniStrlwr: Lower case a unicode string
*/
static inline wchar_t *
UniStrlwr(register wchar_t *upin)
{
register wchar_t *up;
up = upin;
while (*up) { /* For all characters */
*up = UniTolower(*up);
up++;
}
return upin; /* Return input pointer */
}
#endif
#endif /* _CIFS_UNICODE_H */ #endif /* _CIFS_UNICODE_H */

View File

@ -1,239 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) International Business Machines Corp., 2000,2002
*
* uniupr.h - Unicode compressed case ranges
*/
#ifndef UNIUPR_NOUPPER
/*
* Latin upper case
*/
signed char CifsUniUpperTable[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */
0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 060-06f */
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, 0, 0, 0, 0, 0, /* 070-07f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 0e0-0ef */
-32, -32, -32, -32, -32, -32, -32, 0, -32, -32, -32, -32, -32, -32, -32, 121, /* 0f0-0ff */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */
0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */
-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */
0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */
0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */
0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */
0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */
-1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */
0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */
-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */
0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */
};
/* Upper case range - Greek */
static signed char UniCaseRangeU03a0[47] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */
0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 3b0-3bf */
-32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64,
-63, -63,
};
/* Upper case range - Cyrillic */
static signed char UniCaseRangeU0430[48] = {
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 430-43f */
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 440-44f */
0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, 0, -80, -80, /* 450-45f */
};
/* Upper case range - Extended cyrillic */
static signed char UniCaseRangeU0490[61] = {
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */
0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1,
};
/* Upper case range - Extended latin and greek */
static signed char UniCaseRangeU1e00[509] = {
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */
0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */
8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */
8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */
0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */
74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, 126, 126, 0, 0, /* 1f70-1f7f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */
8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */
0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */
8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */
8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */
0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
/* Upper case range - Wide latin */
static signed char UniCaseRangeUff40[27] = {
0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* ff40-ff4f */
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
};
/*
* Upper Case Range
*/
const struct UniCaseRange CifsUniUpperRange[] = {
{0x03a0, 0x03ce, UniCaseRangeU03a0},
{0x0430, 0x045f, UniCaseRangeU0430},
{0x0490, 0x04cc, UniCaseRangeU0490},
{0x1e00, 0x1ffc, UniCaseRangeU1e00},
{0xff40, 0xff5a, UniCaseRangeUff40},
{0}
};
#endif
#ifndef UNIUPR_NOLOWER
/*
* Latin lower case
*/
signed char CifsUniLowerTable[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */
0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 040-04f */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, 0, 0, 0, /* 050-05f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 0c0-0cf */
32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 0, /* 0d0-0df */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */
0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */
0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */
1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, 0, /* 170-17f */
0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, 0, /* 180-18f */
0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */
1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */
0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */
0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */
0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */
};
/* Lower case range - Greek */
static signed char UniCaseRangeL0380[44] = {
0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */
0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 390-39f */
32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32,
};
/* Lower case range - Cyrillic */
static signed char UniCaseRangeL0400[48] = {
0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 0, 80, 80, /* 400-40f */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 410-41f */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 420-42f */
};
/* Lower case range - Extended cyrillic */
static signed char UniCaseRangeL0490[60] = {
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */
0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1,
};
/* Lower case range - Extended latin and greek */
static signed char UniCaseRangeL1e00[504] = {
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */
1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */
0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */
0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, 0, 0, /* 1fc0-1fcf */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, 0, 0, /* 1fe0-1fef */
0, 0, 0, 0, 0, 0, 0, 0,
};
/* Lower case range - Wide latin */
static signed char UniCaseRangeLff20[27] = {
0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* ff20-ff2f */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
};
/*
* Lower Case Range
*/
const struct UniCaseRange CifsUniLowerRange[] = {
{0x0380, 0x03ab, UniCaseRangeL0380},
{0x0400, 0x042f, UniCaseRangeL0400},
{0x0490, 0x04cb, UniCaseRangeL0490},
{0x1e00, 0x1ff7, UniCaseRangeL1e00},
{0xff20, 0xff3a, UniCaseRangeLff20},
{0}
};
#endif

View File

@ -1805,7 +1805,7 @@ exit_cifs(void)
cifs_dbg(NOISY, "exit_smb3\n"); cifs_dbg(NOISY, "exit_smb3\n");
unregister_filesystem(&cifs_fs_type); unregister_filesystem(&cifs_fs_type);
unregister_filesystem(&smb3_fs_type); unregister_filesystem(&smb3_fs_type);
cifs_dfs_release_automount_timer(); cifs_release_automount_timer();
exit_cifs_idmap(); exit_cifs_idmap();
#ifdef CONFIG_CIFS_SWN_UPCALL #ifdef CONFIG_CIFS_SWN_UPCALL
cifs_genl_exit(); cifs_genl_exit();

View File

@ -81,7 +81,7 @@ extern int cifs_fiemap(struct inode *, struct fiemap_extent_info *, u64 start,
extern const struct inode_operations cifs_file_inode_ops; extern const struct inode_operations cifs_file_inode_ops;
extern const struct inode_operations cifs_symlink_inode_ops; extern const struct inode_operations cifs_symlink_inode_ops;
extern const struct inode_operations cifs_dfs_referral_inode_operations; extern const struct inode_operations cifs_namespace_inode_operations;
/* Functions related to files and directories */ /* Functions related to files and directories */
@ -118,14 +118,7 @@ extern void cifs_pages_write_redirty(struct inode *inode, loff_t start, unsigned
extern const struct dentry_operations cifs_dentry_ops; extern const struct dentry_operations cifs_dentry_ops;
extern const struct dentry_operations cifs_ci_dentry_ops; extern const struct dentry_operations cifs_ci_dentry_ops;
#ifdef CONFIG_CIFS_DFS_UPCALL extern struct vfsmount *cifs_d_automount(struct path *path);
extern struct vfsmount *cifs_dfs_d_automount(struct path *path);
#else
static inline struct vfsmount *cifs_dfs_d_automount(struct path *path)
{
return ERR_PTR(-EREMOTE);
}
#endif
/* Functions related to symlinks */ /* Functions related to symlinks */
extern const char *cifs_get_link(struct dentry *, struct inode *, extern const char *cifs_get_link(struct dentry *, struct inode *,

View File

@ -186,6 +186,12 @@ struct cifs_cred {
}; };
struct cifs_open_info_data { struct cifs_open_info_data {
bool adjust_tz;
union {
bool reparse_point;
bool symlink;
};
__u32 reparse_tag;
char *symlink_target; char *symlink_target;
union { union {
struct smb2_file_all_info fi; struct smb2_file_all_info fi;
@ -193,6 +199,10 @@ struct cifs_open_info_data {
}; };
}; };
#define cifs_open_data_reparse(d) \
((d)->reparse_point || \
(le32_to_cpu((d)->fi.Attributes) & ATTR_REPARSE))
static inline void cifs_free_open_info(struct cifs_open_info_data *data) static inline void cifs_free_open_info(struct cifs_open_info_data *data)
{ {
kfree(data->symlink_target); kfree(data->symlink_target);
@ -318,16 +328,21 @@ struct smb_version_operations {
int (*is_path_accessible)(const unsigned int, struct cifs_tcon *, int (*is_path_accessible)(const unsigned int, struct cifs_tcon *,
struct cifs_sb_info *, const char *); struct cifs_sb_info *, const char *);
/* query path data from the server */ /* query path data from the server */
int (*query_path_info)(const unsigned int xid, struct cifs_tcon *tcon, int (*query_path_info)(const unsigned int xid,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_tcon *tcon,
struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); struct cifs_sb_info *cifs_sb,
const char *full_path,
struct cifs_open_info_data *data);
/* query file data from the server */ /* query file data from the server */
int (*query_file_info)(const unsigned int xid, struct cifs_tcon *tcon, int (*query_file_info)(const unsigned int xid, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile, struct cifs_open_info_data *data); struct cifsFileInfo *cfile, struct cifs_open_info_data *data);
/* query reparse tag from srv to determine which type of special file */ /* query reparse point to determine which type of special file */
int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon, int (*query_reparse_point)(const unsigned int xid,
struct cifs_sb_info *cifs_sb, const char *path, struct cifs_tcon *tcon,
__u32 *reparse_tag); struct cifs_sb_info *cifs_sb,
const char *full_path,
u32 *tag, struct kvec *rsp,
int *rsp_buftype);
/* get server index number */ /* get server index number */
int (*get_srv_inum)(const unsigned int xid, struct cifs_tcon *tcon, 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_sb_info *cifs_sb, const char *full_path, u64 *uniqueid,
@ -376,9 +391,12 @@ struct smb_version_operations {
const char *, const char *, const char *, const char *,
struct cifs_sb_info *); struct cifs_sb_info *);
/* query symlink target */ /* query symlink target */
int (*query_symlink)(const unsigned int, struct cifs_tcon *, int (*query_symlink)(const unsigned int xid,
struct cifs_sb_info *, const char *, struct cifs_tcon *tcon,
char **, bool); struct cifs_sb_info *cifs_sb,
const char *full_path,
char **target_path,
struct kvec *rsp_iov);
/* open a file for non-posix mounts */ /* open a file for non-posix mounts */
int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
void *buf); void *buf);
@ -727,8 +745,9 @@ struct TCP_Server_Info {
* primary_server holds the ref-counted * primary_server holds the ref-counted
* pointer to primary channel connection for the session. * pointer to primary channel connection for the session.
*/ */
#define CIFS_SERVER_IS_CHAN(server) (!!(server)->primary_server) #define SERVER_IS_CHAN(server) (!!(server)->primary_server)
struct TCP_Server_Info *primary_server; struct TCP_Server_Info *primary_server;
__u16 channel_sequence_num; /* incremented on primary channel on each chan reconnect */
#ifdef CONFIG_CIFS_SWN_UPCALL #ifdef CONFIG_CIFS_SWN_UPCALL
bool use_swn_dstaddr; bool use_swn_dstaddr;
@ -1076,7 +1095,7 @@ cap_unix(struct cifs_ses *ses)
* inode with new info * inode with new info
*/ */
#define CIFS_FATTR_DFS_REFERRAL 0x1 #define CIFS_FATTR_JUNCTION 0x1
#define CIFS_FATTR_DELETE_PENDING 0x2 #define CIFS_FATTR_DELETE_PENDING 0x2
#define CIFS_FATTR_NEED_REVAL 0x4 #define CIFS_FATTR_NEED_REVAL 0x4
#define CIFS_FATTR_INO_COLLISION 0x8 #define CIFS_FATTR_INO_COLLISION 0x8
@ -1721,11 +1740,23 @@ struct cifs_mount_ctx {
struct list_head dfs_ses_list; struct list_head dfs_ses_list;
}; };
static inline void __free_dfs_info_param(struct dfs_info3_param *param)
{
kfree(param->path_name);
kfree(param->node_name);
}
static inline void free_dfs_info_param(struct dfs_info3_param *param) static inline void free_dfs_info_param(struct dfs_info3_param *param)
{
if (param)
__free_dfs_info_param(param);
}
static inline void zfree_dfs_info_param(struct dfs_info3_param *param)
{ {
if (param) { if (param) {
kfree(param->path_name); __free_dfs_info_param(param);
kfree(param->node_name); memset(param, 0, sizeof(*param));
} }
} }
@ -2184,4 +2215,17 @@ static inline void cifs_sg_set_buf(struct sg_table *sgtable,
} }
} }
struct smb2_compound_vars {
struct cifs_open_parms oparms;
struct kvec rsp_iov[3];
struct smb_rqst rqst[3];
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
struct kvec qi_iov;
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE];
struct kvec close_iov;
struct smb2_file_rename_info rename_info;
struct smb2_file_link_info link_info;
};
#endif /* _CIFS_GLOB_H */ #endif /* _CIFS_GLOB_H */

View File

@ -207,6 +207,9 @@ extern struct inode *cifs_iget(struct super_block *sb,
int cifs_get_inode_info(struct inode **inode, const char *full_path, int cifs_get_inode_info(struct inode **inode, const char *full_path,
struct cifs_open_info_data *data, struct super_block *sb, int xid, struct cifs_open_info_data *data, struct super_block *sb, int xid,
const struct cifs_fid *fid); const struct cifs_fid *fid);
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
struct cifs_fattr *fattr,
u32 tag);
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path, extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
struct super_block *sb, unsigned int xid); struct super_block *sb, unsigned int xid);
extern int cifs_get_inode_info_unix(struct inode **pinode, extern int cifs_get_inode_info_unix(struct inode **pinode,
@ -295,11 +298,7 @@ extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
int from_reconnect); int from_reconnect);
extern void cifs_put_tcon(struct cifs_tcon *tcon); extern void cifs_put_tcon(struct cifs_tcon *tcon);
#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) extern void cifs_release_automount_timer(void);
extern void cifs_dfs_release_automount_timer(void);
#else /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */
#define cifs_dfs_release_automount_timer() do { } while (0)
#endif /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */
void cifs_proc_init(void); void cifs_proc_init(void);
void cifs_proc_clean(void); void cifs_proc_clean(void);

View File

@ -154,7 +154,7 @@ cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
int i; int i;
/* If server is a channel, select the primary channel */ /* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
spin_lock(&pserver->srv_lock); spin_lock(&pserver->srv_lock);
if (!all_channels) { if (!all_channels) {
@ -202,7 +202,7 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
cifs_dbg(FYI, "%s: marking necessary sessions and tcons for reconnect\n", __func__); cifs_dbg(FYI, "%s: marking necessary sessions and tcons for reconnect\n", __func__);
/* If server is a channel, select the primary channel */ /* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
@ -453,10 +453,10 @@ static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_
static int reconnect_dfs_server(struct TCP_Server_Info *server) static int reconnect_dfs_server(struct TCP_Server_Info *server)
{ {
int rc = 0;
struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
struct dfs_cache_tgt_iterator *target_hint = NULL; struct dfs_cache_tgt_iterator *target_hint = NULL;
DFS_CACHE_TGT_LIST(tl);
int num_targets = 0; int num_targets = 0;
int rc = 0;
/* /*
* Determine the number of dfs targets the referral path in @cifs_sb resolves to. * Determine the number of dfs targets the referral path in @cifs_sb resolves to.
@ -911,8 +911,8 @@ cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required)
return 0; return 0;
} }
static noinline_for_stack void
static void clean_demultiplex_info(struct TCP_Server_Info *server) clean_demultiplex_info(struct TCP_Server_Info *server)
{ {
int length; int length;
@ -1551,7 +1551,7 @@ cifs_find_tcp_session(struct smb3_fs_context *ctx)
* Skip ses channels since they're only handled in lower layers * Skip ses channels since they're only handled in lower layers
* (e.g. cifs_send_recv). * (e.g. cifs_send_recv).
*/ */
if (CIFS_SERVER_IS_CHAN(server) || if (SERVER_IS_CHAN(server) ||
!match_server(server, ctx, false)) { !match_server(server, ctx, false)) {
spin_unlock(&server->srv_lock); spin_unlock(&server->srv_lock);
continue; continue;
@ -1587,7 +1587,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
/* For secondary channels, we pick up ref-count on the primary server */ /* For secondary channels, we pick up ref-count on the primary server */
if (CIFS_SERVER_IS_CHAN(server)) if (SERVER_IS_CHAN(server))
cifs_put_tcp_session(server->primary_server, from_reconnect); cifs_put_tcp_session(server->primary_server, from_reconnect);
cancel_delayed_work_sync(&server->echo); cancel_delayed_work_sync(&server->echo);
@ -1686,6 +1686,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
ctx->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); ctx->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
tcp_ses->session_estab = false; tcp_ses->session_estab = false;
tcp_ses->sequence_number = 0; tcp_ses->sequence_number = 0;
tcp_ses->channel_sequence_num = 0; /* only tracked for primary channel */
tcp_ses->reconnect_instance = 1; tcp_ses->reconnect_instance = 1;
tcp_ses->lstrp = jiffies; tcp_ses->lstrp = jiffies;
tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression); tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression);
@ -1792,7 +1793,7 @@ out_err_crypto_release:
out_err: out_err:
if (tcp_ses) { if (tcp_ses) {
if (CIFS_SERVER_IS_CHAN(tcp_ses)) if (SERVER_IS_CHAN(tcp_ses))
cifs_put_tcp_session(tcp_ses->primary_server, false); cifs_put_tcp_session(tcp_ses->primary_server, false);
kfree(tcp_ses->hostname); kfree(tcp_ses->hostname);
kfree(tcp_ses->leaf_fullpath); kfree(tcp_ses->leaf_fullpath);
@ -3813,7 +3814,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
struct nls_table *nls_info) struct nls_table *nls_info)
{ {
int rc = -ENOSYS; int rc = -ENOSYS;
struct TCP_Server_Info *pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; struct TCP_Server_Info *pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&pserver->dstaddr; struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&pserver->dstaddr;
struct sockaddr_in *addr = (struct sockaddr_in *)&pserver->dstaddr; struct sockaddr_in *addr = (struct sockaddr_in *)&pserver->dstaddr;
bool is_binding = false; bool is_binding = false;

View File

@ -3,7 +3,6 @@
* Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de> * Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de>
*/ */
#include <linux/namei.h>
#include "cifsproto.h" #include "cifsproto.h"
#include "cifs_debug.h" #include "cifs_debug.h"
#include "dns_resolve.h" #include "dns_resolve.h"
@ -96,51 +95,134 @@ static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
return 0; return 0;
} }
static int get_dfs_conn(struct cifs_mount_ctx *mnt_ctx, const char *ref_path, const char *full_path, static inline int parse_dfs_target(struct smb3_fs_context *ctx,
const struct dfs_cache_tgt_iterator *tit) struct dfs_ref_walk *rw,
struct dfs_info3_param *tgt)
{
int rc;
const char *fpath = ref_walk_fpath(rw) + 1;
rc = ref_walk_get_tgt(rw, tgt);
if (!rc)
rc = dfs_parse_target_referral(fpath, tgt, ctx);
return rc;
}
static int set_ref_paths(struct cifs_mount_ctx *mnt_ctx,
struct dfs_info3_param *tgt,
struct dfs_ref_walk *rw)
{ {
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
struct dfs_info3_param ref = {}; struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
bool is_refsrv; char *ref_path, *full_path;
int rc, rc2; int rc;
rc = dfs_cache_get_tgt_referral(ref_path + 1, tit, &ref); full_path = smb3_fs_context_fullpath(ctx, CIFS_DIR_SEP(cifs_sb));
if (rc) if (IS_ERR(full_path))
return PTR_ERR(full_path);
if (!tgt || (tgt->server_type == DFS_TYPE_LINK &&
DFS_INTERLINK(tgt->flags)))
ref_path = dfs_get_path(cifs_sb, ctx->UNC);
else
ref_path = dfs_get_path(cifs_sb, full_path);
if (IS_ERR(ref_path)) {
rc = PTR_ERR(ref_path);
kfree(full_path);
return rc; return rc;
rc = dfs_parse_target_referral(full_path + 1, &ref, ctx);
if (rc)
goto out;
cifs_mount_put_conns(mnt_ctx);
rc = get_session(mnt_ctx, ref_path);
if (rc)
goto out;
is_refsrv = !!(ref.flags & DFSREF_REFERRAL_SERVER);
rc = -EREMOTE;
if (ref.flags & DFSREF_STORAGE_SERVER) {
rc = cifs_mount_get_tcon(mnt_ctx);
if (rc)
goto out;
/* some servers may not advertise referral capability under ref.flags */
is_refsrv |= is_tcon_dfs(mnt_ctx->tcon);
rc = cifs_is_path_remote(mnt_ctx);
} }
ref_walk_path(rw) = ref_path;
ref_walk_fpath(rw) = full_path;
return 0;
}
dfs_cache_noreq_update_tgthint(ref_path + 1, tit); static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
struct dfs_ref_walk *rw)
{
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
struct dfs_info3_param tgt = {};
bool is_refsrv;
int rc = -ENOENT;
if (rc == -EREMOTE && is_refsrv) { again:
rc2 = add_root_smb_session(mnt_ctx); do {
if (rc2) if (ref_walk_empty(rw)) {
rc = rc2; rc = dfs_get_referral(mnt_ctx, ref_walk_path(rw) + 1,
} NULL, ref_walk_tl(rw));
if (rc) {
rc = cifs_mount_get_tcon(mnt_ctx);
if (!rc)
rc = cifs_is_path_remote(mnt_ctx);
continue;
}
if (!ref_walk_num_tgts(rw)) {
rc = -ENOENT;
continue;
}
}
while (ref_walk_next_tgt(rw)) {
rc = parse_dfs_target(ctx, rw, &tgt);
if (rc)
continue;
cifs_mount_put_conns(mnt_ctx);
rc = get_session(mnt_ctx, ref_walk_path(rw));
if (rc)
continue;
is_refsrv = tgt.server_type == DFS_TYPE_ROOT ||
DFS_INTERLINK(tgt.flags);
ref_walk_set_tgt_hint(rw);
if (tgt.flags & DFSREF_STORAGE_SERVER) {
rc = cifs_mount_get_tcon(mnt_ctx);
if (!rc)
rc = cifs_is_path_remote(mnt_ctx);
if (!rc)
break;
if (rc != -EREMOTE)
continue;
}
if (is_refsrv) {
rc = add_root_smb_session(mnt_ctx);
if (rc)
goto out;
}
rc = ref_walk_advance(rw);
if (!rc) {
rc = set_ref_paths(mnt_ctx, &tgt, rw);
if (!rc) {
rc = -EREMOTE;
goto again;
}
}
if (rc != -ELOOP)
goto out;
}
} while (rc && ref_walk_descend(rw));
out: out:
free_dfs_info_param(&ref); free_dfs_info_param(&tgt);
return rc;
}
static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx)
{
struct dfs_ref_walk *rw;
int rc;
rw = ref_walk_alloc();
if (IS_ERR(rw))
return PTR_ERR(rw);
ref_walk_init(rw);
rc = set_ref_paths(mnt_ctx, NULL, rw);
if (!rc)
rc = __dfs_referral_walk(mnt_ctx, rw);
ref_walk_free(rw);
return rc; return rc;
} }
@ -148,105 +230,48 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
{ {
struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
char *ref_path = NULL, *full_path = NULL;
struct dfs_cache_tgt_iterator *tit;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
char *origin_fullpath = NULL; char *origin_fullpath;
char sep = CIFS_DIR_SEP(cifs_sb);
int num_links = 0;
int rc; int rc;
ref_path = dfs_get_path(cifs_sb, ctx->UNC); origin_fullpath = dfs_get_path(cifs_sb, ctx->source);
if (IS_ERR(ref_path)) if (IS_ERR(origin_fullpath))
return PTR_ERR(ref_path); return PTR_ERR(origin_fullpath);
full_path = smb3_fs_context_fullpath(ctx, sep); rc = dfs_referral_walk(mnt_ctx);
if (IS_ERR(full_path)) { if (rc)
rc = PTR_ERR(full_path);
full_path = NULL;
goto out; goto out;
tcon = mnt_ctx->tcon;
spin_lock(&tcon->tc_lock);
if (!tcon->origin_fullpath) {
tcon->origin_fullpath = origin_fullpath;
origin_fullpath = NULL;
} }
spin_unlock(&tcon->tc_lock);
origin_fullpath = kstrdup(full_path, GFP_KERNEL); if (list_empty(&tcon->dfs_ses_list)) {
if (!origin_fullpath) { list_replace_init(&mnt_ctx->dfs_ses_list, &tcon->dfs_ses_list);
rc = -ENOMEM; queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
goto out; dfs_cache_get_ttl() * HZ);
} } else {
dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list);
do {
struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
rc = dfs_get_referral(mnt_ctx, ref_path + 1, NULL, &tl);
if (rc) {
rc = cifs_mount_get_tcon(mnt_ctx);
if (!rc)
rc = cifs_is_path_remote(mnt_ctx);
break;
}
tit = dfs_cache_get_tgt_iterator(&tl);
if (!tit) {
cifs_dbg(VFS, "%s: dfs referral (%s) with no targets\n", __func__,
ref_path + 1);
rc = -ENOENT;
dfs_cache_free_tgts(&tl);
break;
}
do {
rc = get_dfs_conn(mnt_ctx, ref_path, full_path, tit);
if (!rc)
break;
if (rc == -EREMOTE) {
if (++num_links > MAX_NESTED_LINKS) {
rc = -ELOOP;
break;
}
kfree(ref_path);
kfree(full_path);
ref_path = full_path = NULL;
full_path = smb3_fs_context_fullpath(ctx, sep);
if (IS_ERR(full_path)) {
rc = PTR_ERR(full_path);
full_path = NULL;
} else {
ref_path = dfs_get_path(cifs_sb, full_path);
if (IS_ERR(ref_path)) {
rc = PTR_ERR(ref_path);
ref_path = NULL;
}
}
break;
}
} while ((tit = dfs_cache_get_next_tgt(&tl, tit)));
dfs_cache_free_tgts(&tl);
} while (rc == -EREMOTE);
if (!rc) {
tcon = mnt_ctx->tcon;
spin_lock(&tcon->tc_lock);
if (!tcon->origin_fullpath) {
tcon->origin_fullpath = origin_fullpath;
origin_fullpath = NULL;
}
spin_unlock(&tcon->tc_lock);
if (list_empty(&tcon->dfs_ses_list)) {
list_replace_init(&mnt_ctx->dfs_ses_list,
&tcon->dfs_ses_list);
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
dfs_cache_get_ttl() * HZ);
} else {
dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list);
}
} }
out: out:
kfree(origin_fullpath); kfree(origin_fullpath);
kfree(ref_path); return rc;
kfree(full_path); }
/* Resolve UNC hostname in @ctx->source and set ip addr in @ctx->dstaddr */
static int update_fs_context_dstaddr(struct smb3_fs_context *ctx)
{
struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;
int rc;
rc = dns_resolve_server_name_to_ip(ctx->source, addr, NULL);
if (!rc)
cifs_set_port(addr, ctx->port);
return rc; return rc;
} }
@ -256,6 +281,10 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
bool nodfs = ctx->nodfs; bool nodfs = ctx->nodfs;
int rc; int rc;
rc = update_fs_context_dstaddr(ctx);
if (rc)
return rc;
*isdfs = false; *isdfs = false;
rc = get_session(mnt_ctx, NULL); rc = get_session(mnt_ctx, NULL);
if (rc) if (rc)
@ -426,7 +455,7 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
/* Try to tree connect to all dfs targets */ /* Try to tree connect to all dfs targets */
for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
const char *target = dfs_cache_get_tgt_name(tit); const char *target = dfs_cache_get_tgt_name(tit);
struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl); DFS_CACHE_TGT_LIST(ntl);
kfree(share); kfree(share);
kfree(prefix); kfree(prefix);
@ -520,7 +549,7 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
int rc; int rc;
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
const struct smb_version_operations *ops = server->ops; const struct smb_version_operations *ops = server->ops;
struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); DFS_CACHE_TGT_LIST(tl);
struct cifs_sb_info *cifs_sb = NULL; struct cifs_sb_info *cifs_sb = NULL;
struct super_block *sb = NULL; struct super_block *sb = NULL;
struct dfs_info3_param ref = {0}; struct dfs_info3_param ref = {0};

View File

@ -9,6 +9,110 @@
#include "cifsglob.h" #include "cifsglob.h"
#include "fs_context.h" #include "fs_context.h"
#include "cifs_unicode.h" #include "cifs_unicode.h"
#include <linux/namei.h>
#define DFS_INTERLINK(v) \
(((v) & DFSREF_REFERRAL_SERVER) && !((v) & DFSREF_STORAGE_SERVER))
struct dfs_ref {
char *path;
char *full_path;
struct dfs_cache_tgt_list tl;
struct dfs_cache_tgt_iterator *tit;
};
struct dfs_ref_walk {
struct dfs_ref *ref;
struct dfs_ref refs[MAX_NESTED_LINKS];
};
#define ref_walk_start(w) ((w)->refs)
#define ref_walk_end(w) (&(w)->refs[ARRAY_SIZE((w)->refs) - 1])
#define ref_walk_cur(w) ((w)->ref)
#define ref_walk_descend(w) (--ref_walk_cur(w) >= ref_walk_start(w))
#define ref_walk_tit(w) (ref_walk_cur(w)->tit)
#define ref_walk_empty(w) (!ref_walk_tit(w))
#define ref_walk_path(w) (ref_walk_cur(w)->path)
#define ref_walk_fpath(w) (ref_walk_cur(w)->full_path)
#define ref_walk_tl(w) (&ref_walk_cur(w)->tl)
static inline struct dfs_ref_walk *ref_walk_alloc(void)
{
struct dfs_ref_walk *rw;
rw = kmalloc(sizeof(*rw), GFP_KERNEL);
if (!rw)
return ERR_PTR(-ENOMEM);
return rw;
}
static inline void ref_walk_init(struct dfs_ref_walk *rw)
{
memset(rw, 0, sizeof(*rw));
ref_walk_cur(rw) = ref_walk_start(rw);
}
static inline void __ref_walk_free(struct dfs_ref *ref)
{
kfree(ref->path);
kfree(ref->full_path);
dfs_cache_free_tgts(&ref->tl);
memset(ref, 0, sizeof(*ref));
}
static inline void ref_walk_free(struct dfs_ref_walk *rw)
{
struct dfs_ref *ref = ref_walk_start(rw);
for (; ref <= ref_walk_end(rw); ref++)
__ref_walk_free(ref);
kfree(rw);
}
static inline int ref_walk_advance(struct dfs_ref_walk *rw)
{
struct dfs_ref *ref = ref_walk_cur(rw) + 1;
if (ref > ref_walk_end(rw))
return -ELOOP;
__ref_walk_free(ref);
ref_walk_cur(rw) = ref;
return 0;
}
static inline struct dfs_cache_tgt_iterator *
ref_walk_next_tgt(struct dfs_ref_walk *rw)
{
struct dfs_cache_tgt_iterator *tit;
struct dfs_ref *ref = ref_walk_cur(rw);
if (!ref->tit)
tit = dfs_cache_get_tgt_iterator(&ref->tl);
else
tit = dfs_cache_get_next_tgt(&ref->tl, ref->tit);
ref->tit = tit;
return tit;
}
static inline int ref_walk_get_tgt(struct dfs_ref_walk *rw,
struct dfs_info3_param *tgt)
{
zfree_dfs_info_param(tgt);
return dfs_cache_get_tgt_referral(ref_walk_path(rw) + 1,
ref_walk_tit(rw), tgt);
}
static inline int ref_walk_num_tgts(struct dfs_ref_walk *rw)
{
return dfs_cache_get_nr_tgts(ref_walk_tl(rw));
}
static inline void ref_walk_set_tgt_hint(struct dfs_ref_walk *rw)
{
dfs_cache_noreq_update_tgthint(ref_walk_path(rw) + 1,
ref_walk_tit(rw));
}
struct dfs_root_ses { struct dfs_root_ses {
struct list_head list; struct list_head list;
@ -34,43 +138,6 @@ static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, const char *p
cifs_remap(cifs_sb), path, ref, tl); cifs_remap(cifs_sb), path, ref, tl);
} }
/* Return DFS full path out of a dentry set for automount */
static inline char *dfs_get_automount_devname(struct dentry *dentry, void *page)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
size_t len;
char *s;
spin_lock(&tcon->tc_lock);
if (unlikely(!tcon->origin_fullpath)) {
spin_unlock(&tcon->tc_lock);
return ERR_PTR(-EREMOTE);
}
spin_unlock(&tcon->tc_lock);
s = dentry_path_raw(dentry, page, PATH_MAX);
if (IS_ERR(s))
return s;
/* for root, we want "" */
if (!s[1])
s++;
spin_lock(&tcon->tc_lock);
len = strlen(tcon->origin_fullpath);
if (s < (char *)page + len) {
spin_unlock(&tcon->tc_lock);
return ERR_PTR(-ENAMETOOLONG);
}
s -= len;
memcpy(s, tcon->origin_fullpath, len);
spin_unlock(&tcon->tc_lock);
convert_delimiter(s, '/');
return s;
}
static inline void dfs_put_root_smb_sessions(struct list_head *head) static inline void dfs_put_root_smb_sessions(struct list_head *head)
{ {
struct dfs_root_ses *root, *tmp; struct dfs_root_ses *root, *tmp;

View File

@ -29,8 +29,6 @@
#define CACHE_MIN_TTL 120 /* 2 minutes */ #define CACHE_MIN_TTL 120 /* 2 minutes */
#define CACHE_DEFAULT_TTL 300 /* 5 minutes */ #define CACHE_DEFAULT_TTL 300 /* 5 minutes */
#define IS_DFS_INTERLINK(v) (((v) & DFSREF_REFERRAL_SERVER) && !((v) & DFSREF_STORAGE_SERVER))
struct cache_dfs_tgt { struct cache_dfs_tgt {
char *name; char *name;
int path_consumed; int path_consumed;
@ -174,7 +172,7 @@ static int dfscache_proc_show(struct seq_file *m, void *v)
"cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n", "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n",
ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link",
ce->ttl, ce->etime.tv_nsec, ce->hdr_flags, ce->ref_flags, ce->ttl, ce->etime.tv_nsec, ce->hdr_flags, ce->ref_flags,
IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no",
ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no"); ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no");
list_for_each_entry(t, &ce->tlist, list) { list_for_each_entry(t, &ce->tlist, list) {
@ -243,7 +241,7 @@ static inline void dump_ce(const struct cache_entry *ce)
ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl,
ce->etime.tv_nsec, ce->etime.tv_nsec,
ce->hdr_flags, ce->ref_flags, ce->hdr_flags, ce->ref_flags,
IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no",
ce->path_consumed, ce->path_consumed,
cache_entry_expired(ce) ? "yes" : "no"); cache_entry_expired(ce) ? "yes" : "no");
dump_tgts(ce); dump_tgts(ce);
@ -1177,9 +1175,9 @@ static bool is_ses_good(struct cifs_ses *ses)
/* Refresh dfs referral of tcon and mark it for reconnect if needed */ /* Refresh dfs referral of tcon and mark it for reconnect if needed */
static int __refresh_tcon(const char *path, struct cifs_ses *ses, bool force_refresh) static int __refresh_tcon(const char *path, struct cifs_ses *ses, bool force_refresh)
{ {
struct dfs_cache_tgt_list old_tl = DFS_CACHE_TGT_LIST_INIT(old_tl);
struct dfs_cache_tgt_list new_tl = DFS_CACHE_TGT_LIST_INIT(new_tl);
struct TCP_Server_Info *server = ses->server; struct TCP_Server_Info *server = ses->server;
DFS_CACHE_TGT_LIST(old_tl);
DFS_CACHE_TGT_LIST(new_tl);
bool needs_refresh = false; bool needs_refresh = false;
struct cache_entry *ce; struct cache_entry *ce;
unsigned int xid; unsigned int xid;

View File

@ -16,7 +16,11 @@
extern struct workqueue_struct *dfscache_wq; extern struct workqueue_struct *dfscache_wq;
extern atomic_t dfs_cache_ttl; extern atomic_t dfs_cache_ttl;
#define DFS_CACHE_TGT_LIST_INIT(var) { .tl_numtgts = 0, .tl_list = LIST_HEAD_INIT((var).tl_list), } #define DFS_CACHE_TGT_LIST_INIT(var) \
{ .tl_numtgts = 0, .tl_list = LIST_HEAD_INIT((var).tl_list), }
#define DFS_CACHE_TGT_LIST(var) \
struct dfs_cache_tgt_list var = DFS_CACHE_TGT_LIST_INIT(var)
struct dfs_cache_tgt_list { struct dfs_cache_tgt_list {
int tl_numtgts; int tl_numtgts;
@ -51,8 +55,8 @@ static inline struct dfs_cache_tgt_iterator *
dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl, dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,
struct dfs_cache_tgt_iterator *it) struct dfs_cache_tgt_iterator *it)
{ {
if (!tl || list_empty(&tl->tl_list) || !it || if (!tl || !tl->tl_numtgts || list_empty(&tl->tl_list) ||
list_is_last(&it->it_list, &tl->tl_list)) !it || list_is_last(&it->it_list, &tl->tl_list))
return NULL; return NULL;
return list_next_entry(it, it_list); return list_next_entry(it, it_list);
} }
@ -71,7 +75,7 @@ static inline void dfs_cache_free_tgts(struct dfs_cache_tgt_list *tl)
{ {
struct dfs_cache_tgt_iterator *it, *nit; struct dfs_cache_tgt_iterator *it, *nit;
if (!tl || list_empty(&tl->tl_list)) if (!tl || !tl->tl_numtgts || list_empty(&tl->tl_list))
return; return;
list_for_each_entry_safe(it, nit, &tl->tl_list, it_list) { list_for_each_entry_safe(it, nit, &tl->tl_list, it_list) {
list_del(&it->it_list); list_del(&it->it_list);

View File

@ -797,7 +797,7 @@ cifs_d_revalidate(struct dentry *direntry, unsigned int flags)
const struct dentry_operations cifs_dentry_ops = { const struct dentry_operations cifs_dentry_ops = {
.d_revalidate = cifs_d_revalidate, .d_revalidate = cifs_d_revalidate,
.d_automount = cifs_dfs_d_automount, .d_automount = cifs_d_automount,
/* d_delete: cifs_d_delete, */ /* not needed except for debugging */ /* d_delete: cifs_d_delete, */ /* not needed except for debugging */
}; };
@ -872,5 +872,5 @@ const struct dentry_operations cifs_ci_dentry_ops = {
.d_revalidate = cifs_d_revalidate, .d_revalidate = cifs_d_revalidate,
.d_hash = cifs_ci_hash, .d_hash = cifs_ci_hash,
.d_compare = cifs_ci_compare, .d_compare = cifs_ci_compare,
.d_automount = cifs_dfs_d_automount, .d_automount = cifs_d_automount,
}; };

View File

@ -58,13 +58,9 @@ static void cifs_set_ops(struct inode *inode)
inode->i_data.a_ops = &cifs_addr_ops; inode->i_data.a_ops = &cifs_addr_ops;
break; break;
case S_IFDIR: case S_IFDIR:
#ifdef CONFIG_CIFS_DFS_UPCALL
if (IS_AUTOMOUNT(inode)) { if (IS_AUTOMOUNT(inode)) {
inode->i_op = &cifs_dfs_referral_inode_operations; inode->i_op = &cifs_namespace_inode_operations;
} else { } else {
#else /* NO DFS support, treat as a directory */
{
#endif
inode->i_op = &cifs_dir_inode_ops; inode->i_op = &cifs_dir_inode_ops;
inode->i_fop = &cifs_dir_ops; inode->i_fop = &cifs_dir_ops;
} }
@ -218,7 +214,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
} }
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
if (fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL) if (fattr->cf_flags & CIFS_FATTR_JUNCTION)
inode->i_flags |= S_AUTOMOUNT; inode->i_flags |= S_AUTOMOUNT;
if (inode->i_state & I_NEW) if (inode->i_state & I_NEW)
cifs_set_ops(inode); cifs_set_ops(inode);
@ -327,14 +323,14 @@ cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info,
* *
* Needed to setup cifs_fattr data for the directory which is the * Needed to setup cifs_fattr data for the directory which is the
* junction to the new submount (ie to setup the fake directory * junction to the new submount (ie to setup the fake directory
* which represents a DFS referral). * which represents a DFS referral or reparse mount point).
*/ */
static void static void cifs_create_junction_fattr(struct cifs_fattr *fattr,
cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb) struct super_block *sb)
{ {
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
cifs_dbg(FYI, "creating fake fattr for DFS referral\n"); cifs_dbg(FYI, "%s: creating fake fattr\n", __func__);
memset(fattr, 0, sizeof(*fattr)); memset(fattr, 0, sizeof(*fattr));
fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU; fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU;
@ -343,7 +339,33 @@ cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)
ktime_get_coarse_real_ts64(&fattr->cf_mtime); ktime_get_coarse_real_ts64(&fattr->cf_mtime);
fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime; fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime;
fattr->cf_nlink = 2; fattr->cf_nlink = 2;
fattr->cf_flags = CIFS_FATTR_DFS_REFERRAL; fattr->cf_flags = CIFS_FATTR_JUNCTION;
}
/* Update inode with final fattr data */
static int update_inode_info(struct super_block *sb,
struct cifs_fattr *fattr,
struct inode **inode)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
int rc = 0;
if (!*inode) {
*inode = cifs_iget(sb, fattr);
if (!*inode)
rc = -ENOMEM;
return rc;
}
/* We already have inode, update it.
*
* If file type or uniqueid is different, return error.
*/
if (unlikely((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) &&
CIFS_I(*inode)->uniqueid != fattr->cf_uniqueid)) {
CIFS_I(*inode)->time = 0; /* force reval */
return -ESTALE;
}
return cifs_fattr_to_inode(*inode, fattr);
} }
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
@ -373,7 +395,7 @@ cifs_get_file_info_unix(struct file *filp)
if (!rc) { if (!rc) {
cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
} else if (rc == -EREMOTE) { } else if (rc == -EREMOTE) {
cifs_create_dfs_fattr(&fattr, inode->i_sb); cifs_create_junction_fattr(&fattr, inode->i_sb);
rc = 0; rc = 0;
} else } else
goto cifs_gfiunix_out; goto cifs_gfiunix_out;
@ -385,17 +407,18 @@ cifs_gfiunix_out:
return rc; return rc;
} }
int cifs_get_inode_info_unix(struct inode **pinode, static int cifs_get_unix_fattr(const unsigned char *full_path,
const unsigned char *full_path, struct super_block *sb,
struct super_block *sb, unsigned int xid) struct cifs_fattr *fattr,
struct inode **pinode,
const unsigned int xid)
{ {
int rc;
FILE_UNIX_BASIC_INFO find_data;
struct cifs_fattr fattr;
struct cifs_tcon *tcon;
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct tcon_link *tlink;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
FILE_UNIX_BASIC_INFO find_data;
struct cifs_tcon *tcon;
struct tcon_link *tlink;
int rc, tmprc;
cifs_dbg(FYI, "Getting info on %s\n", full_path); cifs_dbg(FYI, "Getting info on %s\n", full_path);
@ -412,59 +435,61 @@ int cifs_get_inode_info_unix(struct inode **pinode,
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
if (!rc) { if (!rc) {
cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); cifs_unix_basic_to_fattr(fattr, &find_data, cifs_sb);
} else if (rc == -EREMOTE) { } else if (rc == -EREMOTE) {
cifs_create_dfs_fattr(&fattr, sb); cifs_create_junction_fattr(fattr, sb);
rc = 0; rc = 0;
} else { } else {
return rc; return rc;
} }
if (!*pinode)
cifs_fill_uniqueid(sb, fattr);
/* check for Minshall+French symlinks */ /* check for Minshall+French symlinks */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
int tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, tmprc = check_mf_symlink(xid, tcon, cifs_sb, fattr, full_path);
full_path); cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
if (tmprc)
cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
} }
if (S_ISLNK(fattr.cf_mode) && !fattr.cf_symlink_target) { if (S_ISLNK(fattr->cf_mode) && !fattr->cf_symlink_target) {
if (!server->ops->query_symlink) if (!server->ops->query_symlink)
return -EOPNOTSUPP; return -EOPNOTSUPP;
rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path, rc = server->ops->query_symlink(xid, tcon,
&fattr.cf_symlink_target, false); cifs_sb, full_path,
if (rc) { &fattr->cf_symlink_target,
cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc); NULL);
goto cgiiu_exit; cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc);
}
} }
return rc;
}
if (*pinode == NULL) { int cifs_get_inode_info_unix(struct inode **pinode,
/* get new inode */ const unsigned char *full_path,
cifs_fill_uniqueid(sb, &fattr); struct super_block *sb, unsigned int xid)
*pinode = cifs_iget(sb, &fattr); {
if (!*pinode) struct cifs_fattr fattr = {};
rc = -ENOMEM; int rc;
} else {
/* we already have inode, update it */
/* if uniqueid is different, return error */ rc = cifs_get_unix_fattr(full_path, sb, &fattr, pinode, xid);
if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && if (rc)
CIFS_I(*pinode)->uniqueid != fattr.cf_uniqueid)) { goto out;
CIFS_I(*pinode)->time = 0; /* force reval */
rc = -ESTALE;
goto cgiiu_exit;
}
/* if filetype is different, return error */ rc = update_inode_info(sb, &fattr, pinode);
rc = cifs_fattr_to_inode(*pinode, &fattr); out:
}
cgiiu_exit:
kfree(fattr.cf_symlink_target); kfree(fattr.cf_symlink_target);
return rc; return rc;
} }
#else #else
static inline int cifs_get_unix_fattr(const unsigned char *full_path,
struct super_block *sb,
struct cifs_fattr *fattr,
struct inode **pinode,
const unsigned int xid)
{
return -EOPNOTSUPP;
}
int cifs_get_inode_info_unix(struct inode **pinode, int cifs_get_inode_info_unix(struct inode **pinode,
const unsigned char *full_path, const unsigned char *full_path,
struct super_block *sb, unsigned int xid) struct super_block *sb, unsigned int xid)
@ -632,10 +657,11 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
} }
/* Fill a cifs_fattr struct with info from POSIX info struct */ /* Fill a cifs_fattr struct with info from POSIX info struct */
static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data, static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
struct cifs_open_info_data *data,
struct cifs_sid *owner, struct cifs_sid *owner,
struct cifs_sid *group, struct cifs_sid *group,
struct super_block *sb, bool adjust_tz, bool symlink) struct super_block *sb)
{ {
struct smb311_posix_qinfo *info = &data->posix_fi; struct smb311_posix_qinfo *info = &data->posix_fi;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
@ -655,7 +681,7 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_ope
fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
if (adjust_tz) { if (data->adjust_tz) {
fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj;
fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj;
} }
@ -669,7 +695,7 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_ope
/* The srv fs device id is overridden on network mount so setting rdev isn't needed here */ /* The srv fs device id is overridden on network mount so setting rdev isn't needed here */
/* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */ /* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */
if (symlink) { if (data->symlink) {
fattr->cf_mode |= S_IFLNK; fattr->cf_mode |= S_IFLNK;
fattr->cf_dtype = DT_LNK; fattr->cf_dtype = DT_LNK;
fattr->cf_symlink_target = data->symlink_target; fattr->cf_symlink_target = data->symlink_target;
@ -690,9 +716,46 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_ope
fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
} }
static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data, bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
struct super_block *sb, bool adjust_tz, bool symlink, struct cifs_fattr *fattr,
u32 reparse_tag) u32 tag)
{
switch (tag) {
case IO_REPARSE_TAG_LX_SYMLINK:
fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_LNK;
break;
case IO_REPARSE_TAG_LX_FIFO:
fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_FIFO;
break;
case IO_REPARSE_TAG_AF_UNIX:
fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_SOCK;
break;
case IO_REPARSE_TAG_LX_CHR:
fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_CHR;
break;
case IO_REPARSE_TAG_LX_BLK:
fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_BLK;
break;
case 0: /* SMB1 symlink */
case IO_REPARSE_TAG_SYMLINK:
case IO_REPARSE_TAG_NFS:
fattr->cf_mode = S_IFLNK;
fattr->cf_dtype = DT_LNK;
break;
default:
return false;
}
return true;
}
static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
struct cifs_open_info_data *data,
struct super_block *sb)
{ {
struct smb2_file_all_info *info = &data->fi; struct smb2_file_all_info *info = &data->fi;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
@ -711,7 +774,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_i
fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
if (adjust_tz) { if (data->adjust_tz) {
fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj;
fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj;
} }
@ -719,28 +782,13 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_i
fattr->cf_eof = le64_to_cpu(info->EndOfFile); fattr->cf_eof = le64_to_cpu(info->EndOfFile);
fattr->cf_bytes = le64_to_cpu(info->AllocationSize); fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
fattr->cf_createtime = le64_to_cpu(info->CreationTime); fattr->cf_createtime = le64_to_cpu(info->CreationTime);
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
if (reparse_tag == IO_REPARSE_TAG_LX_SYMLINK) {
fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; if (cifs_open_data_reparse(data) &&
fattr->cf_dtype = DT_LNK; cifs_reparse_point_to_fattr(cifs_sb, fattr, data->reparse_tag))
} else if (reparse_tag == IO_REPARSE_TAG_LX_FIFO) { goto out_reparse;
fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_FIFO; if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
} else if (reparse_tag == IO_REPARSE_TAG_AF_UNIX) {
fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_SOCK;
} else if (reparse_tag == IO_REPARSE_TAG_LX_CHR) {
fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_CHR;
} 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 || 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) {
fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode; fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode;
fattr->cf_dtype = DT_DIR; fattr->cf_dtype = DT_DIR;
/* /*
@ -769,6 +817,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_i
} }
} }
out_reparse:
if (S_ISLNK(fattr->cf_mode)) { if (S_ISLNK(fattr->cf_mode)) {
fattr->cf_symlink_target = data->symlink_target; fattr->cf_symlink_target = data->symlink_target;
data->symlink_target = NULL; data->symlink_target = NULL;
@ -789,8 +838,6 @@ cifs_get_file_info(struct file *filp)
struct cifsFileInfo *cfile = filp->private_data; struct cifsFileInfo *cfile = filp->private_data;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
bool symlink = false;
u32 tag = 0;
if (!server->ops->query_file_info) if (!server->ops->query_file_info)
return -ENOSYS; return -ENOSYS;
@ -800,14 +847,15 @@ cifs_get_file_info(struct file *filp)
switch (rc) { switch (rc) {
case 0: case 0:
/* TODO: add support to query reparse tag */ /* TODO: add support to query reparse tag */
data.adjust_tz = false;
if (data.symlink_target) { if (data.symlink_target) {
symlink = true; data.symlink = true;
tag = IO_REPARSE_TAG_SYMLINK; data.reparse_tag = IO_REPARSE_TAG_SYMLINK;
} }
cifs_open_info_to_fattr(&fattr, &data, inode->i_sb, false, symlink, tag); cifs_open_info_to_fattr(&fattr, &data, inode->i_sb);
break; break;
case -EREMOTE: case -EREMOTE:
cifs_create_dfs_fattr(&fattr, inode->i_sb); cifs_create_junction_fattr(&fattr, inode->i_sb);
rc = 0; rc = 0;
break; break;
case -EOPNOTSUPP: case -EOPNOTSUPP:
@ -960,22 +1008,66 @@ static inline bool is_inode_cache_good(struct inode *ino)
return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0; 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, static int reparse_info_to_fattr(struct cifs_open_info_data *data,
struct cifs_open_info_data *data, struct super_block *sb, int xid, struct super_block *sb,
const struct cifs_fid *fid) const unsigned int xid,
struct cifs_tcon *tcon,
const char *full_path,
struct cifs_fattr *fattr)
{ {
struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct kvec rsp_iov, *iov = NULL;
int rsp_buftype = CIFS_NO_BUFFER;
u32 tag = data->reparse_tag;
int rc = 0;
if (!tag && server->ops->query_reparse_point) {
rc = server->ops->query_reparse_point(xid, tcon, cifs_sb,
full_path, &tag,
&rsp_iov, &rsp_buftype);
if (!rc)
iov = &rsp_iov;
}
switch ((data->reparse_tag = tag)) {
case 0: /* SMB1 symlink */
iov = NULL;
fallthrough;
case IO_REPARSE_TAG_NFS:
case IO_REPARSE_TAG_SYMLINK:
if (!data->symlink_target && server->ops->query_symlink) {
rc = server->ops->query_symlink(xid, tcon,
cifs_sb, full_path,
&data->symlink_target,
iov);
}
break;
case IO_REPARSE_TAG_MOUNT_POINT:
cifs_create_junction_fattr(fattr, sb);
goto out;
}
cifs_open_info_to_fattr(fattr, data, sb);
out:
free_rsp_buf(rsp_buftype, rsp_iov.iov_base);
return rc;
}
static int cifs_get_fattr(struct cifs_open_info_data *data,
struct super_block *sb, int xid,
const struct cifs_fid *fid,
struct cifs_fattr *fattr,
struct inode **inode,
const char *full_path)
{
struct cifs_open_info_data tmp_data = {};
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct tcon_link *tlink; struct tcon_link *tlink;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
bool adjust_tz = false;
struct cifs_fattr fattr = {0};
bool is_reparse_point = false;
struct cifs_open_info_data tmp_data = {};
void *smb1_backup_rsp_buf = NULL; void *smb1_backup_rsp_buf = NULL;
int rc = 0; int rc = 0;
int tmprc = 0; int tmprc = 0;
__u32 reparse_tag = 0;
tlink = cifs_sb_tlink(cifs_sb); tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) if (IS_ERR(tlink))
@ -988,12 +1080,8 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
*/ */
if (!data) { if (!data) {
if (is_inode_cache_good(*inode)) { rc = server->ops->query_path_info(xid, tcon, cifs_sb,
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); full_path, &tmp_data);
goto out;
}
rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, &tmp_data,
&adjust_tz, &is_reparse_point);
data = &tmp_data; data = &tmp_data;
} }
@ -1008,28 +1096,16 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
* since we have to check if its reparse tag matches a known * since we have to check if its reparse tag matches a known
* special file type e.g. symlink or fifo or char etc. * special file type e.g. symlink or fifo or char etc.
*/ */
if (is_reparse_point && data->symlink_target) { if (cifs_open_data_reparse(data)) {
reparse_tag = IO_REPARSE_TAG_SYMLINK; rc = reparse_info_to_fattr(data, sb, xid, tcon,
} else if ((le32_to_cpu(data->fi.Attributes) & ATTR_REPARSE) && full_path, fattr);
server->ops->query_reparse_tag) { } else {
tmprc = server->ops->query_reparse_tag(xid, tcon, cifs_sb, full_path, cifs_open_info_to_fattr(fattr, data, sb);
&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_open_info_to_fattr(&fattr, data, sb, adjust_tz, is_reparse_point, reparse_tag);
break; break;
case -EREMOTE: case -EREMOTE:
/* DFS link, no metadata available on this server */ /* DFS link, no metadata available on this server */
cifs_create_dfs_fattr(&fattr, sb); cifs_create_junction_fattr(fattr, sb);
rc = 0; rc = 0;
break; break;
case -EACCES: case -EACCES:
@ -1059,8 +1135,8 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
fdi = (FILE_DIRECTORY_INFO *)fi; fdi = (FILE_DIRECTORY_INFO *)fi;
si = (SEARCH_ID_FULL_DIR_INFO *)fi; si = (SEARCH_ID_FULL_DIR_INFO *)fi;
cifs_dir_info_to_fattr(&fattr, fdi, cifs_sb); cifs_dir_info_to_fattr(fattr, fdi, cifs_sb);
fattr.cf_uniqueid = le64_to_cpu(si->UniqueId); fattr->cf_uniqueid = le64_to_cpu(si->UniqueId);
/* uniqueid set, skip get inum step */ /* uniqueid set, skip get inum step */
goto handle_mnt_opt; goto handle_mnt_opt;
} else { } else {
@ -1077,10 +1153,10 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
} }
/* /*
* 3. Get or update inode number (fattr.cf_uniqueid) * 3. Get or update inode number (fattr->cf_uniqueid)
*/ */
cifs_set_fattr_ino(xid, tcon, sb, inode, full_path, data, &fattr); cifs_set_fattr_ino(xid, tcon, sb, inode, full_path, data, fattr);
/* /*
* 4. Tweak fattr based on mount options * 4. Tweak fattr based on mount options
@ -1089,17 +1165,17 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
handle_mnt_opt: handle_mnt_opt:
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
/* query for SFU type info if supported and needed */ /* query for SFU type info if supported and needed */
if (fattr.cf_cifsattrs & ATTR_SYSTEM && if ((fattr->cf_cifsattrs & ATTR_SYSTEM) &&
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) {
tmprc = cifs_sfu_type(&fattr, full_path, cifs_sb, xid); tmprc = cifs_sfu_type(fattr, full_path, cifs_sb, xid);
if (tmprc) if (tmprc)
cifs_dbg(FYI, "cifs_sfu_type failed: %d\n", tmprc); cifs_dbg(FYI, "cifs_sfu_type failed: %d\n", tmprc);
} }
/* fill in 0777 bits from ACL */ /* fill in 0777 bits from ACL */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) {
rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, true, rc = cifs_acl_to_fattr(cifs_sb, fattr, *inode,
full_path, fid); true, full_path, fid);
if (rc == -EREMOTE) if (rc == -EREMOTE)
rc = 0; rc = 0;
if (rc) { if (rc) {
@ -1108,8 +1184,8 @@ handle_mnt_opt:
goto out; goto out;
} }
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, false, rc = cifs_acl_to_fattr(cifs_sb, fattr, *inode,
full_path, fid); false, full_path, fid);
if (rc == -EREMOTE) if (rc == -EREMOTE)
rc = 0; rc = 0;
if (rc) { if (rc) {
@ -1121,60 +1197,57 @@ handle_mnt_opt:
/* fill in remaining high mode bits e.g. SUID, VTX */ /* fill in remaining high mode bits e.g. SUID, VTX */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)
cifs_sfu_mode(&fattr, full_path, cifs_sb, xid); cifs_sfu_mode(fattr, full_path, cifs_sb, xid);
/* check for Minshall+French symlinks */ /* check for Minshall+French symlinks */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, tmprc = check_mf_symlink(xid, tcon, cifs_sb, fattr, full_path);
full_path); cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
if (tmprc)
cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
} }
/*
* 5. Update inode with final fattr data
*/
if (!*inode) {
*inode = cifs_iget(sb, &fattr);
if (!*inode)
rc = -ENOMEM;
} else {
/* we already have inode, update it */
/* if uniqueid is different, return error */
if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM &&
CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) {
CIFS_I(*inode)->time = 0; /* force reval */
rc = -ESTALE;
goto out;
}
/* if filetype is different, return error */
rc = cifs_fattr_to_inode(*inode, &fattr);
}
out: out:
cifs_buf_release(smb1_backup_rsp_buf); cifs_buf_release(smb1_backup_rsp_buf);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
cifs_free_open_info(&tmp_data); cifs_free_open_info(&tmp_data);
return rc;
}
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_fattr fattr = {};
int rc;
if (is_inode_cache_good(*inode)) {
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
return 0;
}
rc = cifs_get_fattr(data, sb, xid, fid, &fattr, inode, full_path);
if (rc)
goto out;
rc = update_inode_info(sb, &fattr, inode);
out:
kfree(fattr.cf_symlink_target); kfree(fattr.cf_symlink_target);
return rc; return rc;
} }
int static int smb311_posix_get_fattr(struct cifs_fattr *fattr,
smb311_posix_get_inode_info(struct inode **inode, const char *full_path,
const char *full_path, struct super_block *sb,
struct super_block *sb, unsigned int xid) const unsigned int xid)
{ {
struct cifs_open_info_data data = {};
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct tcon_link *tlink; struct tcon_link *tlink;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
bool adjust_tz = false;
struct cifs_fattr fattr = {0};
bool symlink = false;
struct cifs_open_info_data data = {};
struct cifs_sid owner, group; struct cifs_sid owner, group;
int rc = 0; int tmprc;
int tmprc = 0; int rc;
tlink = cifs_sb_tlink(cifs_sb); tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) if (IS_ERR(tlink))
@ -1185,14 +1258,9 @@ smb311_posix_get_inode_info(struct inode **inode,
* 1. Fetch file metadata * 1. Fetch file metadata
*/ */
if (is_inode_cache_good(*inode)) { rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); full_path, &data,
goto out; &owner, &group);
}
rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, full_path, &data,
&owner, &group, &adjust_tz,
&symlink);
/* /*
* 2. Convert it to internal cifs metadata (fattr) * 2. Convert it to internal cifs metadata (fattr)
@ -1200,12 +1268,11 @@ smb311_posix_get_inode_info(struct inode **inode,
switch (rc) { switch (rc) {
case 0: case 0:
smb311_posix_info_to_fattr(&fattr, &data, &owner, &group, smb311_posix_info_to_fattr(fattr, &data, &owner, &group, sb);
sb, adjust_tz, symlink);
break; break;
case -EREMOTE: case -EREMOTE:
/* DFS link, no metadata available on this server */ /* DFS link, no metadata available on this server */
cifs_create_dfs_fattr(&fattr, sb); cifs_create_junction_fattr(fattr, sb);
rc = 0; rc = 0;
break; break;
case -EACCES: case -EACCES:
@ -1221,48 +1288,41 @@ smb311_posix_get_inode_info(struct inode **inode,
goto out; goto out;
} }
/* /*
* 3. Tweak fattr based on mount options * 3. Tweak fattr based on mount options
*/ */
/* check for Minshall+French symlinks */ /* check for Minshall+French symlinks */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, tmprc = check_mf_symlink(xid, tcon, cifs_sb, fattr, full_path);
full_path); cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
if (tmprc)
cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
} }
/*
* 4. Update inode with final fattr data
*/
if (!*inode) {
*inode = cifs_iget(sb, &fattr);
if (!*inode)
rc = -ENOMEM;
} else {
/* we already have inode, update it */
/* if uniqueid is different, return error */
if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM &&
CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) {
CIFS_I(*inode)->time = 0; /* force reval */
rc = -ESTALE;
goto out;
}
/* if filetype is different, return error */
rc = cifs_fattr_to_inode(*inode, &fattr);
}
out: out:
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
cifs_free_open_info(&data); cifs_free_open_info(&data);
kfree(fattr.cf_symlink_target);
return rc; return rc;
} }
int smb311_posix_get_inode_info(struct inode **inode, const char *full_path,
struct super_block *sb, const unsigned int xid)
{
struct cifs_fattr fattr = {};
int rc;
if (is_inode_cache_good(*inode)) {
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
return 0;
}
rc = smb311_posix_get_fattr(&fattr, full_path, sb, xid);
if (rc)
goto out;
rc = update_inode_info(sb, &fattr, inode);
out:
kfree(fattr.cf_symlink_target);
return rc;
}
static const struct inode_operations cifs_ipc_inode_ops = { static const struct inode_operations cifs_ipc_inode_ops = {
.lookup = cifs_lookup, .lookup = cifs_lookup,
@ -1367,13 +1427,14 @@ retry_iget5_locked:
/* gets root inode */ /* gets root inode */
struct inode *cifs_root_iget(struct super_block *sb) struct inode *cifs_root_iget(struct super_block *sb)
{ {
unsigned int xid;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct inode *inode = NULL; struct cifs_fattr fattr = {};
long rc;
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
struct inode *inode = NULL;
unsigned int xid;
char *path = NULL; char *path = NULL;
int len; int len;
int rc;
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
&& cifs_sb->prepath) { && cifs_sb->prepath) {
@ -1391,21 +1452,29 @@ struct inode *cifs_root_iget(struct super_block *sb)
xid = get_xid(); xid = get_xid();
if (tcon->unix_ext) { if (tcon->unix_ext) {
rc = cifs_get_inode_info_unix(&inode, path, sb, xid); rc = cifs_get_unix_fattr(path, sb, &fattr, &inode, xid);
/* some servers mistakenly claim POSIX support */ /* some servers mistakenly claim POSIX support */
if (rc != -EOPNOTSUPP) if (rc != -EOPNOTSUPP)
goto iget_no_retry; goto iget_root;
cifs_dbg(VFS, "server does not support POSIX extensions\n"); cifs_dbg(VFS, "server does not support POSIX extensions\n");
tcon->unix_ext = false; tcon->unix_ext = false;
} }
convert_delimiter(path, CIFS_DIR_SEP(cifs_sb)); convert_delimiter(path, CIFS_DIR_SEP(cifs_sb));
if (tcon->posix_extensions) if (tcon->posix_extensions)
rc = smb311_posix_get_inode_info(&inode, path, sb, xid); rc = smb311_posix_get_fattr(&fattr, path, sb, xid);
else else
rc = cifs_get_inode_info(&inode, path, NULL, sb, xid, NULL); rc = cifs_get_fattr(NULL, sb, xid, NULL, &fattr, &inode, path);
iget_root:
if (!rc) {
if (fattr.cf_flags & CIFS_FATTR_JUNCTION) {
fattr.cf_flags &= ~CIFS_FATTR_JUNCTION;
cifs_autodisable_serverino(cifs_sb);
}
inode = cifs_iget(sb, &fattr);
}
iget_no_retry:
if (!inode) { if (!inode) {
inode = ERR_PTR(rc); inode = ERR_PTR(rc);
goto out; goto out;
@ -1429,6 +1498,7 @@ iget_no_retry:
out: out:
kfree(path); kfree(path);
free_xid(xid); free_xid(xid);
kfree(fattr.cf_symlink_target);
return inode; return inode;
} }

View File

@ -476,7 +476,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
return false; return false;
/* If server is a channel, select the primary channel */ /* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(srv) ? srv->primary_server : srv; pserver = SERVER_IS_CHAN(srv) ? srv->primary_server : srv;
/* look up tcon based on tid & uid */ /* look up tcon based on tid & uid */
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);

View File

@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Contains the CIFS DFS referral mounting routines used for handling * Contains mounting routines used for handling traversal via SMB junctions.
* traversal via DFS junction point
* *
* Copyright (c) 2007 Igor Mammedov * Copyright (c) 2007 Igor Mammedov
* Copyright (C) International Business Machines Corp., 2008 * Copyright (C) International Business Machines Corp., 2008
* Author(s): Igor Mammedov (niallain@gmail.com) * Author(s): Igor Mammedov (niallain@gmail.com)
* Steve French (sfrench@us.ibm.com) * Steve French (sfrench@us.ibm.com)
* Copyright (c) 2023 Paulo Alcantara <palcantara@suse.de>
*/ */
#include <linux/dcache.h> #include <linux/dcache.h>
@ -19,32 +19,31 @@
#include "cifsglob.h" #include "cifsglob.h"
#include "cifsproto.h" #include "cifsproto.h"
#include "cifsfs.h" #include "cifsfs.h"
#include "dns_resolve.h"
#include "cifs_debug.h" #include "cifs_debug.h"
#include "dfs.h"
#include "fs_context.h" #include "fs_context.h"
static LIST_HEAD(cifs_dfs_automount_list); static LIST_HEAD(cifs_automount_list);
static void cifs_dfs_expire_automounts(struct work_struct *work); static void cifs_expire_automounts(struct work_struct *work);
static DECLARE_DELAYED_WORK(cifs_dfs_automount_task, static DECLARE_DELAYED_WORK(cifs_automount_task,
cifs_dfs_expire_automounts); cifs_expire_automounts);
static int cifs_dfs_mountpoint_expiry_timeout = 500 * HZ; static int cifs_mountpoint_expiry_timeout = 500 * HZ;
static void cifs_dfs_expire_automounts(struct work_struct *work) static void cifs_expire_automounts(struct work_struct *work)
{ {
struct list_head *list = &cifs_dfs_automount_list; struct list_head *list = &cifs_automount_list;
mark_mounts_for_expiry(list); mark_mounts_for_expiry(list);
if (!list_empty(list)) if (!list_empty(list))
schedule_delayed_work(&cifs_dfs_automount_task, schedule_delayed_work(&cifs_automount_task,
cifs_dfs_mountpoint_expiry_timeout); cifs_mountpoint_expiry_timeout);
} }
void cifs_dfs_release_automount_timer(void) void cifs_release_automount_timer(void)
{ {
BUG_ON(!list_empty(&cifs_dfs_automount_list)); if (WARN_ON(!list_empty(&cifs_automount_list)))
cancel_delayed_work_sync(&cifs_dfs_automount_task); return;
cancel_delayed_work_sync(&cifs_automount_task);
} }
/** /**
@ -118,26 +117,53 @@ cifs_build_devname(char *nodename, const char *prepath)
return dev; return dev;
} }
static int set_dest_addr(struct smb3_fs_context *ctx) /* Return full path out of a dentry set for automount */
static char *automount_fullpath(struct dentry *dentry, void *page)
{ {
struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr; struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb);
int rc; struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
size_t len;
char *s;
rc = dns_resolve_server_name_to_ip(ctx->source, addr, NULL); spin_lock(&tcon->tc_lock);
if (!rc) if (!tcon->origin_fullpath) {
cifs_set_port(addr, ctx->port); spin_unlock(&tcon->tc_lock);
return rc; return build_path_from_dentry_optional_prefix(dentry,
page,
true);
}
spin_unlock(&tcon->tc_lock);
s = dentry_path_raw(dentry, page, PATH_MAX);
if (IS_ERR(s))
return s;
/* for root, we want "" */
if (!s[1])
s++;
spin_lock(&tcon->tc_lock);
len = strlen(tcon->origin_fullpath);
if (s < (char *)page + len) {
spin_unlock(&tcon->tc_lock);
return ERR_PTR(-ENAMETOOLONG);
}
s -= len;
memcpy(s, tcon->origin_fullpath, len);
spin_unlock(&tcon->tc_lock);
convert_delimiter(s, '/');
return s;
} }
/* /*
* Create a vfsmount that we can automount * Create a vfsmount that we can automount
*/ */
static struct vfsmount *cifs_dfs_do_automount(struct path *path) static struct vfsmount *cifs_do_automount(struct path *path)
{ {
int rc; int rc;
struct dentry *mntpt = path->dentry; struct dentry *mntpt = path->dentry;
struct fs_context *fc; struct fs_context *fc;
struct cifs_sb_info *cifs_sb;
void *page = NULL; void *page = NULL;
struct smb3_fs_context *ctx, *cur_ctx; struct smb3_fs_context *ctx, *cur_ctx;
struct smb3_fs_context tmp; struct smb3_fs_context tmp;
@ -147,17 +173,7 @@ static struct vfsmount *cifs_dfs_do_automount(struct path *path)
if (IS_ROOT(mntpt)) if (IS_ROOT(mntpt))
return ERR_PTR(-ESTALE); return ERR_PTR(-ESTALE);
/* cur_ctx = CIFS_SB(mntpt->d_sb)->ctx;
* The MSDFS spec states that paths in DFS referral requests and
* responses must be prefixed by a single '\' character instead of
* the double backslashes usually used in the UNC. This function
* gives us the latter, so we must adjust the result.
*/
cifs_sb = CIFS_SB(mntpt->d_sb);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)
return ERR_PTR(-EREMOTE);
cur_ctx = cifs_sb->ctx;
fc = fs_context_for_submount(path->mnt->mnt_sb->s_type, mntpt); fc = fs_context_for_submount(path->mnt->mnt_sb->s_type, mntpt);
if (IS_ERR(fc)) if (IS_ERR(fc))
@ -166,7 +182,7 @@ static struct vfsmount *cifs_dfs_do_automount(struct path *path)
ctx = smb3_fc2context(fc); ctx = smb3_fc2context(fc);
page = alloc_dentry_path(); page = alloc_dentry_path();
full_path = dfs_get_automount_devname(mntpt, page); full_path = automount_fullpath(mntpt, page);
if (IS_ERR(full_path)) { if (IS_ERR(full_path)) {
mnt = ERR_CAST(full_path); mnt = ERR_CAST(full_path);
goto out; goto out;
@ -196,15 +212,10 @@ static struct vfsmount *cifs_dfs_do_automount(struct path *path)
ctx->source = NULL; ctx->source = NULL;
goto out; goto out;
} }
cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s dstaddr=%pISpc\n", cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s\n",
__func__, ctx->source, ctx->UNC, ctx->prepath, &ctx->dstaddr); __func__, ctx->source, ctx->UNC, ctx->prepath);
rc = set_dest_addr(ctx);
if (!rc)
mnt = fc_mount(fc);
else
mnt = ERR_PTR(rc);
mnt = fc_mount(fc);
out: out:
put_fs_context(fc); put_fs_context(fc);
free_dentry_path(page); free_dentry_path(page);
@ -214,25 +225,25 @@ out:
/* /*
* Attempt to automount the referral * Attempt to automount the referral
*/ */
struct vfsmount *cifs_dfs_d_automount(struct path *path) struct vfsmount *cifs_d_automount(struct path *path)
{ {
struct vfsmount *newmnt; struct vfsmount *newmnt;
cifs_dbg(FYI, "%s: %pd\n", __func__, path->dentry); cifs_dbg(FYI, "%s: %pd\n", __func__, path->dentry);
newmnt = cifs_dfs_do_automount(path); newmnt = cifs_do_automount(path);
if (IS_ERR(newmnt)) { if (IS_ERR(newmnt)) {
cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__); cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__);
return newmnt; return newmnt;
} }
mntget(newmnt); /* prevent immediate expiration */ mntget(newmnt); /* prevent immediate expiration */
mnt_set_expiry(newmnt, &cifs_dfs_automount_list); mnt_set_expiry(newmnt, &cifs_automount_list);
schedule_delayed_work(&cifs_dfs_automount_task, schedule_delayed_work(&cifs_automount_task,
cifs_dfs_mountpoint_expiry_timeout); cifs_mountpoint_expiry_timeout);
cifs_dbg(FYI, "leaving %s [ok]\n" , __func__); cifs_dbg(FYI, "leaving %s [ok]\n" , __func__);
return newmnt; return newmnt;
} }
const struct inode_operations cifs_dfs_referral_inode_operations = { const struct inode_operations cifs_namespace_inode_operations = {
}; };

View File

@ -143,6 +143,7 @@ static bool reparse_file_needs_reval(const struct cifs_fattr *fattr)
case IO_REPARSE_TAG_DFSR: case IO_REPARSE_TAG_DFSR:
case IO_REPARSE_TAG_SYMLINK: case IO_REPARSE_TAG_SYMLINK:
case IO_REPARSE_TAG_NFS: case IO_REPARSE_TAG_NFS:
case IO_REPARSE_TAG_MOUNT_POINT:
case 0: case 0:
return true; return true;
} }
@ -163,29 +164,19 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
* TODO: go through all documented reparse tags to see if we can * TODO: go through all documented reparse tags to see if we can
* reasonably map some of them to directories vs. files vs. symlinks * reasonably map some of them to directories vs. files vs. symlinks
*/ */
if ((fattr->cf_cifsattrs & ATTR_REPARSE) &&
cifs_reparse_point_to_fattr(cifs_sb, fattr, fattr->cf_cifstag))
goto out_reparse;
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode; fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode;
fattr->cf_dtype = DT_DIR; fattr->cf_dtype = DT_DIR;
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_SYMLINK) { } else {
fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_LNK;
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_FIFO) {
fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_FIFO;
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_AF_UNIX) {
fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_SOCK;
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_CHR) {
fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_CHR;
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_BLK) {
fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_BLK;
} else { /* TODO: should we mark some other reparse points (like DFSR) as directories? */
fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode; fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_REG; fattr->cf_dtype = DT_REG;
} }
out_reparse:
/* /*
* We need to revalidate it further to make a decision about whether it * We need to revalidate it further to make a decision about whether it
* is a symbolic link, DFS referral or a reparse point with a direct * is a symbolic link, DFS referral or a reparse point with a direct

View File

@ -323,12 +323,12 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
ses->chans[chan_index].iface = iface; ses->chans[chan_index].iface = iface;
/* No iface is found. if secondary chan, drop connection */ /* No iface is found. if secondary chan, drop connection */
if (!iface && CIFS_SERVER_IS_CHAN(server)) if (!iface && SERVER_IS_CHAN(server))
ses->chans[chan_index].server = NULL; ses->chans[chan_index].server = NULL;
spin_unlock(&ses->chan_lock); spin_unlock(&ses->chan_lock);
if (!iface && CIFS_SERVER_IS_CHAN(server)) if (!iface && SERVER_IS_CHAN(server))
cifs_put_tcp_session(server, false); cifs_put_tcp_session(server, false);
return rc; return rc;
@ -360,11 +360,11 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
{ {
struct TCP_Server_Info *chan_server; struct TCP_Server_Info *chan_server;
struct cifs_chan *chan; struct cifs_chan *chan;
struct smb3_fs_context ctx = {NULL}; struct smb3_fs_context *ctx;
static const char unc_fmt[] = "\\%s\\foo"; static const char unc_fmt[] = "\\%s\\foo";
char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0};
struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
size_t len;
int rc; int rc;
unsigned int xid = get_xid(); unsigned int xid = get_xid();
@ -388,54 +388,64 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
* the session and server without caring about memory * the session and server without caring about memory
* management. * management.
*/ */
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
rc = -ENOMEM;
goto out_free_xid;
}
/* Always make new connection for now (TODO?) */ /* Always make new connection for now (TODO?) */
ctx.nosharesock = true; ctx->nosharesock = true;
/* Auth */ /* Auth */
ctx.domainauto = ses->domainAuto; ctx->domainauto = ses->domainAuto;
ctx.domainname = ses->domainName; ctx->domainname = ses->domainName;
/* no hostname for extra channels */ /* no hostname for extra channels */
ctx.server_hostname = ""; ctx->server_hostname = "";
ctx.username = ses->user_name; ctx->username = ses->user_name;
ctx.password = ses->password; ctx->password = ses->password;
ctx.sectype = ses->sectype; ctx->sectype = ses->sectype;
ctx.sign = ses->sign; ctx->sign = ses->sign;
/* UNC and paths */ /* UNC and paths */
/* XXX: Use ses->server->hostname? */ /* XXX: Use ses->server->hostname? */
sprintf(unc, unc_fmt, ses->ip_addr); len = sizeof(unc_fmt) + SERVER_NAME_LEN_WITH_NULL;
ctx.UNC = unc; ctx->UNC = kzalloc(len, GFP_KERNEL);
ctx.prepath = ""; if (!ctx->UNC) {
rc = -ENOMEM;
goto out_free_ctx;
}
scnprintf(ctx->UNC, len, unc_fmt, ses->ip_addr);
ctx->prepath = "";
/* Reuse same version as master connection */ /* Reuse same version as master connection */
ctx.vals = ses->server->vals; ctx->vals = ses->server->vals;
ctx.ops = ses->server->ops; ctx->ops = ses->server->ops;
ctx.noblocksnd = ses->server->noblocksnd; ctx->noblocksnd = ses->server->noblocksnd;
ctx.noautotune = ses->server->noautotune; ctx->noautotune = ses->server->noautotune;
ctx.sockopt_tcp_nodelay = ses->server->tcp_nodelay; ctx->sockopt_tcp_nodelay = ses->server->tcp_nodelay;
ctx.echo_interval = ses->server->echo_interval / HZ; ctx->echo_interval = ses->server->echo_interval / HZ;
ctx.max_credits = ses->server->max_credits; ctx->max_credits = ses->server->max_credits;
/* /*
* This will be used for encoding/decoding user/domain/pw * This will be used for encoding/decoding user/domain/pw
* during sess setup auth. * during sess setup auth.
*/ */
ctx.local_nls = cifs_sb->local_nls; ctx->local_nls = cifs_sb->local_nls;
/* Use RDMA if possible */ /* Use RDMA if possible */
ctx.rdma = iface->rdma_capable; ctx->rdma = iface->rdma_capable;
memcpy(&ctx.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage)); memcpy(&ctx->dstaddr, &iface->sockaddr, sizeof(ctx->dstaddr));
/* reuse master con client guid */ /* reuse master con client guid */
memcpy(&ctx.client_guid, ses->server->client_guid, memcpy(&ctx->client_guid, ses->server->client_guid,
SMB2_CLIENT_GUID_SIZE); sizeof(ctx->client_guid));
ctx.use_client_guid = true; ctx->use_client_guid = true;
chan_server = cifs_get_tcp_session(&ctx, ses->server); chan_server = cifs_get_tcp_session(ctx, ses->server);
spin_lock(&ses->chan_lock); spin_lock(&ses->chan_lock);
chan = &ses->chans[ses->chan_count]; chan = &ses->chans[ses->chan_count];
@ -497,6 +507,10 @@ out:
cifs_put_tcp_session(chan->server, 0); cifs_put_tcp_session(chan->server, 0);
} }
kfree(ctx->UNC);
out_free_ctx:
kfree(ctx);
out_free_xid:
free_xid(xid); free_xid(xid);
return rc; return rc;
} }

View File

@ -542,14 +542,17 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
static int cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, static int cifs_query_path_info(const unsigned int xid,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_tcon *tcon,
struct cifs_open_info_data *data, bool *adjustTZ, bool *symlink) struct cifs_sb_info *cifs_sb,
const char *full_path,
struct cifs_open_info_data *data)
{ {
int rc; int rc;
FILE_ALL_INFO fi = {}; FILE_ALL_INFO fi = {};
*symlink = false; data->symlink = false;
data->adjust_tz = false;
/* could do find first instead but this returns more info */ /* could do find first instead but this returns more info */
rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */, cifs_sb->local_nls, rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */, cifs_sb->local_nls,
@ -562,7 +565,7 @@ static int cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) { if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls, rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls,
cifs_remap(cifs_sb)); cifs_remap(cifs_sb));
*adjustTZ = true; data->adjust_tz = true;
} }
if (!rc) { if (!rc) {
@ -589,7 +592,7 @@ static int cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
/* Need to check if this is a symbolic link or not */ /* Need to check if this is a symbolic link or not */
tmprc = CIFS_open(xid, &oparms, &oplock, NULL); tmprc = CIFS_open(xid, &oparms, &oplock, NULL);
if (tmprc == -EOPNOTSUPP) if (tmprc == -EOPNOTSUPP)
*symlink = true; data->symlink = true;
else if (tmprc == 0) else if (tmprc == 0)
CIFSSMBClose(xid, tcon, fid.netfid); CIFSSMBClose(xid, tcon, fid.netfid);
} }
@ -969,13 +972,16 @@ cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon,
#endif #endif
} }
static int static int cifs_query_symlink(const unsigned int xid,
cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb,
char **target_path, bool is_reparse_point) const char *full_path,
char **target_path,
struct kvec *rsp_iov)
{ {
int rc; int rc;
int oplock = 0; int oplock = 0;
bool is_reparse_point = !!rsp_iov;
struct cifs_fid fid; struct cifs_fid fid;
struct cifs_open_parms oparms; struct cifs_open_parms oparms;

View File

@ -35,34 +35,22 @@ free_set_inf_compound(struct smb_rqst *rqst)
SMB2_close_free(&rqst[2]); SMB2_close_free(&rqst[2]);
} }
struct cop_vars {
struct cifs_open_parms oparms;
struct kvec rsp_iov[3];
struct smb_rqst rqst[3];
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
struct kvec qi_iov[1];
struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE];
struct kvec close_iov[1];
struct smb2_file_rename_info rename_info;
struct smb2_file_link_info link_info;
};
/* /*
* note: If cfile is passed, the reference to it is dropped here. * 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. * 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 * If passing @out_iov and @out_buftype, ensure to make them both large enough
* error responses. Caller is also responsible for freeing them up. * (>= 3) to hold all compounded responses. Caller is also responsible for
* freeing them up with free_rsp_buf().
*/ */
static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb, const char *full_path,
__u32 desired_access, __u32 create_disposition, __u32 create_options, __u32 desired_access, __u32 create_disposition, __u32 create_options,
umode_t mode, void *ptr, int command, struct cifsFileInfo *cfile, umode_t mode, void *ptr, int command, struct cifsFileInfo *cfile,
__u8 **extbuf, size_t *extbuflen, __u8 **extbuf, size_t *extbuflen,
struct kvec *err_iov, int *err_buftype) struct kvec *out_iov, int *out_buftype)
{ {
struct cop_vars *vars = NULL; struct smb2_compound_vars *vars = NULL;
struct kvec *rsp_iov; struct kvec *rsp_iov;
struct smb_rqst *rqst; struct smb_rqst *rqst;
int rc; int rc;
@ -133,7 +121,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
/* Operation */ /* Operation */
switch (command) { switch (command) {
case SMB2_OP_QUERY_INFO: case SMB2_OP_QUERY_INFO:
rqst[num_rqst].rq_iov = &vars->qi_iov[0]; rqst[num_rqst].rq_iov = &vars->qi_iov;
rqst[num_rqst].rq_nvec = 1; rqst[num_rqst].rq_nvec = 1;
if (cfile) if (cfile)
@ -167,7 +155,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
full_path); full_path);
break; break;
case SMB2_OP_POSIX_QUERY_INFO: case SMB2_OP_POSIX_QUERY_INFO:
rqst[num_rqst].rq_iov = &vars->qi_iov[0]; rqst[num_rqst].rq_iov = &vars->qi_iov;
rqst[num_rqst].rq_nvec = 1; rqst[num_rqst].rq_nvec = 1;
if (cfile) if (cfile)
@ -375,7 +363,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
goto after_close; goto after_close;
/* Close */ /* Close */
flags |= CIFS_CP_CREATE_CLOSE_OP; flags |= CIFS_CP_CREATE_CLOSE_OP;
rqst[num_rqst].rq_iov = &vars->close_iov[0]; rqst[num_rqst].rq_iov = &vars->close_iov;
rqst[num_rqst].rq_nvec = 1; rqst[num_rqst].rq_nvec = 1;
rc = SMB2_close_init(tcon, server, rc = SMB2_close_init(tcon, server,
&rqst[num_rqst], COMPOUND_FID, &rqst[num_rqst], COMPOUND_FID,
@ -529,9 +517,9 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
if (cfile) if (cfile)
cifsFileInfo_put(cfile); cifsFileInfo_put(cfile);
if (rc && err_iov && err_buftype) { if (out_iov && out_buftype) {
memcpy(err_iov, rsp_iov, 3 * sizeof(*err_iov)); memcpy(out_iov, rsp_iov, 3 * sizeof(*out_iov));
memcpy(err_buftype, resp_buftype, 3 * sizeof(*err_buftype)); memcpy(out_buftype, resp_buftype, 3 * sizeof(*out_buftype));
} else { } else {
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 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[1], rsp_iov[1].iov_base);
@ -541,20 +529,50 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, static int parse_create_response(struct cifs_open_info_data *data,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb,
struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse) const struct kvec *iov)
{
struct smb2_create_rsp *rsp = iov->iov_base;
bool reparse_point = false;
u32 tag = 0;
int rc = 0;
switch (rsp->hdr.Status) {
case STATUS_STOPPED_ON_SYMLINK:
rc = smb2_parse_symlink_response(cifs_sb, iov,
&data->symlink_target);
if (rc)
return rc;
tag = IO_REPARSE_TAG_SYMLINK;
reparse_point = true;
break;
case STATUS_SUCCESS:
reparse_point = !!(rsp->Flags & SMB2_CREATE_FLAG_REPARSEPOINT);
break;
}
data->reparse_point = reparse_point;
data->reparse_tag = tag;
return rc;
}
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)
{ {
__u32 create_options = 0; __u32 create_options = 0;
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct cached_fid *cfid = NULL; struct cached_fid *cfid = NULL;
struct kvec err_iov[3] = {}; struct smb2_hdr *hdr;
int err_buftype[3] = {}; struct kvec out_iov[3] = {};
int out_buftype[3] = {};
bool islink; bool islink;
int rc, rc2; int rc, rc2;
*adjust_tz = false; data->adjust_tz = false;
*reparse = false; data->reparse_point = false;
if (strcmp(full_path, "")) if (strcmp(full_path, ""))
rc = -ENOENT; rc = -ENOENT;
@ -575,69 +593,73 @@ int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
cifs_get_readable_path(tcon, full_path, &cfile); cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, 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, create_options, ACL_NO_MODE, data, SMB2_OP_QUERY_INFO, cfile,
NULL, NULL, err_iov, err_buftype); NULL, NULL, out_iov, out_buftype);
if (rc) { hdr = out_iov[0].iov_base;
struct smb2_hdr *hdr = err_iov[0].iov_base; /*
* If first iov is unset, then SMB session was dropped or we've got a
* cached open file (@cfile).
*/
if (!hdr || out_buftype[0] == CIFS_NO_BUFFER)
goto out;
if (unlikely(!hdr || err_buftype[0] == CIFS_NO_BUFFER)) switch (rc) {
case 0:
case -EOPNOTSUPP:
rc = parse_create_response(data, cifs_sb, &out_iov[0]);
if (rc || !data->reparse_point)
goto out; goto out;
if (rc == -EOPNOTSUPP && hdr->Command == SMB2_CREATE &&
hdr->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;
create_options |= OPEN_REPARSE_POINT; /* Failed on a symbolic link - query a reparse point info */
cifs_get_readable_path(tcon, full_path, &cfile);
/* Failed on a symbolic link - query a reparse point info */ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
cifs_get_readable_path(tcon, full_path, &cfile); FILE_READ_ATTRIBUTES, FILE_OPEN,
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, create_options, ACL_NO_MODE, data,
FILE_READ_ATTRIBUTES, FILE_OPEN, SMB2_OP_QUERY_INFO, cfile, NULL, NULL,
create_options, ACL_NO_MODE, data, NULL, NULL);
SMB2_OP_QUERY_INFO, cfile, NULL, NULL, break;
NULL, NULL); case -EREMOTE:
break;
default:
if (hdr->Status != STATUS_OBJECT_NAME_INVALID)
break;
rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
full_path, &islink);
if (rc2) {
rc = rc2;
goto out; goto out;
} else if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) {
rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
full_path, &islink);
if (rc2) {
rc = rc2;
goto out;
}
if (islink)
rc = -EREMOTE;
} }
if (islink)
rc = -EREMOTE;
} }
out: out:
free_rsp_buf(err_buftype[0], err_iov[0].iov_base); free_rsp_buf(out_buftype[0], out_iov[0].iov_base);
free_rsp_buf(err_buftype[1], err_iov[1].iov_base); free_rsp_buf(out_buftype[1], out_iov[1].iov_base);
free_rsp_buf(err_buftype[2], err_iov[2].iov_base); free_rsp_buf(out_buftype[2], out_iov[2].iov_base);
return rc; return rc;
} }
int smb311_posix_query_path_info(const unsigned int xid,
int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb,
const char *full_path,
struct cifs_open_info_data *data, struct cifs_open_info_data *data,
struct cifs_sid *owner, struct cifs_sid *owner,
struct cifs_sid *group, struct cifs_sid *group)
bool *adjust_tz, bool *reparse)
{ {
int rc; int rc;
__u32 create_options = 0; __u32 create_options = 0;
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct kvec err_iov[3] = {}; struct kvec out_iov[3] = {};
int err_buftype[3] = {}; int out_buftype[3] = {};
__u8 *sidsbuf = NULL; __u8 *sidsbuf = NULL;
__u8 *sidsbuf_end = NULL; __u8 *sidsbuf_end = NULL;
size_t sidsbuflen = 0; size_t sidsbuflen = 0;
size_t owner_len, group_len; size_t owner_len, group_len;
*adjust_tz = false; data->adjust_tz = false;
*reparse = false; data->reparse_point = false;
/* /*
* BB TODO: Add support for using the cached root handle. * BB TODO: Add support for using the cached root handle.
@ -649,27 +671,33 @@ int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
cifs_get_readable_path(tcon, full_path, &cfile); cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, 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, create_options, ACL_NO_MODE, data, SMB2_OP_POSIX_QUERY_INFO, cfile,
&sidsbuf, &sidsbuflen, err_iov, err_buftype); &sidsbuf, &sidsbuflen, out_iov, out_buftype);
if (rc == -EOPNOTSUPP) { /*
/* BB TODO: When support for special files added to Samba re-verify this path */ * If first iov is unset, then SMB session was dropped or we've got a
if (err_iov[0].iov_base && err_buftype[0] != CIFS_NO_BUFFER && * cached open file (@cfile).
((struct smb2_hdr *)err_iov[0].iov_base)->Command == SMB2_CREATE && */
((struct smb2_hdr *)err_iov[0].iov_base)->Status == STATUS_STOPPED_ON_SYMLINK) { if (!out_iov[0].iov_base || out_buftype[0] == CIFS_NO_BUFFER)
rc = smb2_parse_symlink_response(cifs_sb, err_iov, &data->symlink_target); goto out;
if (rc)
goto out;
}
*reparse = true;
create_options |= OPEN_REPARSE_POINT;
switch (rc) {
case 0:
case -EOPNOTSUPP:
/* BB TODO: When support for special files added to Samba re-verify this path */
rc = parse_create_response(data, cifs_sb, &out_iov[0]);
if (rc || !data->reparse_point)
goto out;
create_options |= OPEN_REPARSE_POINT;
/* Failed on a symbolic link - query a reparse point info */ /* Failed on a symbolic link - query a reparse point info */
cifs_get_readable_path(tcon, full_path, &cfile); cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES,
FILE_OPEN, create_options, ACL_NO_MODE, data, FILE_OPEN, create_options, ACL_NO_MODE, data,
SMB2_OP_POSIX_QUERY_INFO, cfile, SMB2_OP_POSIX_QUERY_INFO, cfile,
&sidsbuf, &sidsbuflen, NULL, NULL); &sidsbuf, &sidsbuflen, NULL, NULL);
break;
} }
out:
if (rc == 0) { if (rc == 0) {
sidsbuf_end = sidsbuf + sidsbuflen; sidsbuf_end = sidsbuf + sidsbuflen;
@ -689,11 +717,10 @@ int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
memcpy(group, sidsbuf + owner_len, group_len); memcpy(group, sidsbuf + owner_len, group_len);
} }
out:
kfree(sidsbuf); kfree(sidsbuf);
free_rsp_buf(err_buftype[0], err_iov[0].iov_base); free_rsp_buf(out_buftype[0], out_iov[0].iov_base);
free_rsp_buf(err_buftype[1], err_iov[1].iov_base); free_rsp_buf(out_buftype[1], out_iov[1].iov_base);
free_rsp_buf(err_buftype[2], err_iov[2].iov_base); free_rsp_buf(out_buftype[2], out_iov[2].iov_base);
return rc; return rc;
} }

View File

@ -145,7 +145,7 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
__u64 mid; __u64 mid;
/* If server is a channel, select the primary channel */ /* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
/* /*
* Add function to do table lookup of StructureSize by command * Add function to do table lookup of StructureSize by command
@ -623,7 +623,7 @@ smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
cifs_dbg(FYI, "Checking for lease break\n"); cifs_dbg(FYI, "Checking for lease break\n");
/* If server is a channel, select the primary channel */ /* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
/* look up tcon based on tid & uid */ /* look up tcon based on tid & uid */
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
@ -698,7 +698,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
cifs_dbg(FYI, "oplock level 0x%x\n", rsp->OplockLevel); cifs_dbg(FYI, "oplock level 0x%x\n", rsp->OplockLevel);
/* If server is a channel, select the primary channel */ /* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
/* look up tcon based on tid & uid */ /* look up tcon based on tid & uid */
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);

View File

@ -172,8 +172,17 @@ smb2_set_credits(struct TCP_Server_Info *server, const int val)
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
server->credits = val; server->credits = val;
if (val == 1) if (val == 1) {
server->reconnect_instance++; server->reconnect_instance++;
/*
* ChannelSequence updated for all channels in primary channel so that consistent
* across SMB3 requests sent on any channel. See MS-SMB2 3.2.4.1 and 3.2.7.1
*/
if (SERVER_IS_CHAN(server))
server->primary_server->channel_sequence_num++;
else
server->channel_sequence_num++;
}
scredits = server->credits; scredits = server->credits;
in_flight = server->in_flight; in_flight = server->in_flight;
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
@ -1075,31 +1084,28 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
static int static int
smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
const char *path, const char *ea_name, const void *ea_value, const char *path, const char *ea_name, const void *ea_value,
const __u16 ea_value_len, const struct nls_table *nls_codepage, const __u16 ea_value_len, const struct nls_table *nls_codepage,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
struct smb2_compound_vars *vars;
struct cifs_ses *ses = tcon->ses; struct cifs_ses *ses = tcon->ses;
struct TCP_Server_Info *server = cifs_pick_channel(ses); struct TCP_Server_Info *server = cifs_pick_channel(ses);
struct smb_rqst *rqst;
struct kvec *rsp_iov;
__le16 *utf16_path = NULL; __le16 *utf16_path = NULL;
int ea_name_len = strlen(ea_name); int ea_name_len = strlen(ea_name);
int flags = CIFS_CP_CREATE_CLOSE_OP; int flags = CIFS_CP_CREATE_CLOSE_OP;
int len; int len;
struct smb_rqst rqst[3];
int resp_buftype[3]; int resp_buftype[3];
struct kvec rsp_iov[3];
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
struct cifs_open_parms oparms; struct cifs_open_parms oparms;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE; __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
struct cifs_fid fid; struct cifs_fid fid;
struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE];
unsigned int size[1]; unsigned int size[1];
void *data[1]; void *data[1];
struct smb2_file_full_ea_info *ea = NULL; struct smb2_file_full_ea_info *ea = NULL;
struct kvec close_iov[1];
struct smb2_query_info_rsp *rsp; struct smb2_query_info_rsp *rsp;
int rc, used_len = 0; int rc, used_len = 0;
@ -1113,9 +1119,14 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
if (!utf16_path) if (!utf16_path)
return -ENOMEM; return -ENOMEM;
memset(rqst, 0, sizeof(rqst));
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
memset(rsp_iov, 0, sizeof(rsp_iov)); vars = kzalloc(sizeof(*vars), GFP_KERNEL);
if (!vars) {
rc = -ENOMEM;
goto out_free_path;
}
rqst = vars->rqst;
rsp_iov = vars->rsp_iov;
if (ses->server->ops->query_all_EAs) { if (ses->server->ops->query_all_EAs) {
if (!ea_value) { if (!ea_value) {
@ -1160,8 +1171,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
} }
/* Open */ /* Open */
memset(&open_iov, 0, sizeof(open_iov)); rqst[0].rq_iov = vars->open_iov;
rqst[0].rq_iov = open_iov;
rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
oparms = (struct cifs_open_parms) { oparms = (struct cifs_open_parms) {
@ -1181,8 +1191,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
/* Set Info */ /* Set Info */
memset(&si_iov, 0, sizeof(si_iov)); rqst[1].rq_iov = vars->si_iov;
rqst[1].rq_iov = si_iov;
rqst[1].rq_nvec = 1; rqst[1].rq_nvec = 1;
len = sizeof(*ea) + ea_name_len + ea_value_len + 1; len = sizeof(*ea) + ea_name_len + ea_value_len + 1;
@ -1210,10 +1219,8 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
smb2_set_next_command(tcon, &rqst[1]); smb2_set_next_command(tcon, &rqst[1]);
smb2_set_related(&rqst[1]); smb2_set_related(&rqst[1]);
/* Close */ /* Close */
memset(&close_iov, 0, sizeof(close_iov)); rqst[2].rq_iov = &vars->close_iov;
rqst[2].rq_iov = close_iov;
rqst[2].rq_nvec = 1; rqst[2].rq_nvec = 1;
rc = SMB2_close_init(tcon, server, rc = SMB2_close_init(tcon, server,
&rqst[2], COMPOUND_FID, COMPOUND_FID, false); &rqst[2], COMPOUND_FID, COMPOUND_FID, false);
@ -1228,13 +1235,15 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
sea_exit: sea_exit:
kfree(ea); kfree(ea);
kfree(utf16_path);
SMB2_open_free(&rqst[0]); SMB2_open_free(&rqst[0]);
SMB2_set_info_free(&rqst[1]); SMB2_set_info_free(&rqst[1]);
SMB2_close_free(&rqst[2]); SMB2_close_free(&rqst[2]);
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 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[1], rsp_iov[1].iov_base);
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
kfree(vars);
out_free_path:
kfree(utf16_path);
return rc; return rc;
} }
#endif #endif
@ -1446,16 +1455,6 @@ req_res_key_exit:
return rc; return rc;
} }
struct iqi_vars {
struct smb_rqst rqst[3];
struct kvec rsp_iov[3];
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
struct kvec qi_iov[1];
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE];
struct kvec close_iov[1];
};
static int static int
smb2_ioctl_query_info(const unsigned int xid, smb2_ioctl_query_info(const unsigned int xid,
struct cifs_tcon *tcon, struct cifs_tcon *tcon,
@ -1463,7 +1462,7 @@ smb2_ioctl_query_info(const unsigned int xid,
__le16 *path, int is_dir, __le16 *path, int is_dir,
unsigned long p) unsigned long p)
{ {
struct iqi_vars *vars; struct smb2_compound_vars *vars;
struct smb_rqst *rqst; struct smb_rqst *rqst;
struct kvec *rsp_iov; struct kvec *rsp_iov;
struct cifs_ses *ses = tcon->ses; struct cifs_ses *ses = tcon->ses;
@ -1581,7 +1580,7 @@ smb2_ioctl_query_info(const unsigned int xid,
rc = -EINVAL; rc = -EINVAL;
goto free_open_req; goto free_open_req;
} }
rqst[1].rq_iov = &vars->si_iov[0]; rqst[1].rq_iov = vars->si_iov;
rqst[1].rq_nvec = 1; rqst[1].rq_nvec = 1;
/* MS-FSCC 2.4.13 FileEndOfFileInformation */ /* MS-FSCC 2.4.13 FileEndOfFileInformation */
@ -1593,7 +1592,7 @@ smb2_ioctl_query_info(const unsigned int xid,
SMB2_O_INFO_FILE, 0, data, size); SMB2_O_INFO_FILE, 0, data, size);
free_req1_func = SMB2_set_info_free; free_req1_func = SMB2_set_info_free;
} else if (qi.flags == PASSTHRU_QUERY_INFO) { } else if (qi.flags == PASSTHRU_QUERY_INFO) {
rqst[1].rq_iov = &vars->qi_iov[0]; rqst[1].rq_iov = &vars->qi_iov;
rqst[1].rq_nvec = 1; rqst[1].rq_nvec = 1;
rc = SMB2_query_info_init(tcon, server, rc = SMB2_query_info_init(tcon, server,
@ -1615,7 +1614,7 @@ smb2_ioctl_query_info(const unsigned int xid,
smb2_set_related(&rqst[1]); smb2_set_related(&rqst[1]);
/* Close */ /* Close */
rqst[2].rq_iov = &vars->close_iov[0]; rqst[2].rq_iov = &vars->close_iov;
rqst[2].rq_nvec = 1; rqst[2].rq_nvec = 1;
rc = SMB2_close_init(tcon, server, rc = SMB2_close_init(tcon, server,
@ -2408,7 +2407,7 @@ smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
return false; return false;
/* If server is a channel, select the primary channel */ /* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
@ -2524,15 +2523,13 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
struct kvec *rsp, int *buftype, struct kvec *rsp, int *buftype,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
struct smb2_compound_vars *vars;
struct cifs_ses *ses = tcon->ses; struct cifs_ses *ses = tcon->ses;
struct TCP_Server_Info *server = cifs_pick_channel(ses); struct TCP_Server_Info *server = cifs_pick_channel(ses);
int flags = CIFS_CP_CREATE_CLOSE_OP; int flags = CIFS_CP_CREATE_CLOSE_OP;
struct smb_rqst rqst[3]; struct smb_rqst *rqst;
int resp_buftype[3]; int resp_buftype[3];
struct kvec rsp_iov[3]; struct kvec *rsp_iov;
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
struct kvec qi_iov[1];
struct kvec close_iov[1];
u8 oplock = SMB2_OPLOCK_LEVEL_NONE; u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
struct cifs_open_parms oparms; struct cifs_open_parms oparms;
struct cifs_fid fid; struct cifs_fid fid;
@ -2549,9 +2546,14 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
if (smb3_encryption_required(tcon)) if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ; flags |= CIFS_TRANSFORM_REQ;
memset(rqst, 0, sizeof(rqst));
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
memset(rsp_iov, 0, sizeof(rsp_iov)); vars = kzalloc(sizeof(*vars), GFP_KERNEL);
if (!vars) {
rc = -ENOMEM;
goto out_free_path;
}
rqst = vars->rqst;
rsp_iov = vars->rsp_iov;
/* /*
* We can only call this for things we know are directories. * We can only call this for things we know are directories.
@ -2560,8 +2562,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
open_cached_dir(xid, tcon, path, cifs_sb, false, open_cached_dir(xid, tcon, path, cifs_sb, false,
&cfid); /* cfid null if open dir failed */ &cfid); /* cfid null if open dir failed */
memset(&open_iov, 0, sizeof(open_iov)); rqst[0].rq_iov = vars->open_iov;
rqst[0].rq_iov = open_iov;
rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
oparms = (struct cifs_open_parms) { oparms = (struct cifs_open_parms) {
@ -2579,8 +2580,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
goto qic_exit; goto qic_exit;
smb2_set_next_command(tcon, &rqst[0]); smb2_set_next_command(tcon, &rqst[0]);
memset(&qi_iov, 0, sizeof(qi_iov)); rqst[1].rq_iov = &vars->qi_iov;
rqst[1].rq_iov = qi_iov;
rqst[1].rq_nvec = 1; rqst[1].rq_nvec = 1;
if (cfid) { if (cfid) {
@ -2607,8 +2607,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
smb2_set_related(&rqst[1]); smb2_set_related(&rqst[1]);
} }
memset(&close_iov, 0, sizeof(close_iov)); rqst[2].rq_iov = &vars->close_iov;
rqst[2].rq_iov = close_iov;
rqst[2].rq_nvec = 1; rqst[2].rq_nvec = 1;
rc = SMB2_close_init(tcon, server, rc = SMB2_close_init(tcon, server,
@ -2639,7 +2638,6 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
*buftype = resp_buftype[1]; *buftype = resp_buftype[1];
qic_exit: qic_exit:
kfree(utf16_path);
SMB2_open_free(&rqst[0]); SMB2_open_free(&rqst[0]);
SMB2_query_info_free(&rqst[1]); SMB2_query_info_free(&rqst[1]);
SMB2_close_free(&rqst[2]); SMB2_close_free(&rqst[2]);
@ -2647,6 +2645,9 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
if (cfid) if (cfid)
close_cached_dir(cfid); close_cached_dir(cfid);
kfree(vars);
out_free_path:
kfree(utf16_path);
return rc; return rc;
} }
@ -2949,154 +2950,32 @@ parse_reparse_point(struct reparse_data_buffer *buf,
} }
} }
static int static int smb2_query_symlink(const unsigned int xid,
smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb,
char **target_path, bool is_reparse_point) const char *full_path,
char **target_path,
struct kvec *rsp_iov)
{ {
int rc; struct reparse_data_buffer *buf;
__le16 *utf16_path = NULL; struct smb2_ioctl_rsp *io = rsp_iov->iov_base;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE; u32 plen = le32_to_cpu(io->OutputCount);
struct cifs_open_parms oparms;
struct cifs_fid fid;
struct kvec err_iov = {NULL, 0};
struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
int flags = CIFS_CP_CREATE_CLOSE_OP;
struct smb_rqst rqst[3];
int resp_buftype[3];
struct kvec rsp_iov[3];
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
struct kvec close_iov[1];
struct smb2_create_rsp *create_rsp;
struct smb2_ioctl_rsp *ioctl_rsp;
struct reparse_data_buffer *reparse_buf;
int create_options = is_reparse_point ? OPEN_REPARSE_POINT : 0;
u32 plen;
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
*target_path = NULL; buf = (struct reparse_data_buffer *)((u8 *)io +
le32_to_cpu(io->OutputOffset));
if (smb3_encryption_required(tcon)) return parse_reparse_point(buf, plen, target_path, cifs_sb);
flags |= CIFS_TRANSFORM_REQ;
memset(rqst, 0, sizeof(rqst));
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
memset(rsp_iov, 0, sizeof(rsp_iov));
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
if (!utf16_path)
return -ENOMEM;
/* Open */
memset(&open_iov, 0, sizeof(open_iov));
rqst[0].rq_iov = open_iov;
rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
oparms = (struct cifs_open_parms) {
.tcon = tcon,
.path = full_path,
.desired_access = FILE_READ_ATTRIBUTES,
.disposition = FILE_OPEN,
.create_options = cifs_create_options(cifs_sb, create_options),
.fid = &fid,
};
rc = SMB2_open_init(tcon, server,
&rqst[0], &oplock, &oparms, utf16_path);
if (rc)
goto querty_exit;
smb2_set_next_command(tcon, &rqst[0]);
/* IOCTL */
memset(&io_iov, 0, sizeof(io_iov));
rqst[1].rq_iov = io_iov;
rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
rc = SMB2_ioctl_init(tcon, server,
&rqst[1], fid.persistent_fid,
fid.volatile_fid, FSCTL_GET_REPARSE_POINT, NULL, 0,
CIFSMaxBufSize -
MAX_SMB2_CREATE_RESPONSE_SIZE -
MAX_SMB2_CLOSE_RESPONSE_SIZE);
if (rc)
goto querty_exit;
smb2_set_next_command(tcon, &rqst[1]);
smb2_set_related(&rqst[1]);
/* Close */
memset(&close_iov, 0, sizeof(close_iov));
rqst[2].rq_iov = close_iov;
rqst[2].rq_nvec = 1;
rc = SMB2_close_init(tcon, server,
&rqst[2], COMPOUND_FID, COMPOUND_FID, false);
if (rc)
goto querty_exit;
smb2_set_related(&rqst[2]);
rc = compound_send_recv(xid, tcon->ses, server,
flags, 3, rqst,
resp_buftype, rsp_iov);
create_rsp = rsp_iov[0].iov_base;
if (create_rsp && create_rsp->hdr.Status)
err_iov = rsp_iov[0];
ioctl_rsp = rsp_iov[1].iov_base;
/*
* Open was successful and we got an ioctl response.
*/
if ((rc == 0) && (is_reparse_point)) {
/* See MS-FSCC 2.3.23 */
reparse_buf = (struct reparse_data_buffer *)
((char *)ioctl_rsp +
le32_to_cpu(ioctl_rsp->OutputOffset));
plen = le32_to_cpu(ioctl_rsp->OutputCount);
if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) >
rsp_iov[1].iov_len) {
cifs_tcon_dbg(VFS, "srv returned invalid ioctl len: %d\n",
plen);
rc = -EIO;
goto querty_exit;
}
rc = parse_reparse_point(reparse_buf, plen, target_path,
cifs_sb);
goto querty_exit;
}
if (!rc || !err_iov.iov_base) {
rc = -ENOENT;
goto querty_exit;
}
rc = smb2_parse_symlink_response(cifs_sb, &err_iov, target_path);
querty_exit:
cifs_dbg(FYI, "query symlink rc %d\n", rc);
kfree(utf16_path);
SMB2_open_free(&rqst[0]);
SMB2_ioctl_free(&rqst[1]);
SMB2_close_free(&rqst[2]);
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);
return rc;
} }
int static int smb2_query_reparse_point(const unsigned int xid,
smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb,
__u32 *tag) const char *full_path,
u32 *tag, struct kvec *rsp,
int *rsp_buftype)
{ {
struct smb2_compound_vars *vars;
int rc; int rc;
__le16 *utf16_path = NULL; __le16 *utf16_path = NULL;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE; __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
@ -3104,12 +2983,9 @@ smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_fid fid; struct cifs_fid fid;
struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
int flags = CIFS_CP_CREATE_CLOSE_OP; int flags = CIFS_CP_CREATE_CLOSE_OP;
struct smb_rqst rqst[3]; struct smb_rqst *rqst;
int resp_buftype[3]; int resp_buftype[3];
struct kvec rsp_iov[3]; struct kvec *rsp_iov;
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
struct kvec close_iov[1];
struct smb2_ioctl_rsp *ioctl_rsp; struct smb2_ioctl_rsp *ioctl_rsp;
struct reparse_data_buffer *reparse_buf; struct reparse_data_buffer *reparse_buf;
u32 plen; u32 plen;
@ -3119,20 +2995,24 @@ smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
if (smb3_encryption_required(tcon)) if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ; flags |= CIFS_TRANSFORM_REQ;
memset(rqst, 0, sizeof(rqst));
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
memset(rsp_iov, 0, sizeof(rsp_iov));
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
if (!utf16_path) if (!utf16_path)
return -ENOMEM; return -ENOMEM;
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
vars = kzalloc(sizeof(*vars), GFP_KERNEL);
if (!vars) {
rc = -ENOMEM;
goto out_free_path;
}
rqst = vars->rqst;
rsp_iov = vars->rsp_iov;
/* /*
* setup smb2open - TODO add optimization to call cifs_get_readable_path * setup smb2open - TODO add optimization to call cifs_get_readable_path
* to see if there is a handle already open that we can use * to see if there is a handle already open that we can use
*/ */
memset(&open_iov, 0, sizeof(open_iov)); rqst[0].rq_iov = vars->open_iov;
rqst[0].rq_iov = open_iov;
rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
oparms = (struct cifs_open_parms) { oparms = (struct cifs_open_parms) {
@ -3152,8 +3032,7 @@ smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
/* IOCTL */ /* IOCTL */
memset(&io_iov, 0, sizeof(io_iov)); rqst[1].rq_iov = vars->io_iov;
rqst[1].rq_iov = io_iov;
rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
rc = SMB2_ioctl_init(tcon, server, rc = SMB2_ioctl_init(tcon, server,
@ -3168,10 +3047,8 @@ smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
smb2_set_next_command(tcon, &rqst[1]); smb2_set_next_command(tcon, &rqst[1]);
smb2_set_related(&rqst[1]); smb2_set_related(&rqst[1]);
/* Close */ /* Close */
memset(&close_iov, 0, sizeof(close_iov)); rqst[2].rq_iov = &vars->close_iov;
rqst[2].rq_iov = close_iov;
rqst[2].rq_nvec = 1; rqst[2].rq_nvec = 1;
rc = SMB2_close_init(tcon, server, rc = SMB2_close_init(tcon, server,
@ -3206,16 +3083,21 @@ smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
goto query_rp_exit; goto query_rp_exit;
} }
*tag = le32_to_cpu(reparse_buf->ReparseTag); *tag = le32_to_cpu(reparse_buf->ReparseTag);
*rsp = rsp_iov[1];
*rsp_buftype = resp_buftype[1];
resp_buftype[1] = CIFS_NO_BUFFER;
} }
query_rp_exit: query_rp_exit:
kfree(utf16_path);
SMB2_open_free(&rqst[0]); SMB2_open_free(&rqst[0]);
SMB2_ioctl_free(&rqst[1]); SMB2_ioctl_free(&rqst[1]);
SMB2_close_free(&rqst[2]); SMB2_close_free(&rqst[2]);
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 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[1], rsp_iov[1].iov_base);
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
kfree(vars);
out_free_path:
kfree(utf16_path);
return rc; return rc;
} }
@ -4401,7 +4283,7 @@ smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key)
u8 *ses_enc_key; u8 *ses_enc_key;
/* If server is a channel, select the primary channel */ /* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
@ -5299,6 +5181,7 @@ struct smb_version_operations smb20_operations = {
.can_echo = smb2_can_echo, .can_echo = smb2_can_echo,
.echo = SMB2_echo, .echo = SMB2_echo,
.query_path_info = smb2_query_path_info, .query_path_info = smb2_query_path_info,
.query_reparse_point = smb2_query_reparse_point,
.get_srv_inum = smb2_get_srv_inum, .get_srv_inum = smb2_get_srv_inum,
.query_file_info = smb2_query_file_info, .query_file_info = smb2_query_file_info,
.set_path_size = smb2_set_path_size, .set_path_size = smb2_set_path_size,
@ -5400,6 +5283,7 @@ struct smb_version_operations smb21_operations = {
.can_echo = smb2_can_echo, .can_echo = smb2_can_echo,
.echo = SMB2_echo, .echo = SMB2_echo,
.query_path_info = smb2_query_path_info, .query_path_info = smb2_query_path_info,
.query_reparse_point = smb2_query_reparse_point,
.get_srv_inum = smb2_get_srv_inum, .get_srv_inum = smb2_get_srv_inum,
.query_file_info = smb2_query_file_info, .query_file_info = smb2_query_file_info,
.set_path_size = smb2_set_path_size, .set_path_size = smb2_set_path_size,
@ -5504,7 +5388,7 @@ struct smb_version_operations smb30_operations = {
.echo = SMB2_echo, .echo = SMB2_echo,
.query_path_info = smb2_query_path_info, .query_path_info = smb2_query_path_info,
/* WSL tags introduced long after smb2.1, enable for SMB3, 3.11 only */ /* WSL tags introduced long after smb2.1, enable for SMB3, 3.11 only */
.query_reparse_tag = smb2_query_reparse_tag, .query_reparse_point = smb2_query_reparse_point,
.get_srv_inum = smb2_get_srv_inum, .get_srv_inum = smb2_get_srv_inum,
.query_file_info = smb2_query_file_info, .query_file_info = smb2_query_file_info,
.set_path_size = smb2_set_path_size, .set_path_size = smb2_set_path_size,
@ -5617,7 +5501,7 @@ struct smb_version_operations smb311_operations = {
.can_echo = smb2_can_echo, .can_echo = smb2_can_echo,
.echo = SMB2_echo, .echo = SMB2_echo,
.query_path_info = smb2_query_path_info, .query_path_info = smb2_query_path_info,
.query_reparse_tag = smb2_query_reparse_tag, .query_reparse_point = smb2_query_reparse_point,
.get_srv_inum = smb2_get_srv_inum, .get_srv_inum = smb2_get_srv_inum,
.query_file_info = smb2_query_file_info, .query_file_info = smb2_query_file_info,
.set_path_size = smb2_set_path_size, .set_path_size = smb2_set_path_size,

View File

@ -88,9 +88,20 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd,
const struct cifs_tcon *tcon, const struct cifs_tcon *tcon,
struct TCP_Server_Info *server) struct TCP_Server_Info *server)
{ {
struct smb3_hdr_req *smb3_hdr;
shdr->ProtocolId = SMB2_PROTO_NUMBER; shdr->ProtocolId = SMB2_PROTO_NUMBER;
shdr->StructureSize = cpu_to_le16(64); shdr->StructureSize = cpu_to_le16(64);
shdr->Command = smb2_cmd; shdr->Command = smb2_cmd;
if (server->dialect >= SMB30_PROT_ID) {
/* After reconnect SMB3 must set ChannelSequence on subsequent reqs */
smb3_hdr = (struct smb3_hdr_req *)shdr;
/* if primary channel is not set yet, use default channel for chan sequence num */
if (SERVER_IS_CHAN(server))
smb3_hdr->ChannelSequence =
cpu_to_le16(server->primary_server->channel_sequence_num);
else
smb3_hdr->ChannelSequence = cpu_to_le16(server->channel_sequence_num);
}
if (server) { if (server) {
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
/* Request up to 10 credits but don't go over the limit. */ /* Request up to 10 credits but don't go over the limit. */
@ -553,7 +564,7 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
* secondary channels don't have the hostname field populated * secondary channels don't have the hostname field populated
* use the hostname field in the primary channel instead * use the hostname field in the primary channel instead
*/ */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
cifs_server_lock(pserver); cifs_server_lock(pserver);
hostname = pserver->hostname; hostname = pserver->hostname;
if (hostname && (hostname[0] != 0)) { if (hostname && (hostname[0] != 0)) {
@ -2570,8 +2581,8 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
/* Do not append the separator if the path is empty */ /* Do not append the separator if the path is empty */
if (path[0] != cpu_to_le16(0x0000)) { if (path[0] != cpu_to_le16(0x0000)) {
UniStrcat(*out_path, sep); UniStrcat((wchar_t *)*out_path, (wchar_t *)sep);
UniStrcat(*out_path, path); UniStrcat((wchar_t *)*out_path, (wchar_t *)path);
} }
unload_nls(cp); unload_nls(cp);
@ -3785,7 +3796,7 @@ void smb2_reconnect_server(struct work_struct *work)
bool resched = false; bool resched = false;
/* If server is a channel, select the primary channel */ /* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
/* Prevent simultaneous reconnects that can corrupt tcon->rlist list */ /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
mutex_lock(&pserver->reconnect_mutex); mutex_lock(&pserver->reconnect_mutex);

View File

@ -56,9 +56,11 @@ extern int smb3_handle_read_data(struct TCP_Server_Info *server,
extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *path, struct cifs_sb_info *cifs_sb, const char *path,
__u32 *reparse_tag); __u32 *reparse_tag);
int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, int smb2_query_path_info(const unsigned int xid,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_tcon *tcon,
struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); struct cifs_sb_info *cifs_sb,
const char *full_path,
struct cifs_open_info_data *data);
extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
const char *full_path, __u64 size, const char *full_path, __u64 size,
struct cifs_sb_info *cifs_sb, bool set_alloc); struct cifs_sb_info *cifs_sb, bool set_alloc);
@ -275,12 +277,13 @@ extern int smb2_query_info_compound(const unsigned int xid,
struct kvec *rsp, int *buftype, struct kvec *rsp, int *buftype,
struct cifs_sb_info *cifs_sb); struct cifs_sb_info *cifs_sb);
/* query path info from the server using SMB311 POSIX extensions*/ /* query path info from the server using SMB311 POSIX extensions*/
int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, int smb311_posix_query_path_info(const unsigned int xid,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
const char *full_path,
struct cifs_open_info_data *data, struct cifs_open_info_data *data,
struct cifs_sid *owner, struct cifs_sid *owner,
struct cifs_sid *group, struct cifs_sid *group);
bool *adjust_tz, bool *reparse);
int posix_info_parse(const void *beg, const void *end, int posix_info_parse(const void *beg, const void *end,
struct smb2_posix_info_parsed *out); struct smb2_posix_info_parsed *out);
int posix_info_sid_size(const void *beg, const void *end); int posix_info_sid_size(const void *beg, const void *end);

View File

@ -86,7 +86,7 @@ int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
/* If server is a channel, select the primary channel */ /* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
if (ses->Suid == ses_id) if (ses->Suid == ses_id)
@ -149,7 +149,7 @@ smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
struct cifs_ses *ses; struct cifs_ses *ses;
/* If server is a channel, select the primary channel */ /* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
if (ses->Suid != ses_id) if (ses->Suid != ses_id)

View File

@ -416,13 +416,19 @@ out:
return rc; return rc;
} }
struct send_req_vars {
struct smb2_transform_hdr tr_hdr;
struct smb_rqst rqst[MAX_COMPOUND];
struct kvec iov;
};
static int static int
smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
struct smb_rqst *rqst, int flags) struct smb_rqst *rqst, int flags)
{ {
struct kvec iov; struct send_req_vars *vars;
struct smb2_transform_hdr *tr_hdr; struct smb_rqst *cur_rqst;
struct smb_rqst cur_rqst[MAX_COMPOUND]; struct kvec *iov;
int rc; int rc;
if (!(flags & CIFS_TRANSFORM_REQ)) if (!(flags & CIFS_TRANSFORM_REQ))
@ -436,16 +442,15 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
return -EIO; return -EIO;
} }
tr_hdr = kzalloc(sizeof(*tr_hdr), GFP_NOFS); vars = kzalloc(sizeof(*vars), GFP_NOFS);
if (!tr_hdr) if (!vars)
return -ENOMEM; return -ENOMEM;
cur_rqst = vars->rqst;
iov = &vars->iov;
memset(&cur_rqst[0], 0, sizeof(cur_rqst)); iov->iov_base = &vars->tr_hdr;
memset(&iov, 0, sizeof(iov)); iov->iov_len = sizeof(vars->tr_hdr);
cur_rqst[0].rq_iov = iov;
iov.iov_base = tr_hdr;
iov.iov_len = sizeof(*tr_hdr);
cur_rqst[0].rq_iov = &iov;
cur_rqst[0].rq_nvec = 1; cur_rqst[0].rq_nvec = 1;
rc = server->ops->init_transform_rq(server, num_rqst + 1, rc = server->ops->init_transform_rq(server, num_rqst + 1,
@ -456,7 +461,7 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
rc = __smb_send_rqst(server, num_rqst + 1, &cur_rqst[0]); rc = __smb_send_rqst(server, num_rqst + 1, &cur_rqst[0]);
smb3_free_compound_rqst(num_rqst, &cur_rqst[1]); smb3_free_compound_rqst(num_rqst, &cur_rqst[1]);
out: out:
kfree(tr_hdr); kfree(vars);
return rc; return rc;
} }

View File

@ -153,6 +153,28 @@ struct smb2_hdr {
__u8 Signature[16]; __u8 Signature[16];
} __packed; } __packed;
struct smb3_hdr_req {
__le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */
__le16 StructureSize; /* 64 */
__le16 CreditCharge; /* MBZ */
__le16 ChannelSequence; /* See MS-SMB2 3.2.4.1 and 3.2.7.1 */
__le16 Reserved;
__le16 Command;
__le16 CreditRequest; /* CreditResponse */
__le32 Flags;
__le32 NextCommand;
__le64 MessageId;
union {
struct {
__le32 ProcessId;
__le32 TreeId;
} __packed SyncId;
__le64 AsyncId;
} __packed Id;
__le64 SessionId;
__u8 Signature[16];
} __packed;
struct smb2_pdu { struct smb2_pdu {
struct smb2_hdr hdr; struct smb2_hdr hdr;
__le16 StructureSize2; /* size of wct area (varies, request specific) */ __le16 StructureSize2; /* size of wct area (varies, request specific) */

View File

@ -5,6 +5,7 @@ config SMB_SERVER
depends on FILE_LOCKING depends on FILE_LOCKING
select NLS select NLS
select NLS_UTF8 select NLS_UTF8
select NLS_UCS2_UTILS
select CRYPTO select CRYPTO
select CRYPTO_MD5 select CRYPTO_MD5
select CRYPTO_HMAC select CRYPTO_HMAC

View File

@ -11,7 +11,6 @@
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include "glob.h" #include "glob.h"
#include "unicode.h" #include "unicode.h"
#include "uniupr.h"
#include "smb_common.h" #include "smb_common.h"
/* /*

View File

@ -18,49 +18,14 @@
* This is a compressed table of upper and lower case conversion. * This is a compressed table of upper and lower case conversion.
* *
*/ */
#ifndef _CIFS_UNICODE_H #ifndef _SMB_UNICODE_H
#define _CIFS_UNICODE_H #define _SMB_UNICODE_H
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/nls.h> #include <linux/nls.h>
#include <linux/unicode.h> #include <linux/unicode.h>
#include "../../nls/nls_ucs2_utils.h"
#define UNIUPR_NOLOWER /* Example to not expand lower case tables */
/*
* Windows maps these to the user defined 16 bit Unicode range since they are
* reserved symbols (along with \ and /), otherwise illegal to store
* in filenames in NTFS
*/
#define UNI_ASTERISK ((__u16)('*' + 0xF000))
#define UNI_QUESTION ((__u16)('?' + 0xF000))
#define UNI_COLON ((__u16)(':' + 0xF000))
#define UNI_GRTRTHAN ((__u16)('>' + 0xF000))
#define UNI_LESSTHAN ((__u16)('<' + 0xF000))
#define UNI_PIPE ((__u16)('|' + 0xF000))
#define UNI_SLASH ((__u16)('\\' + 0xF000))
/* Just define what we want from uniupr.h. We don't want to define the tables
* in each source file.
*/
#ifndef UNICASERANGE_DEFINED
struct UniCaseRange {
wchar_t start;
wchar_t end;
signed char *table;
};
#endif /* UNICASERANGE_DEFINED */
#ifndef UNIUPR_NOUPPER
extern signed char SmbUniUpperTable[512];
extern const struct UniCaseRange SmbUniUpperRange[];
#endif /* UNIUPR_NOUPPER */
#ifndef UNIUPR_NOLOWER
extern signed char CifsUniLowerTable[512];
extern const struct UniCaseRange CifsUniLowerRange[];
#endif /* UNIUPR_NOLOWER */
#ifdef __KERNEL__ #ifdef __KERNEL__
int smb_strtoUTF16(__le16 *to, const char *from, int len, int smb_strtoUTF16(__le16 *to, const char *from, int len,
@ -73,286 +38,4 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename);
#endif #endif
/* #endif /* _SMB_UNICODE_H */
* UniStrcat: Concatenate the second string to the first
*
* Returns:
* Address of the first string
*/
static inline wchar_t *UniStrcat(wchar_t *ucs1, const wchar_t *ucs2)
{
wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */
while (*ucs1++)
/*NULL*/; /* To end of first string */
ucs1--; /* Return to the null */
while ((*ucs1++ = *ucs2++))
/*NULL*/; /* copy string 2 over */
return anchor;
}
/*
* UniStrchr: Find a character in a string
*
* Returns:
* Address of first occurrence of character in string
* or NULL if the character is not in the string
*/
static inline wchar_t *UniStrchr(const wchar_t *ucs, wchar_t uc)
{
while ((*ucs != uc) && *ucs)
ucs++;
if (*ucs == uc)
return (wchar_t *)ucs;
return NULL;
}
/*
* UniStrcmp: Compare two strings
*
* Returns:
* < 0: First string is less than second
* = 0: Strings are equal
* > 0: First string is greater than second
*/
static inline int UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2)
{
while ((*ucs1 == *ucs2) && *ucs1) {
ucs1++;
ucs2++;
}
return (int)*ucs1 - (int)*ucs2;
}
/*
* UniStrcpy: Copy a string
*/
static inline wchar_t *UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2)
{
wchar_t *anchor = ucs1; /* save the start of result string */
while ((*ucs1++ = *ucs2++))
/*NULL*/;
return anchor;
}
/*
* UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes)
*/
static inline size_t UniStrlen(const wchar_t *ucs1)
{
int i = 0;
while (*ucs1++)
i++;
return i;
}
/*
* UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a
* string (length limited)
*/
static inline size_t UniStrnlen(const wchar_t *ucs1, int maxlen)
{
int i = 0;
while (*ucs1++) {
i++;
if (i >= maxlen)
break;
}
return i;
}
/*
* UniStrncat: Concatenate length limited string
*/
static inline wchar_t *UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
wchar_t *anchor = ucs1; /* save pointer to string 1 */
while (*ucs1++)
/*NULL*/;
ucs1--; /* point to null terminator of s1 */
while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */
ucs1++;
ucs2++;
}
*ucs1 = 0; /* Null terminate the result */
return anchor;
}
/*
* UniStrncmp: Compare length limited string
*/
static inline int UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
if (!n)
return 0; /* Null strings are equal */
while ((*ucs1 == *ucs2) && *ucs1 && --n) {
ucs1++;
ucs2++;
}
return (int)*ucs1 - (int)*ucs2;
}
/*
* UniStrncmp_le: Compare length limited string - native to little-endian
*/
static inline int
UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
if (!n)
return 0; /* Null strings are equal */
while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) {
ucs1++;
ucs2++;
}
return (int)*ucs1 - (int)__le16_to_cpu(*ucs2);
}
/*
* UniStrncpy: Copy length limited string with pad
*/
static inline wchar_t *UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
wchar_t *anchor = ucs1;
while (n-- && *ucs2) /* Copy the strings */
*ucs1++ = *ucs2++;
n++;
while (n--) /* Pad with nulls */
*ucs1++ = 0;
return anchor;
}
/*
* UniStrncpy_le: Copy length limited string with pad to little-endian
*/
static inline wchar_t *UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
wchar_t *anchor = ucs1;
while (n-- && *ucs2) /* Copy the strings */
*ucs1++ = __le16_to_cpu(*ucs2++);
n++;
while (n--) /* Pad with nulls */
*ucs1++ = 0;
return anchor;
}
/*
* UniStrstr: Find a string in a string
*
* Returns:
* Address of first match found
* NULL if no matching string is found
*/
static inline wchar_t *UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2)
{
const wchar_t *anchor1 = ucs1;
const wchar_t *anchor2 = ucs2;
while (*ucs1) {
if (*ucs1 == *ucs2) {
/* Partial match found */
ucs1++;
ucs2++;
} else {
if (!*ucs2) /* Match found */
return (wchar_t *)anchor1;
ucs1 = ++anchor1; /* No match */
ucs2 = anchor2;
}
}
if (!*ucs2) /* Both end together */
return (wchar_t *)anchor1; /* Match found */
return NULL; /* No match */
}
#ifndef UNIUPR_NOUPPER
/*
* UniToupper: Convert a unicode character to upper case
*/
static inline wchar_t UniToupper(register wchar_t uc)
{
register const struct UniCaseRange *rp;
if (uc < sizeof(SmbUniUpperTable)) {
/* Latin characters */
return uc + SmbUniUpperTable[uc]; /* Use base tables */
}
rp = SmbUniUpperRange; /* Use range tables */
while (rp->start) {
if (uc < rp->start) /* Before start of range */
return uc; /* Uppercase = input */
if (uc <= rp->end) /* In range */
return uc + rp->table[uc - rp->start];
rp++; /* Try next range */
}
return uc; /* Past last range */
}
/*
* UniStrupr: Upper case a unicode string
*/
static inline __le16 *UniStrupr(register __le16 *upin)
{
register __le16 *up;
up = upin;
while (*up) { /* For all characters */
*up = cpu_to_le16(UniToupper(le16_to_cpu(*up)));
up++;
}
return upin; /* Return input pointer */
}
#endif /* UNIUPR_NOUPPER */
#ifndef UNIUPR_NOLOWER
/*
* UniTolower: Convert a unicode character to lower case
*/
static inline wchar_t UniTolower(register wchar_t uc)
{
register const struct UniCaseRange *rp;
if (uc < sizeof(CifsUniLowerTable)) {
/* Latin characters */
return uc + CifsUniLowerTable[uc]; /* Use base tables */
}
rp = CifsUniLowerRange; /* Use range tables */
while (rp->start) {
if (uc < rp->start) /* Before start of range */
return uc; /* Uppercase = input */
if (uc <= rp->end) /* In range */
return uc + rp->table[uc - rp->start];
rp++; /* Try next range */
}
return uc; /* Past last range */
}
/*
* UniStrlwr: Lower case a unicode string
*/
static inline wchar_t *UniStrlwr(register wchar_t *upin)
{
register wchar_t *up;
up = upin;
while (*up) { /* For all characters */
*up = UniTolower(*up);
up++;
}
return upin; /* Return input pointer */
}
#endif
#endif /* _CIFS_UNICODE_H */