Skip to content

Commit 309c2b7

Browse files
palismfrench
authored andcommitted
cifs: Add support for creating reparse points over SMB1
SMB1 already supports querying reparse points and detecting types of symlink, fifo, socket, block and char. This change implements the missing part - ability to create a new reparse points over SMB1. This includes everything which SMB2+ already supports: - native SMB symlinks and sockets - NFS style of special files (symlinks, fifos, sockets, char/block devs) - WSL style of special files (symlinks, fifos, sockets, char/block devs) Attaching a reparse point to an existing file or directory is done via SMB1 SMB_COM_NT_TRANSACT/NT_TRANSACT_IOCTL/FSCTL_SET_REPARSE_POINT command and implemented in a new cifs_create_reparse_inode() function. This change introduce a new callback ->create_reparse_inode() which creates a new reperse point file or directory and returns inode. For SMB1 it is provided via that new cifs_create_reparse_inode() function. Existing reparse.c code was only slightly updated to call new protocol callback ->create_reparse_inode() instead of hardcoded SMB2+ function. This make the whole reparse.c code to work with every SMB dialect. The original callback ->create_reparse_symlink() is not needed anymore as the implementation of new create_reparse_symlink() function is dialect agnostic too. So the link.c code was updated to call that function directly (and not via callback). Signed-off-by: Pali Rohár <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 75d519b commit 309c2b7

File tree

10 files changed

+188
-43
lines changed

10 files changed

+188
-43
lines changed

fs/smb/client/cifsglob.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -627,12 +627,14 @@ struct smb_version_operations {
627627
bool (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv);
628628
struct reparse_data_buffer * (*get_reparse_point_buffer)(const struct kvec *rsp_iov,
629629
u32 *plen);
630-
int (*create_reparse_symlink)(const unsigned int xid,
631-
struct inode *inode,
632-
struct dentry *dentry,
633-
struct cifs_tcon *tcon,
634-
const char *full_path,
635-
const char *symname);
630+
struct inode * (*create_reparse_inode)(struct cifs_open_info_data *data,
631+
struct super_block *sb,
632+
const unsigned int xid,
633+
struct cifs_tcon *tcon,
634+
const char *full_path,
635+
bool directory,
636+
struct kvec *reparse_iov,
637+
struct kvec *xattr_iov);
636638
};
637639

638640
struct smb_version_values {

fs/smb/client/cifsproto.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,14 @@ extern int cifs_query_reparse_point(const unsigned int xid,
483483
const char *full_path,
484484
u32 *tag, struct kvec *rsp,
485485
int *rsp_buftype);
486+
extern struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
487+
struct super_block *sb,
488+
const unsigned int xid,
489+
struct cifs_tcon *tcon,
490+
const char *full_path,
491+
bool directory,
492+
struct kvec *reparse_iov,
493+
struct kvec *xattr_iov);
486494
extern int CIFSSMB_set_compression(const unsigned int xid,
487495
struct cifs_tcon *tcon, __u16 fid);
488496
extern int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms,

fs/smb/client/cifssmb.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2851,6 +2851,134 @@ int cifs_query_reparse_point(const unsigned int xid,
28512851
return rc;
28522852
}
28532853

2854+
struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
2855+
struct super_block *sb,
2856+
const unsigned int xid,
2857+
struct cifs_tcon *tcon,
2858+
const char *full_path,
2859+
bool directory,
2860+
struct kvec *reparse_iov,
2861+
struct kvec *xattr_iov)
2862+
{
2863+
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
2864+
struct cifs_open_parms oparms;
2865+
TRANSACT_IOCTL_REQ *io_req;
2866+
struct inode *new = NULL;
2867+
struct kvec in_iov[2];
2868+
struct kvec out_iov;
2869+
struct cifs_fid fid;
2870+
int io_req_len;
2871+
int oplock = 0;
2872+
int buf_type = 0;
2873+
int rc;
2874+
2875+
cifs_tcon_dbg(FYI, "%s: path=%s\n", __func__, full_path);
2876+
2877+
/*
2878+
* If server filesystem does not support reparse points then do not
2879+
* attempt to create reparse point. This will prevent creating unusable
2880+
* empty object on the server.
2881+
*/
2882+
if (!(le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS))
2883+
return ERR_PTR(-EOPNOTSUPP);
2884+
2885+
#ifndef CONFIG_CIFS_XATTR
2886+
if (xattr_iov)
2887+
return ERR_PTR(-EOPNOTSUPP);
2888+
#endif
2889+
2890+
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
2891+
FILE_READ_ATTRIBUTES | FILE_WRITE_DATA | FILE_WRITE_EA,
2892+
FILE_CREATE,
2893+
(directory ? CREATE_NOT_FILE : CREATE_NOT_DIR) | OPEN_REPARSE_POINT,
2894+
ACL_NO_MODE);
2895+
oparms.fid = &fid;
2896+
2897+
rc = CIFS_open(xid, &oparms, &oplock, NULL);
2898+
if (rc)
2899+
return ERR_PTR(rc);
2900+
2901+
#ifdef CONFIG_CIFS_XATTR
2902+
if (xattr_iov) {
2903+
struct smb2_file_full_ea_info *ea;
2904+
2905+
ea = &((struct smb2_create_ea_ctx *)xattr_iov->iov_base)->ea;
2906+
while (1) {
2907+
rc = CIFSSMBSetEA(xid,
2908+
tcon,
2909+
full_path,
2910+
&ea->ea_data[0],
2911+
&ea->ea_data[ea->ea_name_length+1],
2912+
le16_to_cpu(ea->ea_value_length),
2913+
cifs_sb->local_nls,
2914+
cifs_sb);
2915+
if (rc)
2916+
goto out_close;
2917+
if (le32_to_cpu(ea->next_entry_offset) == 0)
2918+
break;
2919+
ea = (struct smb2_file_full_ea_info *)((u8 *)ea +
2920+
le32_to_cpu(ea->next_entry_offset));
2921+
}
2922+
}
2923+
#endif
2924+
2925+
rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **)&io_req, NULL);
2926+
if (rc)
2927+
goto out_close;
2928+
2929+
inc_rfc1001_len(io_req, sizeof(io_req->Pad));
2930+
2931+
io_req_len = be32_to_cpu(io_req->hdr.smb_buf_length) + sizeof(io_req->hdr.smb_buf_length);
2932+
2933+
/* NT IOCTL response contains one-word long output setup buffer with size of output data. */
2934+
io_req->MaxSetupCount = 1;
2935+
/* NT IOCTL response does not contain output parameters. */
2936+
io_req->MaxParameterCount = cpu_to_le32(0);
2937+
/* FSCTL_SET_REPARSE_POINT response contains empty output data. */
2938+
io_req->MaxDataCount = cpu_to_le32(0);
2939+
2940+
io_req->TotalParameterCount = cpu_to_le32(0);
2941+
io_req->TotalDataCount = cpu_to_le32(reparse_iov->iov_len);
2942+
io_req->ParameterCount = io_req->TotalParameterCount;
2943+
io_req->ParameterOffset = cpu_to_le32(0);
2944+
io_req->DataCount = io_req->TotalDataCount;
2945+
io_req->DataOffset = cpu_to_le32(offsetof(typeof(*io_req), Data) -
2946+
sizeof(io_req->hdr.smb_buf_length));
2947+
io_req->SetupCount = 4;
2948+
io_req->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
2949+
io_req->FunctionCode = cpu_to_le32(FSCTL_SET_REPARSE_POINT);
2950+
io_req->Fid = fid.netfid;
2951+
io_req->IsFsctl = 1;
2952+
io_req->IsRootFlag = 0;
2953+
io_req->ByteCount = cpu_to_le16(le32_to_cpu(io_req->DataCount) + sizeof(io_req->Pad));
2954+
2955+
inc_rfc1001_len(io_req, reparse_iov->iov_len);
2956+
2957+
in_iov[0].iov_base = (char *)io_req;
2958+
in_iov[0].iov_len = io_req_len;
2959+
in_iov[1] = *reparse_iov;
2960+
rc = SendReceive2(xid, tcon->ses, in_iov, ARRAY_SIZE(in_iov), &buf_type,
2961+
CIFS_NO_RSP_BUF, &out_iov);
2962+
2963+
cifs_buf_release(io_req);
2964+
2965+
if (!rc)
2966+
rc = cifs_get_inode_info(&new, full_path, data, sb, xid, NULL);
2967+
2968+
out_close:
2969+
CIFSSMBClose(xid, tcon, fid.netfid);
2970+
2971+
/*
2972+
* If CREATE was successful but FSCTL_SET_REPARSE_POINT failed then
2973+
* remove the intermediate object created by CREATE. Otherwise
2974+
* empty object stay on the server when reparse call failed.
2975+
*/
2976+
if (rc)
2977+
CIFSSMBDelFile(xid, tcon, full_path, cifs_sb, NULL);
2978+
2979+
return rc ? ERR_PTR(rc) : new;
2980+
}
2981+
28542982
int
28552983
CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
28562984
__u16 fid)

fs/smb/client/link.c

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "smb2proto.h"
2020
#include "cifs_ioctl.h"
2121
#include "fs_context.h"
22+
#include "reparse.h"
2223

2324
/*
2425
* M-F Symlink Functions - Begin
@@ -570,7 +571,6 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
570571
int rc = -EOPNOTSUPP;
571572
unsigned int xid;
572573
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
573-
struct TCP_Server_Info *server;
574574
struct tcon_link *tlink;
575575
struct cifs_tcon *pTcon;
576576
const char *full_path;
@@ -593,7 +593,6 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
593593
goto symlink_exit;
594594
}
595595
pTcon = tlink_tcon(tlink);
596-
server = cifs_pick_channel(pTcon->ses);
597596

598597
full_path = build_path_from_dentry(direntry, page);
599598
if (IS_ERR(full_path)) {
@@ -643,13 +642,9 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
643642
case CIFS_SYMLINK_TYPE_NATIVE:
644643
case CIFS_SYMLINK_TYPE_NFS:
645644
case CIFS_SYMLINK_TYPE_WSL:
646-
if (server->ops->create_reparse_symlink &&
647-
(le32_to_cpu(pTcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS)) {
648-
rc = server->ops->create_reparse_symlink(xid, inode,
649-
direntry,
650-
pTcon,
651-
full_path,
652-
symname);
645+
if (le32_to_cpu(pTcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS) {
646+
rc = create_reparse_symlink(xid, inode, direntry, pTcon,
647+
full_path, symname);
653648
goto symlink_exit;
654649
}
655650
break;

fs/smb/client/reparse.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ static int detect_directory_symlink_target(struct cifs_sb_info *cifs_sb,
3434
const char *symname,
3535
bool *directory);
3636

37-
int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
37+
int create_reparse_symlink(const unsigned int xid, struct inode *inode,
3838
struct dentry *dentry, struct cifs_tcon *tcon,
3939
const char *full_path, const char *symname)
4040
{
@@ -227,7 +227,8 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode,
227227

228228
iov.iov_base = buf;
229229
iov.iov_len = len;
230-
new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
230+
new = tcon->ses->server->ops->create_reparse_inode(
231+
&data, inode->i_sb, xid,
231232
tcon, full_path, directory,
232233
&iov, NULL);
233234
if (!IS_ERR(new))
@@ -399,7 +400,8 @@ static int create_native_socket(const unsigned int xid, struct inode *inode,
399400
struct inode *new;
400401
int rc = 0;
401402

402-
new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
403+
new = tcon->ses->server->ops->create_reparse_inode(
404+
&data, inode->i_sb, xid,
403405
tcon, full_path, false, &iov, NULL);
404406
if (!IS_ERR(new))
405407
d_instantiate(dentry, new);
@@ -492,7 +494,8 @@ static int mknod_nfs(unsigned int xid, struct inode *inode,
492494
.symlink_target = kstrdup(symname, GFP_KERNEL),
493495
};
494496

495-
new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
497+
new = tcon->ses->server->ops->create_reparse_inode(
498+
&data, inode->i_sb, xid,
496499
tcon, full_path, false, &iov, NULL);
497500
if (!IS_ERR(new))
498501
d_instantiate(dentry, new);
@@ -685,7 +688,8 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
685688
memcpy(data.wsl.eas, &cc->ea, len);
686689
data.wsl.eas_len = len;
687690

688-
new = smb2_get_reparse_inode(&data, inode->i_sb,
691+
new = tcon->ses->server->ops->create_reparse_inode(
692+
&data, inode->i_sb,
689693
xid, tcon, full_path, false,
690694
&reparse_iov, &xattr_iov);
691695
if (!IS_ERR(new))
@@ -698,7 +702,7 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
698702
return rc;
699703
}
700704

701-
int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
705+
int mknod_reparse(unsigned int xid, struct inode *inode,
702706
struct dentry *dentry, struct cifs_tcon *tcon,
703707
const char *full_path, umode_t mode, dev_t dev)
704708
{

fs/smb/client/reparse.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,10 @@ static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
129129
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
130130
struct cifs_fattr *fattr,
131131
struct cifs_open_info_data *data);
132-
int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
132+
int create_reparse_symlink(const unsigned int xid, struct inode *inode,
133133
struct dentry *dentry, struct cifs_tcon *tcon,
134134
const char *full_path, const char *symname);
135-
int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
135+
int mknod_reparse(unsigned int xid, struct inode *inode,
136136
struct dentry *dentry, struct cifs_tcon *tcon,
137137
const char *full_path, umode_t mode, dev_t dev);
138138
struct reparse_data_buffer *smb2_get_reparse_point_buffer(const struct kvec *rsp_iov, u32 *len);

fs/smb/client/smb1ops.c

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "fs_context.h"
1717
#include "nterr.h"
1818
#include "smberr.h"
19+
#include "reparse.h"
1920

2021
/*
2122
* An NT cancel request header looks just like the original request except:
@@ -1263,17 +1264,26 @@ cifs_make_node(unsigned int xid, struct inode *inode,
12631264
if (rc == 0)
12641265
d_instantiate(dentry, newinode);
12651266
return rc;
1267+
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
1268+
/*
1269+
* Check if mounted with mount parm 'sfu' mount parm.
1270+
* SFU emulation should work with all servers
1271+
* and was used by default in earlier versions of Windows.
1272+
*/
1273+
return cifs_sfu_make_node(xid, inode, dentry, tcon,
1274+
full_path, mode, dev);
1275+
} else if (le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS) {
1276+
/*
1277+
* mknod via reparse points requires server support for
1278+
* storing reparse points, which is available since
1279+
* Windows 2000, but was not widely used until release
1280+
* of Windows Server 2012 by the Windows NFS server.
1281+
*/
1282+
return mknod_reparse(xid, inode, dentry, tcon,
1283+
full_path, mode, dev);
1284+
} else {
1285+
return -EOPNOTSUPP;
12661286
}
1267-
/*
1268-
* Check if mounted with mount parm 'sfu' mount parm.
1269-
* SFU emulation should work with all servers, but only
1270-
* supports block and char device, socket & fifo,
1271-
* and was used by default in earlier versions of Windows
1272-
*/
1273-
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
1274-
return -EPERM;
1275-
return cifs_sfu_make_node(xid, inode, dentry, tcon,
1276-
full_path, mode, dev);
12771287
}
12781288

12791289
static bool
@@ -1370,6 +1380,7 @@ struct smb_version_operations smb1_operations = {
13701380
.create_hardlink = CIFSCreateHardLink,
13711381
.query_symlink = cifs_query_symlink,
13721382
.get_reparse_point_buffer = cifs_get_reparse_point_buffer,
1383+
.create_reparse_inode = cifs_create_reparse_inode,
13731384
.open = cifs_open_file,
13741385
.set_fid = cifs_set_fid,
13751386
.close = cifs_close_file,

fs/smb/client/smb2inode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1321,7 +1321,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
13211321
return rc;
13221322
}
13231323

1324-
struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
1324+
struct inode *smb2_create_reparse_inode(struct cifs_open_info_data *data,
13251325
struct super_block *sb,
13261326
const unsigned int xid,
13271327
struct cifs_tcon *tcon,

fs/smb/client/smb2ops.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5262,7 +5262,7 @@ static int smb2_make_node(unsigned int xid, struct inode *inode,
52625262
full_path, mode, dev);
52635263
} else if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS)
52645264
|| (tcon->posix_extensions)) {
5265-
rc = smb2_mknod_reparse(xid, inode, dentry, tcon,
5265+
rc = mknod_reparse(xid, inode, dentry, tcon,
52665266
full_path, mode, dev);
52675267
}
52685268
return rc;
@@ -5321,7 +5321,7 @@ struct smb_version_operations smb20_operations = {
53215321
.get_reparse_point_buffer = smb2_get_reparse_point_buffer,
53225322
.query_mf_symlink = smb3_query_mf_symlink,
53235323
.create_mf_symlink = smb3_create_mf_symlink,
5324-
.create_reparse_symlink = smb2_create_reparse_symlink,
5324+
.create_reparse_inode = smb2_create_reparse_inode,
53255325
.open = smb2_open_file,
53265326
.set_fid = smb2_set_fid,
53275327
.close = smb2_close_file,
@@ -5424,7 +5424,7 @@ struct smb_version_operations smb21_operations = {
54245424
.get_reparse_point_buffer = smb2_get_reparse_point_buffer,
54255425
.query_mf_symlink = smb3_query_mf_symlink,
54265426
.create_mf_symlink = smb3_create_mf_symlink,
5427-
.create_reparse_symlink = smb2_create_reparse_symlink,
5427+
.create_reparse_inode = smb2_create_reparse_inode,
54285428
.open = smb2_open_file,
54295429
.set_fid = smb2_set_fid,
54305430
.close = smb2_close_file,
@@ -5531,7 +5531,7 @@ struct smb_version_operations smb30_operations = {
55315531
.get_reparse_point_buffer = smb2_get_reparse_point_buffer,
55325532
.query_mf_symlink = smb3_query_mf_symlink,
55335533
.create_mf_symlink = smb3_create_mf_symlink,
5534-
.create_reparse_symlink = smb2_create_reparse_symlink,
5534+
.create_reparse_inode = smb2_create_reparse_inode,
55355535
.open = smb2_open_file,
55365536
.set_fid = smb2_set_fid,
55375537
.close = smb2_close_file,
@@ -5647,7 +5647,7 @@ struct smb_version_operations smb311_operations = {
56475647
.get_reparse_point_buffer = smb2_get_reparse_point_buffer,
56485648
.query_mf_symlink = smb3_query_mf_symlink,
56495649
.create_mf_symlink = smb3_create_mf_symlink,
5650-
.create_reparse_symlink = smb2_create_reparse_symlink,
5650+
.create_reparse_inode = smb2_create_reparse_inode,
56515651
.open = smb2_open_file,
56525652
.set_fid = smb2_set_fid,
56535653
.close = smb2_close_file,

0 commit comments

Comments
 (0)