Skip to content

Commit 102466f

Browse files
Paulo Alcantarasmfrench
authored andcommitted
smb: client: allow creating special files via reparse points
Add support for creating special files (e.g. char/block devices, sockets, fifos) via NFS reparse points on SMB2+, which are fully supported by most SMB servers and documented in MS-FSCC. smb2_get_reparse_inode() creates the file with a corresponding reparse point buffer set in @Iov through a single roundtrip to the server. Reported-by: kernel test robot <lkp@intel.com> Closes: https://lore.kernel.org/oe-kbuild-all/202311260746.HOJ039BV-lkp@intel.com/ Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 3322960 commit 102466f

10 files changed

Lines changed: 256 additions & 60 deletions

File tree

fs/smb/client/cifsproto.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,12 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
211211
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
212212
struct cifs_fattr *fattr,
213213
struct cifs_open_info_data *data);
214-
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
215-
struct super_block *sb, unsigned int xid);
214+
215+
extern int smb311_posix_get_inode_info(struct inode **inode,
216+
const char *full_path,
217+
struct cifs_open_info_data *data,
218+
struct super_block *sb,
219+
const unsigned int xid);
216220
extern int cifs_get_inode_info_unix(struct inode **pinode,
217221
const unsigned char *search_path,
218222
struct super_block *sb, unsigned int xid);

fs/smb/client/dir.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -680,9 +680,10 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
680680
full_path, d_inode(direntry));
681681

682682
again:
683-
if (pTcon->posix_extensions)
684-
rc = smb311_posix_get_inode_info(&newInode, full_path, parent_dir_inode->i_sb, xid);
685-
else if (pTcon->unix_ext) {
683+
if (pTcon->posix_extensions) {
684+
rc = smb311_posix_get_inode_info(&newInode, full_path, NULL,
685+
parent_dir_inode->i_sb, xid);
686+
} else if (pTcon->unix_ext) {
686687
rc = cifs_get_inode_info_unix(&newInode, full_path,
687688
parent_dir_inode->i_sb, xid);
688689
} else {

fs/smb/client/file.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,14 +1020,16 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
10201020
if (!is_interrupt_error(rc))
10211021
mapping_set_error(inode->i_mapping, rc);
10221022

1023-
if (tcon->posix_extensions)
1024-
rc = smb311_posix_get_inode_info(&inode, full_path, inode->i_sb, xid);
1025-
else if (tcon->unix_ext)
1023+
if (tcon->posix_extensions) {
1024+
rc = smb311_posix_get_inode_info(&inode, full_path,
1025+
NULL, inode->i_sb, xid);
1026+
} else if (tcon->unix_ext) {
10261027
rc = cifs_get_inode_info_unix(&inode, full_path,
10271028
inode->i_sb, xid);
1028-
else
1029+
} else {
10291030
rc = cifs_get_inode_info(&inode, full_path, NULL,
10301031
inode->i_sb, xid, NULL);
1032+
}
10311033
}
10321034
/*
10331035
* Else we are writing out data to server already and could deadlock if

fs/smb/client/inode.c

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,9 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
10611061
const unsigned int xid,
10621062
struct cifs_tcon *tcon,
10631063
const char *full_path,
1064-
struct cifs_fattr *fattr)
1064+
struct cifs_fattr *fattr,
1065+
struct cifs_sid *owner,
1066+
struct cifs_sid *group)
10651067
{
10661068
struct TCP_Server_Info *server = tcon->ses->server;
10671069
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
@@ -1092,7 +1094,8 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
10921094
rc = 0;
10931095
goto out;
10941096
default:
1095-
if (data->symlink_target) {
1097+
/* Check for cached reparse point data */
1098+
if (data->symlink_target || data->reparse.buf) {
10961099
rc = 0;
10971100
} else if (server->ops->parse_reparse_point) {
10981101
rc = server->ops->parse_reparse_point(cifs_sb,
@@ -1101,7 +1104,10 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
11011104
break;
11021105
}
11031106

1104-
cifs_open_info_to_fattr(fattr, data, sb);
1107+
if (tcon->posix_extensions)
1108+
smb311_posix_info_to_fattr(fattr, data, owner, group, sb);
1109+
else
1110+
cifs_open_info_to_fattr(fattr, data, sb);
11051111
out:
11061112
free_rsp_buf(rsp_buftype, rsp_iov.iov_base);
11071113
return rc;
@@ -1152,7 +1158,8 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
11521158
*/
11531159
if (cifs_open_data_reparse(data)) {
11541160
rc = reparse_info_to_fattr(data, sb, xid, tcon,
1155-
full_path, fattr);
1161+
full_path, fattr,
1162+
NULL, NULL);
11561163
} else {
11571164
cifs_open_info_to_fattr(fattr, data, sb);
11581165
}
@@ -1290,39 +1297,49 @@ int cifs_get_inode_info(struct inode **inode,
12901297
return rc;
12911298
}
12921299

1293-
static int smb311_posix_get_fattr(struct cifs_fattr *fattr,
1300+
static int smb311_posix_get_fattr(struct cifs_open_info_data *data,
1301+
struct cifs_fattr *fattr,
12941302
const char *full_path,
12951303
struct super_block *sb,
12961304
const unsigned int xid)
12971305
{
1298-
struct cifs_open_info_data data = {};
1306+
struct cifs_open_info_data tmp_data = {};
12991307
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
13001308
struct cifs_tcon *tcon;
13011309
struct tcon_link *tlink;
13021310
struct cifs_sid owner, group;
13031311
int tmprc;
1304-
int rc;
1312+
int rc = 0;
13051313

13061314
tlink = cifs_sb_tlink(cifs_sb);
13071315
if (IS_ERR(tlink))
13081316
return PTR_ERR(tlink);
13091317
tcon = tlink_tcon(tlink);
13101318

13111319
/*
1312-
* 1. Fetch file metadata
1320+
* 1. Fetch file metadata if not provided (data)
13131321
*/
1314-
1315-
rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
1316-
full_path, &data,
1317-
&owner, &group);
1322+
if (!data) {
1323+
rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
1324+
full_path, &tmp_data,
1325+
&owner, &group);
1326+
data = &tmp_data;
1327+
}
13181328

13191329
/*
13201330
* 2. Convert it to internal cifs metadata (fattr)
13211331
*/
13221332

13231333
switch (rc) {
13241334
case 0:
1325-
smb311_posix_info_to_fattr(fattr, &data, &owner, &group, sb);
1335+
if (cifs_open_data_reparse(data)) {
1336+
rc = reparse_info_to_fattr(data, sb, xid, tcon,
1337+
full_path, fattr,
1338+
&owner, &group);
1339+
} else {
1340+
smb311_posix_info_to_fattr(fattr, data,
1341+
&owner, &group, sb);
1342+
}
13261343
break;
13271344
case -EREMOTE:
13281345
/* DFS link, no metadata available on this server */
@@ -1353,12 +1370,15 @@ static int smb311_posix_get_fattr(struct cifs_fattr *fattr,
13531370

13541371
out:
13551372
cifs_put_tlink(tlink);
1356-
cifs_free_open_info(&data);
1373+
cifs_free_open_info(data);
13571374
return rc;
13581375
}
13591376

1360-
int smb311_posix_get_inode_info(struct inode **inode, const char *full_path,
1361-
struct super_block *sb, const unsigned int xid)
1377+
int smb311_posix_get_inode_info(struct inode **inode,
1378+
const char *full_path,
1379+
struct cifs_open_info_data *data,
1380+
struct super_block *sb,
1381+
const unsigned int xid)
13621382
{
13631383
struct cifs_fattr fattr = {};
13641384
int rc;
@@ -1368,7 +1388,7 @@ int smb311_posix_get_inode_info(struct inode **inode, const char *full_path,
13681388
return 0;
13691389
}
13701390

1371-
rc = smb311_posix_get_fattr(&fattr, full_path, sb, xid);
1391+
rc = smb311_posix_get_fattr(data, &fattr, full_path, sb, xid);
13721392
if (rc)
13731393
goto out;
13741394

@@ -1516,7 +1536,7 @@ struct inode *cifs_root_iget(struct super_block *sb)
15161536

15171537
convert_delimiter(path, CIFS_DIR_SEP(cifs_sb));
15181538
if (tcon->posix_extensions)
1519-
rc = smb311_posix_get_fattr(&fattr, path, sb, xid);
1539+
rc = smb311_posix_get_fattr(NULL, &fattr, path, sb, xid);
15201540
else
15211541
rc = cifs_get_fattr(NULL, sb, xid, NULL, &fattr, &inode, path);
15221542

@@ -1889,16 +1909,18 @@ cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
18891909
int rc = 0;
18901910
struct inode *inode = NULL;
18911911

1892-
if (tcon->posix_extensions)
1893-
rc = smb311_posix_get_inode_info(&inode, full_path, parent->i_sb, xid);
1912+
if (tcon->posix_extensions) {
1913+
rc = smb311_posix_get_inode_info(&inode, full_path,
1914+
NULL, parent->i_sb, xid);
18941915
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
1895-
else if (tcon->unix_ext)
1916+
} else if (tcon->unix_ext) {
18961917
rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb,
18971918
xid);
18981919
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
1899-
else
1920+
} else {
19001921
rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb,
19011922
xid, NULL);
1923+
}
19021924

19031925
if (rc)
19041926
return rc;
@@ -2579,13 +2601,15 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry)
25792601
dentry, cifs_get_time(dentry), jiffies);
25802602

25812603
again:
2582-
if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions)
2583-
rc = smb311_posix_get_inode_info(&inode, full_path, sb, xid);
2584-
else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
2604+
if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions) {
2605+
rc = smb311_posix_get_inode_info(&inode, full_path,
2606+
NULL, sb, xid);
2607+
} else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) {
25852608
rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
2586-
else
2609+
} else {
25872610
rc = cifs_get_inode_info(&inode, full_path, NULL, sb,
25882611
xid, NULL);
2612+
}
25892613
if (rc == -EAGAIN && count++ < 10)
25902614
goto again;
25912615
out:

fs/smb/client/link.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -614,14 +614,16 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
614614
cifs_sb_target->local_nls); */
615615

616616
if (rc == 0) {
617-
if (pTcon->posix_extensions)
618-
rc = smb311_posix_get_inode_info(&newinode, full_path, inode->i_sb, xid);
619-
else if (pTcon->unix_ext)
617+
if (pTcon->posix_extensions) {
618+
rc = smb311_posix_get_inode_info(&newinode, full_path,
619+
NULL, inode->i_sb, xid);
620+
} else if (pTcon->unix_ext) {
620621
rc = cifs_get_inode_info_unix(&newinode, full_path,
621622
inode->i_sb, xid);
622-
else
623+
} else {
623624
rc = cifs_get_inode_info(&newinode, full_path, NULL,
624625
inode->i_sb, xid, NULL);
626+
}
625627

626628
if (rc != 0) {
627629
cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n",

fs/smb/client/smb2glob.h

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,20 @@
2323
* Identifiers for functions that use the open, operation, close pattern
2424
* in smb2inode.c:smb2_compound_op()
2525
*/
26-
#define SMB2_OP_SET_DELETE 1
27-
#define SMB2_OP_SET_INFO 2
28-
#define SMB2_OP_QUERY_INFO 3
29-
#define SMB2_OP_QUERY_DIR 4
30-
#define SMB2_OP_MKDIR 5
31-
#define SMB2_OP_RENAME 6
32-
#define SMB2_OP_DELETE 7
33-
#define SMB2_OP_HARDLINK 8
34-
#define SMB2_OP_SET_EOF 9
35-
#define SMB2_OP_RMDIR 10
36-
#define SMB2_OP_POSIX_QUERY_INFO 11
26+
enum smb2_compound_ops {
27+
SMB2_OP_SET_DELETE = 1,
28+
SMB2_OP_SET_INFO,
29+
SMB2_OP_QUERY_INFO,
30+
SMB2_OP_QUERY_DIR,
31+
SMB2_OP_MKDIR,
32+
SMB2_OP_RENAME,
33+
SMB2_OP_DELETE,
34+
SMB2_OP_HARDLINK,
35+
SMB2_OP_SET_EOF,
36+
SMB2_OP_RMDIR,
37+
SMB2_OP_POSIX_QUERY_INFO,
38+
SMB2_OP_SET_REPARSE
39+
};
3740

3841
/* Used when constructing chained read requests. */
3942
#define CHAINED_REQUEST 1

fs/smb/client/smb2inode.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,22 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
347347
smb2_set_related(&rqst[num_rqst++]);
348348
trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);
349349
break;
350+
case SMB2_OP_SET_REPARSE:
351+
rqst[num_rqst].rq_iov = vars->io_iov;
352+
rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
353+
354+
rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
355+
COMPOUND_FID, COMPOUND_FID,
356+
FSCTL_SET_REPARSE_POINT,
357+
in_iov[i].iov_base,
358+
in_iov[i].iov_len, 0);
359+
if (rc)
360+
goto finished;
361+
smb2_set_next_command(tcon, &rqst[num_rqst]);
362+
smb2_set_related(&rqst[num_rqst++]);
363+
trace_smb3_set_reparse_compound_enter(xid, ses->Suid,
364+
tcon->tid, full_path);
365+
break;
350366
default:
351367
cifs_dbg(VFS, "Invalid command\n");
352368
rc = -EINVAL;
@@ -503,6 +519,16 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
503519
tcon->tid);
504520
SMB2_set_info_free(&rqst[num_rqst++]);
505521
break;
522+
case SMB2_OP_SET_REPARSE:
523+
if (rc) {
524+
trace_smb3_set_reparse_compound_err(xid, ses->Suid,
525+
tcon->tid, rc);
526+
} else {
527+
trace_smb3_set_reparse_compound_done(xid, ses->Suid,
528+
tcon->tid);
529+
}
530+
SMB2_ioctl_free(&rqst[num_rqst++]);
531+
break;
506532
}
507533
}
508534
SMB2_close_free(&rqst[num_rqst]);
@@ -887,3 +913,52 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
887913
cifs_put_tlink(tlink);
888914
return rc;
889915
}
916+
917+
struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
918+
struct super_block *sb,
919+
const unsigned int xid,
920+
struct cifs_tcon *tcon,
921+
const char *full_path,
922+
struct kvec *iov)
923+
{
924+
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
925+
struct cifsFileInfo *cfile;
926+
struct inode *new = NULL;
927+
struct kvec in_iov[2];
928+
int cmds[2];
929+
int da, co, cd;
930+
int rc;
931+
932+
da = SYNCHRONIZE | DELETE |
933+
FILE_READ_ATTRIBUTES |
934+
FILE_WRITE_ATTRIBUTES;
935+
co = CREATE_NOT_DIR | OPEN_REPARSE_POINT;
936+
cd = FILE_CREATE;
937+
cmds[0] = SMB2_OP_SET_REPARSE;
938+
in_iov[0] = *iov;
939+
in_iov[1].iov_base = data;
940+
in_iov[1].iov_len = sizeof(*data);
941+
942+
if (tcon->posix_extensions) {
943+
cmds[1] = SMB2_OP_POSIX_QUERY_INFO;
944+
cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
945+
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
946+
da, cd, co, ACL_NO_MODE, in_iov,
947+
cmds, 2, cfile, NULL, NULL, NULL, NULL);
948+
if (!rc) {
949+
rc = smb311_posix_get_inode_info(&new, full_path,
950+
data, sb, xid);
951+
}
952+
} else {
953+
cmds[1] = SMB2_OP_QUERY_INFO;
954+
cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
955+
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
956+
da, cd, co, ACL_NO_MODE, in_iov,
957+
cmds, 2, cfile, NULL, NULL, NULL, NULL);
958+
if (!rc) {
959+
rc = cifs_get_inode_info(&new, full_path,
960+
data, sb, xid, NULL);
961+
}
962+
}
963+
return rc ? ERR_PTR(rc) : new;
964+
}

0 commit comments

Comments
 (0)