]>
Commit | Line | Data |
---|---|---|
929be906 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
be4cb9e3 | 2 | /* |
be4cb9e3 PS |
3 | * |
4 | * Copyright (C) International Business Machines Corp., 2002, 2011 | |
5 | * Etersoft, 2012 | |
6 | * Author(s): Pavel Shilovsky (pshilovsky@samba.org), | |
7 | * Steve French (sfrench@us.ibm.com) | |
8 | * | |
be4cb9e3 PS |
9 | */ |
10 | #include <linux/fs.h> | |
11 | #include <linux/stat.h> | |
12 | #include <linux/slab.h> | |
13 | #include <linux/pagemap.h> | |
14 | #include <asm/div64.h> | |
15 | #include "cifsfs.h" | |
16 | #include "cifspdu.h" | |
17 | #include "cifsglob.h" | |
18 | #include "cifsproto.h" | |
19 | #include "cifs_debug.h" | |
20 | #include "cifs_fs_sb.h" | |
21 | #include "cifs_unicode.h" | |
22 | #include "fscache.h" | |
23 | #include "smb2glob.h" | |
24 | #include "smb2pdu.h" | |
25 | #include "smb2proto.h" | |
05b98fd2 | 26 | #include "cached_dir.h" |
76894f3e | 27 | #include "smb2status.h" |
be4cb9e3 | 28 | |
67ec9949 PA |
29 | static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov) |
30 | { | |
31 | struct reparse_data_buffer *buf; | |
32 | struct smb2_ioctl_rsp *io = iov->iov_base; | |
33 | u32 off, count, len; | |
34 | ||
35 | count = le32_to_cpu(io->OutputCount); | |
36 | off = le32_to_cpu(io->OutputOffset); | |
37 | if (check_add_overflow(off, count, &len) || len > iov->iov_len) | |
38 | return ERR_PTR(-EIO); | |
39 | ||
40 | buf = (struct reparse_data_buffer *)((u8 *)io + off); | |
41 | len = sizeof(*buf); | |
42 | if (count < len || count < le16_to_cpu(buf->ReparseDataLength) + len) | |
43 | return ERR_PTR(-EIO); | |
44 | return buf; | |
45 | } | |
46 | ||
5408990a PA |
47 | static inline __u32 file_create_options(struct dentry *dentry) |
48 | { | |
49 | struct cifsInodeInfo *ci; | |
50 | ||
51 | if (dentry) { | |
52 | ci = CIFS_I(d_inode(dentry)); | |
53 | if (ci->cifsAttrs & ATTR_REPARSE) | |
54 | return OPEN_REPARSE_POINT; | |
55 | } | |
56 | return 0; | |
57 | } | |
58 | ||
858e7487 PA |
59 | /* Parse owner and group from SMB3.1.1 POSIX query info */ |
60 | static int parse_posix_sids(struct cifs_open_info_data *data, | |
61 | struct kvec *rsp_iov) | |
62 | { | |
63 | struct smb2_query_info_rsp *qi = rsp_iov->iov_base; | |
64 | unsigned int out_len = le32_to_cpu(qi->OutputBufferLength); | |
65 | unsigned int qi_len = sizeof(data->posix_fi); | |
66 | int owner_len, group_len; | |
67 | u8 *sidsbuf, *sidsbuf_end; | |
68 | ||
69 | if (out_len <= qi_len) | |
70 | return -EINVAL; | |
71 | ||
72 | sidsbuf = (u8 *)qi + le16_to_cpu(qi->OutputBufferOffset) + qi_len; | |
73 | sidsbuf_end = sidsbuf + out_len - qi_len; | |
74 | ||
75 | owner_len = posix_info_sid_size(sidsbuf, sidsbuf_end); | |
76 | if (owner_len == -1) | |
77 | return -EINVAL; | |
78 | ||
79 | memcpy(&data->posix_owner, sidsbuf, owner_len); | |
80 | group_len = posix_info_sid_size(sidsbuf + owner_len, sidsbuf_end); | |
81 | if (group_len == -1) | |
82 | return -EINVAL; | |
83 | ||
84 | memcpy(&data->posix_group, sidsbuf + owner_len, group_len); | |
85 | return 0; | |
86 | } | |
87 | ||
b6f2a0f8 SP |
88 | /* |
89 | * note: If cfile is passed, the reference to it is dropped here. | |
90 | * So make sure that you do not reuse cfile after return from this func. | |
76894f3e | 91 | * |
c5f44a3d PA |
92 | * If passing @out_iov and @out_buftype, ensure to make them both large enough |
93 | * (>= 3) to hold all compounded responses. Caller is also responsible for | |
94 | * freeing them up with free_rsp_buf(). | |
b6f2a0f8 | 95 | */ |
76894f3e PA |
96 | static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, |
97 | struct cifs_sb_info *cifs_sb, const char *full_path, | |
3322960c PA |
98 | __u32 desired_access, __u32 create_disposition, |
99 | __u32 create_options, umode_t mode, struct kvec *in_iov, | |
100 | int *cmds, int num_cmds, struct cifsFileInfo *cfile, | |
c5f44a3d | 101 | struct kvec *out_iov, int *out_buftype) |
c5a5f38f | 102 | { |
67ec9949 PA |
103 | |
104 | struct reparse_data_buffer *rbuf; | |
f4e5ceb6 | 105 | struct smb2_compound_vars *vars = NULL; |
67ec9949 | 106 | struct kvec *rsp_iov, *iov; |
a7d5c294 | 107 | struct smb_rqst *rqst; |
c5a5f38f RS |
108 | int rc; |
109 | __le16 *utf16_path = NULL; | |
110 | __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; | |
c5a5f38f RS |
111 | struct cifs_fid fid; |
112 | struct cifs_ses *ses = tcon->ses; | |
352d96f3 | 113 | struct TCP_Server_Info *server; |
3322960c PA |
114 | int num_rqst = 0, i; |
115 | int resp_buftype[MAX_COMPOUND]; | |
c2e0fe3f | 116 | struct smb2_query_info_rsp *qi_rsp = NULL; |
76894f3e | 117 | struct cifs_open_info_data *idata; |
c5a5f38f | 118 | int flags = 0; |
c2e0fe3f | 119 | __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0}; |
bb435512 RS |
120 | unsigned int size[2]; |
121 | void *data[2]; | |
bb435512 | 122 | int len; |
c5a5f38f | 123 | |
a7d5c294 RS |
124 | vars = kzalloc(sizeof(*vars), GFP_ATOMIC); |
125 | if (vars == NULL) | |
126 | return -ENOMEM; | |
127 | rqst = &vars->rqst[0]; | |
128 | rsp_iov = &vars->rsp_iov[0]; | |
129 | ||
352d96f3 AA |
130 | server = cifs_pick_channel(ses); |
131 | ||
c5a5f38f RS |
132 | if (smb3_encryption_required(tcon)) |
133 | flags |= CIFS_TRANSFORM_REQ; | |
134 | ||
3322960c PA |
135 | for (i = 0; i < ARRAY_SIZE(resp_buftype); i++) |
136 | resp_buftype[i] = CIFS_NO_BUFFER; | |
c5a5f38f | 137 | |
8de9e86c RS |
138 | /* We already have a handle so we can skip the open */ |
139 | if (cfile) | |
140 | goto after_open; | |
141 | ||
c5a5f38f RS |
142 | /* Open */ |
143 | utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); | |
8de9e86c RS |
144 | if (!utf16_path) { |
145 | rc = -ENOMEM; | |
146 | goto finished; | |
147 | } | |
c5a5f38f | 148 | |
de036dca VL |
149 | vars->oparms = (struct cifs_open_parms) { |
150 | .tcon = tcon, | |
fddc6ccc | 151 | .path = full_path, |
de036dca VL |
152 | .desired_access = desired_access, |
153 | .disposition = create_disposition, | |
154 | .create_options = cifs_create_options(cifs_sb, create_options), | |
155 | .fid = &fid, | |
156 | .mode = mode, | |
157 | .cifs_sb = cifs_sb, | |
158 | }; | |
a7d5c294 RS |
159 | |
160 | rqst[num_rqst].rq_iov = &vars->open_iov[0]; | |
c5a5f38f | 161 | rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE; |
352d96f3 AA |
162 | rc = SMB2_open_init(tcon, server, |
163 | &rqst[num_rqst], &oplock, &vars->oparms, | |
c5a5f38f RS |
164 | utf16_path); |
165 | kfree(utf16_path); | |
166 | if (rc) | |
167 | goto finished; | |
168 | ||
8de9e86c RS |
169 | smb2_set_next_command(tcon, &rqst[num_rqst]); |
170 | after_open: | |
171 | num_rqst++; | |
172 | rc = 0; | |
c5a5f38f | 173 | |
3322960c PA |
174 | for (i = 0; i < num_cmds; i++) { |
175 | /* Operation */ | |
176 | switch (cmds[i]) { | |
177 | case SMB2_OP_QUERY_INFO: | |
178 | rqst[num_rqst].rq_iov = &vars->qi_iov; | |
179 | rqst[num_rqst].rq_nvec = 1; | |
180 | ||
181 | if (cfile) { | |
182 | rc = SMB2_query_info_init(tcon, server, | |
183 | &rqst[num_rqst], | |
184 | cfile->fid.persistent_fid, | |
185 | cfile->fid.volatile_fid, | |
186 | FILE_ALL_INFORMATION, | |
187 | SMB2_O_INFO_FILE, 0, | |
188 | sizeof(struct smb2_file_all_info) + | |
189 | PATH_MAX * 2, 0, NULL); | |
190 | } else { | |
191 | rc = SMB2_query_info_init(tcon, server, | |
192 | &rqst[num_rqst], | |
193 | COMPOUND_FID, | |
194 | COMPOUND_FID, | |
195 | FILE_ALL_INFORMATION, | |
196 | SMB2_O_INFO_FILE, 0, | |
197 | sizeof(struct smb2_file_all_info) + | |
198 | PATH_MAX * 2, 0, NULL); | |
199 | if (!rc) { | |
200 | smb2_set_next_command(tcon, &rqst[num_rqst]); | |
201 | smb2_set_related(&rqst[num_rqst]); | |
202 | } | |
496902dc | 203 | } |
496902dc | 204 | |
3322960c PA |
205 | if (rc) |
206 | goto finished; | |
207 | num_rqst++; | |
208 | trace_smb3_query_info_compound_enter(xid, ses->Suid, | |
209 | tcon->tid, full_path); | |
210 | break; | |
211 | case SMB2_OP_POSIX_QUERY_INFO: | |
212 | rqst[num_rqst].rq_iov = &vars->qi_iov; | |
213 | rqst[num_rqst].rq_nvec = 1; | |
214 | ||
215 | if (cfile) { | |
6a5f6592 | 216 | /* TBD: fix following to allow for longer SIDs */ |
3322960c PA |
217 | rc = SMB2_query_info_init(tcon, server, |
218 | &rqst[num_rqst], | |
219 | cfile->fid.persistent_fid, | |
220 | cfile->fid.volatile_fid, | |
221 | SMB_FIND_FILE_POSIX_INFO, | |
222 | SMB2_O_INFO_FILE, 0, | |
223 | sizeof(struct smb311_posix_qinfo *) + | |
224 | (PATH_MAX * 2) + | |
225 | (sizeof(struct cifs_sid) * 2), 0, NULL); | |
226 | } else { | |
227 | rc = SMB2_query_info_init(tcon, server, | |
228 | &rqst[num_rqst], | |
229 | COMPOUND_FID, | |
230 | COMPOUND_FID, | |
231 | SMB_FIND_FILE_POSIX_INFO, | |
232 | SMB2_O_INFO_FILE, 0, | |
233 | sizeof(struct smb311_posix_qinfo *) + | |
234 | (PATH_MAX * 2) + | |
235 | (sizeof(struct cifs_sid) * 2), 0, NULL); | |
236 | if (!rc) { | |
237 | smb2_set_next_command(tcon, &rqst[num_rqst]); | |
238 | smb2_set_related(&rqst[num_rqst]); | |
239 | } | |
6a5f6592 | 240 | } |
6a5f6592 | 241 | |
3322960c PA |
242 | if (rc) |
243 | goto finished; | |
244 | num_rqst++; | |
245 | trace_smb3_posix_query_info_compound_enter(xid, ses->Suid, | |
246 | tcon->tid, full_path); | |
247 | break; | |
248 | case SMB2_OP_DELETE: | |
249 | trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path); | |
250 | break; | |
251 | case SMB2_OP_MKDIR: | |
252 | /* | |
253 | * Directories are created through parameters in the | |
254 | * SMB2_open() call. | |
255 | */ | |
256 | trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path); | |
257 | break; | |
258 | case SMB2_OP_RMDIR: | |
259 | rqst[num_rqst].rq_iov = &vars->si_iov[0]; | |
260 | rqst[num_rqst].rq_nvec = 1; | |
f7bfe04b | 261 | |
3322960c PA |
262 | size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */ |
263 | data[0] = &delete_pending[0]; | |
f7bfe04b | 264 | |
211baef0 | 265 | rc = SMB2_set_info_init(tcon, server, |
3322960c PA |
266 | &rqst[num_rqst], COMPOUND_FID, |
267 | COMPOUND_FID, current->tgid, | |
268 | FILE_DISPOSITION_INFORMATION, | |
269 | SMB2_O_INFO_FILE, 0, data, size); | |
270 | if (rc) | |
271 | goto finished; | |
272 | smb2_set_next_command(tcon, &rqst[num_rqst]); | |
273 | smb2_set_related(&rqst[num_rqst++]); | |
274 | trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path); | |
275 | break; | |
276 | case SMB2_OP_SET_EOF: | |
277 | rqst[num_rqst].rq_iov = &vars->si_iov[0]; | |
278 | rqst[num_rqst].rq_nvec = 1; | |
279 | ||
280 | size[0] = in_iov[i].iov_len; | |
281 | data[0] = in_iov[i].iov_base; | |
282 | ||
283 | if (cfile) { | |
284 | rc = SMB2_set_info_init(tcon, server, | |
285 | &rqst[num_rqst], | |
286 | cfile->fid.persistent_fid, | |
287 | cfile->fid.volatile_fid, | |
288 | current->tgid, | |
289 | FILE_END_OF_FILE_INFORMATION, | |
290 | SMB2_O_INFO_FILE, 0, | |
291 | data, size); | |
292 | } else { | |
293 | rc = SMB2_set_info_init(tcon, server, | |
294 | &rqst[num_rqst], | |
295 | COMPOUND_FID, | |
296 | COMPOUND_FID, | |
297 | current->tgid, | |
298 | FILE_END_OF_FILE_INFORMATION, | |
299 | SMB2_O_INFO_FILE, 0, | |
300 | data, size); | |
301 | if (!rc) { | |
302 | smb2_set_next_command(tcon, &rqst[num_rqst]); | |
303 | smb2_set_related(&rqst[num_rqst]); | |
304 | } | |
211baef0 | 305 | } |
3322960c PA |
306 | if (rc) |
307 | goto finished; | |
308 | num_rqst++; | |
309 | trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path); | |
310 | break; | |
311 | case SMB2_OP_SET_INFO: | |
312 | rqst[num_rqst].rq_iov = &vars->si_iov[0]; | |
313 | rqst[num_rqst].rq_nvec = 1; | |
314 | ||
315 | size[0] = in_iov[i].iov_len; | |
316 | data[0] = in_iov[i].iov_base; | |
317 | ||
318 | if (cfile) { | |
319 | rc = SMB2_set_info_init(tcon, server, | |
320 | &rqst[num_rqst], | |
321 | cfile->fid.persistent_fid, | |
322 | cfile->fid.volatile_fid, current->tgid, | |
323 | FILE_BASIC_INFORMATION, | |
324 | SMB2_O_INFO_FILE, 0, data, size); | |
325 | } else { | |
326 | rc = SMB2_set_info_init(tcon, server, | |
327 | &rqst[num_rqst], | |
328 | COMPOUND_FID, | |
329 | COMPOUND_FID, current->tgid, | |
330 | FILE_BASIC_INFORMATION, | |
331 | SMB2_O_INFO_FILE, 0, data, size); | |
332 | if (!rc) { | |
333 | smb2_set_next_command(tcon, &rqst[num_rqst]); | |
334 | smb2_set_related(&rqst[num_rqst]); | |
335 | } | |
dc9300a6 | 336 | } |
dc9300a6 | 337 | |
3322960c PA |
338 | if (rc) |
339 | goto finished; | |
340 | num_rqst++; | |
341 | trace_smb3_set_info_compound_enter(xid, ses->Suid, | |
342 | tcon->tid, full_path); | |
343 | break; | |
344 | case SMB2_OP_RENAME: | |
345 | rqst[num_rqst].rq_iov = &vars->si_iov[0]; | |
346 | rqst[num_rqst].rq_nvec = 2; | |
bb435512 | 347 | |
3322960c | 348 | len = in_iov[i].iov_len; |
bb435512 | 349 | |
3322960c PA |
350 | vars->rename_info.ReplaceIfExists = 1; |
351 | vars->rename_info.RootDirectory = 0; | |
352 | vars->rename_info.FileNameLength = cpu_to_le32(len); | |
bb435512 | 353 | |
3322960c PA |
354 | size[0] = sizeof(struct smb2_file_rename_info); |
355 | data[0] = &vars->rename_info; | |
bb435512 | 356 | |
3322960c PA |
357 | size[1] = len + 2 /* null */; |
358 | data[1] = in_iov[i].iov_base; | |
bb435512 | 359 | |
3322960c PA |
360 | if (cfile) { |
361 | rc = SMB2_set_info_init(tcon, server, | |
362 | &rqst[num_rqst], | |
363 | cfile->fid.persistent_fid, | |
364 | cfile->fid.volatile_fid, | |
365 | current->tgid, FILE_RENAME_INFORMATION, | |
366 | SMB2_O_INFO_FILE, 0, data, size); | |
367 | } else { | |
368 | rc = SMB2_set_info_init(tcon, server, | |
369 | &rqst[num_rqst], | |
370 | COMPOUND_FID, COMPOUND_FID, | |
371 | current->tgid, FILE_RENAME_INFORMATION, | |
372 | SMB2_O_INFO_FILE, 0, data, size); | |
373 | if (!rc) { | |
374 | smb2_set_next_command(tcon, &rqst[num_rqst]); | |
375 | smb2_set_related(&rqst[num_rqst]); | |
376 | } | |
dc9300a6 | 377 | } |
3322960c PA |
378 | if (rc) |
379 | goto finished; | |
380 | num_rqst++; | |
381 | trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path); | |
382 | break; | |
383 | case SMB2_OP_HARDLINK: | |
384 | rqst[num_rqst].rq_iov = &vars->si_iov[0]; | |
385 | rqst[num_rqst].rq_nvec = 2; | |
bb435512 | 386 | |
3322960c | 387 | len = in_iov[i].iov_len; |
bb435512 | 388 | |
3322960c PA |
389 | vars->link_info.ReplaceIfExists = 0; |
390 | vars->link_info.RootDirectory = 0; | |
391 | vars->link_info.FileNameLength = cpu_to_le32(len); | |
bb435512 | 392 | |
3322960c PA |
393 | size[0] = sizeof(struct smb2_file_link_info); |
394 | data[0] = &vars->link_info; | |
bb435512 | 395 | |
3322960c PA |
396 | size[1] = len + 2 /* null */; |
397 | data[1] = in_iov[i].iov_base; | |
bb435512 | 398 | |
3322960c PA |
399 | rc = SMB2_set_info_init(tcon, server, |
400 | &rqst[num_rqst], COMPOUND_FID, | |
401 | COMPOUND_FID, current->tgid, | |
402 | FILE_LINK_INFORMATION, | |
403 | SMB2_O_INFO_FILE, 0, data, size); | |
404 | if (rc) | |
405 | goto finished; | |
406 | smb2_set_next_command(tcon, &rqst[num_rqst]); | |
407 | smb2_set_related(&rqst[num_rqst++]); | |
408 | trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path); | |
409 | break; | |
102466f3 PA |
410 | case SMB2_OP_SET_REPARSE: |
411 | rqst[num_rqst].rq_iov = vars->io_iov; | |
412 | rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov); | |
413 | ||
414 | rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst], | |
415 | COMPOUND_FID, COMPOUND_FID, | |
416 | FSCTL_SET_REPARSE_POINT, | |
417 | in_iov[i].iov_base, | |
418 | in_iov[i].iov_len, 0); | |
419 | if (rc) | |
420 | goto finished; | |
421 | smb2_set_next_command(tcon, &rqst[num_rqst]); | |
422 | smb2_set_related(&rqst[num_rqst++]); | |
423 | trace_smb3_set_reparse_compound_enter(xid, ses->Suid, | |
424 | tcon->tid, full_path); | |
425 | break; | |
67ec9949 PA |
426 | case SMB2_OP_GET_REPARSE: |
427 | rqst[num_rqst].rq_iov = vars->io_iov; | |
428 | rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov); | |
429 | ||
430 | rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst], | |
431 | COMPOUND_FID, COMPOUND_FID, | |
432 | FSCTL_GET_REPARSE_POINT, | |
433 | NULL, 0, CIFSMaxBufSize); | |
434 | if (rc) | |
435 | goto finished; | |
436 | smb2_set_next_command(tcon, &rqst[num_rqst]); | |
437 | smb2_set_related(&rqst[num_rqst++]); | |
438 | trace_smb3_get_reparse_compound_enter(xid, ses->Suid, | |
439 | tcon->tid, full_path); | |
440 | break; | |
3322960c PA |
441 | default: |
442 | cifs_dbg(VFS, "Invalid command\n"); | |
443 | rc = -EINVAL; | |
444 | } | |
c5a5f38f RS |
445 | } |
446 | if (rc) | |
447 | goto finished; | |
448 | ||
8de9e86c RS |
449 | /* We already have a handle so we can skip the close */ |
450 | if (cfile) | |
451 | goto after_close; | |
c5a5f38f | 452 | /* Close */ |
04ad69c3 | 453 | flags |= CIFS_CP_CREATE_CLOSE_OP; |
f4e5ceb6 | 454 | rqst[num_rqst].rq_iov = &vars->close_iov; |
c5a5f38f | 455 | rqst[num_rqst].rq_nvec = 1; |
352d96f3 AA |
456 | rc = SMB2_close_init(tcon, server, |
457 | &rqst[num_rqst], COMPOUND_FID, | |
43f8a6a7 | 458 | COMPOUND_FID, false); |
8de9e86c | 459 | smb2_set_related(&rqst[num_rqst]); |
c5a5f38f RS |
460 | if (rc) |
461 | goto finished; | |
8de9e86c RS |
462 | after_close: |
463 | num_rqst++; | |
464 | ||
465 | if (cfile) { | |
352d96f3 AA |
466 | rc = compound_send_recv(xid, ses, server, |
467 | flags, num_rqst - 2, | |
8de9e86c RS |
468 | &rqst[1], &resp_buftype[1], |
469 | &rsp_iov[1]); | |
470 | } else | |
352d96f3 AA |
471 | rc = compound_send_recv(xid, ses, server, |
472 | flags, num_rqst, | |
8de9e86c RS |
473 | rqst, resp_buftype, |
474 | rsp_iov); | |
c5a5f38f | 475 | |
3322960c PA |
476 | finished: |
477 | num_rqst = 0; | |
478 | SMB2_open_free(&rqst[num_rqst++]); | |
7dcc82c2 | 479 | if (rc == -EREMCHG) { |
68e14569 | 480 | pr_warn_once("server share %s deleted\n", tcon->tree_name); |
7dcc82c2 SF |
481 | tcon->need_reconnect = true; |
482 | } | |
483 | ||
3322960c PA |
484 | for (i = 0; i < num_cmds; i++) { |
485 | switch (cmds[i]) { | |
486 | case SMB2_OP_QUERY_INFO: | |
487 | idata = in_iov[i].iov_base; | |
488 | if (rc == 0 && cfile && cfile->symlink_target) { | |
489 | idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); | |
490 | if (!idata->symlink_target) | |
491 | rc = -ENOMEM; | |
492 | } | |
493 | if (rc == 0) { | |
494 | qi_rsp = (struct smb2_query_info_rsp *) | |
495 | rsp_iov[i + 1].iov_base; | |
496 | rc = smb2_validate_and_copy_iov( | |
497 | le16_to_cpu(qi_rsp->OutputBufferOffset), | |
498 | le32_to_cpu(qi_rsp->OutputBufferLength), | |
499 | &rsp_iov[i + 1], sizeof(idata->fi), (char *)&idata->fi); | |
500 | } | |
501 | SMB2_query_info_free(&rqst[num_rqst++]); | |
502 | if (rc) | |
503 | trace_smb3_query_info_compound_err(xid, ses->Suid, | |
504 | tcon->tid, rc); | |
505 | else | |
506 | trace_smb3_query_info_compound_done(xid, ses->Suid, | |
507 | tcon->tid); | |
508 | break; | |
509 | case SMB2_OP_POSIX_QUERY_INFO: | |
510 | idata = in_iov[i].iov_base; | |
511 | if (rc == 0 && cfile && cfile->symlink_target) { | |
512 | idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); | |
513 | if (!idata->symlink_target) | |
64ce47cb | 514 | rc = -ENOMEM; |
64ce47cb | 515 | } |
3322960c PA |
516 | if (rc == 0) { |
517 | qi_rsp = (struct smb2_query_info_rsp *) | |
518 | rsp_iov[i + 1].iov_base; | |
519 | rc = smb2_validate_and_copy_iov( | |
520 | le16_to_cpu(qi_rsp->OutputBufferOffset), | |
521 | le32_to_cpu(qi_rsp->OutputBufferLength), | |
522 | &rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */, | |
523 | (char *)&idata->posix_fi); | |
524 | } | |
858e7487 PA |
525 | if (rc == 0) |
526 | rc = parse_posix_sids(idata, &rsp_iov[i + 1]); | |
527 | ||
3322960c PA |
528 | SMB2_query_info_free(&rqst[num_rqst++]); |
529 | if (rc) | |
530 | trace_smb3_posix_query_info_compound_err(xid, ses->Suid, | |
531 | tcon->tid, rc); | |
532 | else | |
533 | trace_smb3_posix_query_info_compound_done(xid, ses->Suid, | |
534 | tcon->tid); | |
535 | break; | |
536 | case SMB2_OP_DELETE: | |
537 | if (rc) | |
538 | trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc); | |
539 | else | |
540 | trace_smb3_delete_done(xid, ses->Suid, tcon->tid); | |
541 | break; | |
542 | case SMB2_OP_MKDIR: | |
543 | if (rc) | |
544 | trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc); | |
545 | else | |
546 | trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid); | |
547 | break; | |
548 | case SMB2_OP_HARDLINK: | |
549 | if (rc) | |
550 | trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc); | |
551 | else | |
552 | trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid); | |
553 | SMB2_set_info_free(&rqst[num_rqst++]); | |
554 | break; | |
555 | case SMB2_OP_RENAME: | |
556 | if (rc) | |
557 | trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc); | |
558 | else | |
559 | trace_smb3_rename_done(xid, ses->Suid, tcon->tid); | |
560 | SMB2_set_info_free(&rqst[num_rqst++]); | |
561 | break; | |
562 | case SMB2_OP_RMDIR: | |
563 | if (rc) | |
564 | trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc); | |
565 | else | |
566 | trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid); | |
567 | SMB2_set_info_free(&rqst[num_rqst++]); | |
568 | break; | |
569 | case SMB2_OP_SET_EOF: | |
570 | if (rc) | |
571 | trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc); | |
572 | else | |
573 | trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid); | |
574 | SMB2_set_info_free(&rqst[num_rqst++]); | |
575 | break; | |
576 | case SMB2_OP_SET_INFO: | |
577 | if (rc) | |
578 | trace_smb3_set_info_compound_err(xid, ses->Suid, | |
579 | tcon->tid, rc); | |
580 | else | |
581 | trace_smb3_set_info_compound_done(xid, ses->Suid, | |
582 | tcon->tid); | |
583 | SMB2_set_info_free(&rqst[num_rqst++]); | |
584 | break; | |
102466f3 PA |
585 | case SMB2_OP_SET_REPARSE: |
586 | if (rc) { | |
587 | trace_smb3_set_reparse_compound_err(xid, ses->Suid, | |
588 | tcon->tid, rc); | |
589 | } else { | |
590 | trace_smb3_set_reparse_compound_done(xid, ses->Suid, | |
591 | tcon->tid); | |
592 | } | |
593 | SMB2_ioctl_free(&rqst[num_rqst++]); | |
594 | break; | |
67ec9949 PA |
595 | case SMB2_OP_GET_REPARSE: |
596 | if (!rc) { | |
597 | iov = &rsp_iov[i + 1]; | |
598 | idata = in_iov[i].iov_base; | |
599 | idata->reparse.io.iov = *iov; | |
600 | idata->reparse.io.buftype = resp_buftype[i + 1]; | |
601 | rbuf = reparse_buf_ptr(iov); | |
602 | if (IS_ERR(rbuf)) { | |
603 | rc = PTR_ERR(rbuf); | |
604 | trace_smb3_set_reparse_compound_err(xid, ses->Suid, | |
605 | tcon->tid, rc); | |
606 | } else { | |
607 | idata->reparse.tag = le32_to_cpu(rbuf->ReparseTag); | |
608 | trace_smb3_set_reparse_compound_done(xid, ses->Suid, | |
609 | tcon->tid); | |
610 | } | |
611 | memset(iov, 0, sizeof(*iov)); | |
612 | resp_buftype[i + 1] = CIFS_NO_BUFFER; | |
613 | } else { | |
614 | trace_smb3_set_reparse_compound_err(xid, ses->Suid, | |
615 | tcon->tid, rc); | |
616 | } | |
617 | SMB2_ioctl_free(&rqst[num_rqst++]); | |
618 | break; | |
64ce47cb | 619 | } |
c5a5f38f | 620 | } |
3322960c | 621 | SMB2_close_free(&rqst[num_rqst]); |
76894f3e | 622 | |
33f73618 SP |
623 | if (cfile) |
624 | cifsFileInfo_put(cfile); | |
625 | ||
3322960c | 626 | num_cmds += 2; |
c5f44a3d | 627 | if (out_iov && out_buftype) { |
3322960c PA |
628 | memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov)); |
629 | memcpy(out_buftype, resp_buftype, | |
630 | num_cmds * sizeof(*out_buftype)); | |
76894f3e | 631 | } else { |
3322960c PA |
632 | for (i = 0; i < num_cmds; i++) |
633 | free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base); | |
76894f3e | 634 | } |
a7d5c294 | 635 | kfree(vars); |
c5a5f38f RS |
636 | return rc; |
637 | } | |
638 | ||
5f71ebc4 PA |
639 | static int parse_create_response(struct cifs_open_info_data *data, |
640 | struct cifs_sb_info *cifs_sb, | |
641 | const struct kvec *iov) | |
642 | { | |
643 | struct smb2_create_rsp *rsp = iov->iov_base; | |
644 | bool reparse_point = false; | |
645 | u32 tag = 0; | |
646 | int rc = 0; | |
647 | ||
648 | switch (rsp->hdr.Status) { | |
7fb77d9c PA |
649 | case STATUS_IO_REPARSE_TAG_NOT_HANDLED: |
650 | reparse_point = true; | |
651 | break; | |
5f71ebc4 PA |
652 | case STATUS_STOPPED_ON_SYMLINK: |
653 | rc = smb2_parse_symlink_response(cifs_sb, iov, | |
654 | &data->symlink_target); | |
655 | if (rc) | |
656 | return rc; | |
657 | tag = IO_REPARSE_TAG_SYMLINK; | |
658 | reparse_point = true; | |
659 | break; | |
660 | case STATUS_SUCCESS: | |
661 | reparse_point = !!(rsp->Flags & SMB2_CREATE_FLAG_REPARSEPOINT); | |
662 | break; | |
663 | } | |
664 | data->reparse_point = reparse_point; | |
45e72402 | 665 | data->reparse.tag = tag; |
5f71ebc4 PA |
666 | return rc; |
667 | } | |
668 | ||
8b4e285d PA |
669 | int smb2_query_path_info(const unsigned int xid, |
670 | struct cifs_tcon *tcon, | |
671 | struct cifs_sb_info *cifs_sb, | |
672 | const char *full_path, | |
673 | struct cifs_open_info_data *data) | |
be4cb9e3 | 674 | { |
61351d6d | 675 | __u32 create_options = 0; |
496902dc | 676 | struct cifsFileInfo *cfile; |
9e81e8ff | 677 | struct cached_fid *cfid = NULL; |
5f71ebc4 | 678 | struct smb2_hdr *hdr; |
67ec9949 | 679 | struct kvec in_iov[2], out_iov[3] = {}; |
c5f44a3d | 680 | int out_buftype[3] = {}; |
f83709b9 | 681 | int cmds[2]; |
b9ee2e30 | 682 | bool islink; |
67ec9949 | 683 | int i, num_cmds; |
b9ee2e30 | 684 | int rc, rc2; |
be4cb9e3 | 685 | |
8b4e285d PA |
686 | data->adjust_tz = false; |
687 | data->reparse_point = false; | |
be4cb9e3 | 688 | |
f83709b9 PA |
689 | /* |
690 | * BB TODO: Add support for using cached root handle in SMB3.1.1 POSIX. | |
691 | * Create SMB2_query_posix_info worker function to do non-compounded | |
692 | * query when we already have an open file handle for this. For now this | |
693 | * is fast enough (always using the compounded version). | |
694 | */ | |
695 | if (!tcon->posix_extensions) { | |
696 | if (*full_path) { | |
697 | rc = -ENOENT; | |
e6eb1950 | 698 | } else { |
f83709b9 PA |
699 | rc = open_cached_dir(xid, tcon, full_path, |
700 | cifs_sb, false, &cfid); | |
b0f6df73 | 701 | } |
f83709b9 PA |
702 | /* If it is a root and its handle is cached then use it */ |
703 | if (!rc) { | |
704 | if (cfid->file_all_info_is_valid) { | |
705 | memcpy(&data->fi, &cfid->file_all_info, | |
706 | sizeof(data->fi)); | |
707 | } else { | |
708 | rc = SMB2_query_info(xid, tcon, | |
709 | cfid->fid.persistent_fid, | |
710 | cfid->fid.volatile_fid, | |
711 | &data->fi); | |
712 | } | |
713 | close_cached_dir(cfid); | |
714 | return rc; | |
715 | } | |
716 | cmds[0] = SMB2_OP_QUERY_INFO; | |
717 | } else { | |
718 | cmds[0] = SMB2_OP_POSIX_QUERY_INFO; | |
6a9cbdd1 PS |
719 | } |
720 | ||
67ec9949 PA |
721 | in_iov[0].iov_base = data; |
722 | in_iov[0].iov_len = sizeof(*data); | |
723 | in_iov[1] = in_iov[0]; | |
3322960c | 724 | |
496902dc | 725 | cifs_get_readable_path(tcon, full_path, &cfile); |
3322960c PA |
726 | rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, |
727 | FILE_READ_ATTRIBUTES, FILE_OPEN, | |
858e7487 PA |
728 | create_options, ACL_NO_MODE, in_iov, |
729 | cmds, 1, cfile, out_iov, out_buftype); | |
5f71ebc4 PA |
730 | hdr = out_iov[0].iov_base; |
731 | /* | |
732 | * If first iov is unset, then SMB session was dropped or we've got a | |
733 | * cached open file (@cfile). | |
734 | */ | |
735 | if (!hdr || out_buftype[0] == CIFS_NO_BUFFER) | |
736 | goto out; | |
737 | ||
738 | switch (rc) { | |
739 | case 0: | |
740 | case -EOPNOTSUPP: | |
f83709b9 PA |
741 | /* |
742 | * BB TODO: When support for special files added to Samba | |
743 | * re-verify this path. | |
744 | */ | |
5f71ebc4 PA |
745 | rc = parse_create_response(data, cifs_sb, &out_iov[0]); |
746 | if (rc || !data->reparse_point) | |
c877ce47 | 747 | goto out; |
5f71ebc4 | 748 | |
67ec9949 PA |
749 | if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) { |
750 | /* symlink already parsed in create response */ | |
751 | num_cmds = 1; | |
752 | } else { | |
753 | cmds[1] = SMB2_OP_GET_REPARSE; | |
754 | num_cmds = 2; | |
755 | } | |
5f71ebc4 | 756 | create_options |= OPEN_REPARSE_POINT; |
5f71ebc4 PA |
757 | cifs_get_readable_path(tcon, full_path, &cfile); |
758 | rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, | |
759 | FILE_READ_ATTRIBUTES, FILE_OPEN, | |
858e7487 PA |
760 | create_options, ACL_NO_MODE, in_iov, |
761 | cmds, num_cmds, cfile, NULL, NULL); | |
5f71ebc4 PA |
762 | break; |
763 | case -EREMOTE: | |
764 | break; | |
765 | default: | |
766 | if (hdr->Status != STATUS_OBJECT_NAME_INVALID) | |
767 | break; | |
768 | rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb, | |
769 | full_path, &islink); | |
770 | if (rc2) { | |
771 | rc = rc2; | |
c877ce47 | 772 | goto out; |
76894f3e | 773 | } |
5f71ebc4 PA |
774 | if (islink) |
775 | rc = -EREMOTE; | |
eb85d94b | 776 | } |
be4cb9e3 | 777 | |
be4cb9e3 | 778 | out: |
67ec9949 PA |
779 | for (i = 0; i < ARRAY_SIZE(out_buftype); i++) |
780 | free_rsp_buf(out_buftype[i], out_iov[i].iov_base); | |
be4cb9e3 PS |
781 | return rc; |
782 | } | |
a0e73183 PS |
783 | |
784 | int | |
c3ca78e2 SF |
785 | smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode, |
786 | struct cifs_tcon *tcon, const char *name, | |
a0e73183 PS |
787 | struct cifs_sb_info *cifs_sb) |
788 | { | |
f733e393 RS |
789 | return smb2_compound_op(xid, tcon, cifs_sb, name, |
790 | FILE_WRITE_ATTRIBUTES, FILE_CREATE, | |
858e7487 PA |
791 | CREATE_NOT_FILE, mode, |
792 | NULL, &(int){SMB2_OP_MKDIR}, 1, | |
793 | NULL, NULL, NULL); | |
a0e73183 PS |
794 | } |
795 | ||
796 | void | |
797 | smb2_mkdir_setinfo(struct inode *inode, const char *name, | |
798 | struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, | |
799 | const unsigned int xid) | |
800 | { | |
3322960c | 801 | FILE_BASIC_INFO data = {}; |
a0e73183 | 802 | struct cifsInodeInfo *cifs_i; |
dc9300a6 | 803 | struct cifsFileInfo *cfile; |
3322960c | 804 | struct kvec in_iov; |
a0e73183 PS |
805 | u32 dosattrs; |
806 | int tmprc; | |
807 | ||
3322960c PA |
808 | in_iov.iov_base = &data; |
809 | in_iov.iov_len = sizeof(data); | |
a0e73183 PS |
810 | cifs_i = CIFS_I(inode); |
811 | dosattrs = cifs_i->cifsAttrs | ATTR_READONLY; | |
812 | data.Attributes = cpu_to_le32(dosattrs); | |
86f740f2 | 813 | cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile); |
dcbf9103 RS |
814 | tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, |
815 | FILE_WRITE_ATTRIBUTES, FILE_CREATE, | |
3322960c PA |
816 | CREATE_NOT_FILE, ACL_NO_MODE, &in_iov, |
817 | &(int){SMB2_OP_SET_INFO}, 1, | |
858e7487 | 818 | cfile, NULL, NULL); |
a0e73183 PS |
819 | if (tmprc == 0) |
820 | cifs_i->cifsAttrs = dosattrs; | |
821 | } | |
1a500f01 PS |
822 | |
823 | int | |
824 | smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, | |
825 | struct cifs_sb_info *cifs_sb) | |
826 | { | |
8e77860c | 827 | drop_cached_dir_by_name(xid, tcon, name, cifs_sb); |
3322960c PA |
828 | return smb2_compound_op(xid, tcon, cifs_sb, name, |
829 | DELETE, FILE_OPEN, CREATE_NOT_FILE, | |
858e7487 PA |
830 | ACL_NO_MODE, NULL, |
831 | &(int){SMB2_OP_RMDIR}, 1, | |
832 | NULL, NULL, NULL); | |
1a500f01 | 833 | } |
cbe6f439 PS |
834 | |
835 | int | |
836 | smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, | |
837 | struct cifs_sb_info *cifs_sb) | |
838 | { | |
47dd9597 RS |
839 | return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, |
840 | CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, | |
858e7487 PA |
841 | ACL_NO_MODE, NULL, |
842 | &(int){SMB2_OP_DELETE}, 1, | |
843 | NULL, NULL, NULL); | |
cbe6f439 | 844 | } |
35143eb5 | 845 | |
7435d51b PA |
846 | static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, |
847 | const char *from_name, const char *to_name, | |
848 | struct cifs_sb_info *cifs_sb, | |
849 | __u32 create_options, __u32 access, | |
850 | int command, struct cifsFileInfo *cfile) | |
35143eb5 | 851 | { |
3322960c | 852 | struct kvec in_iov; |
35143eb5 PS |
853 | __le16 *smb2_to_name = NULL; |
854 | int rc; | |
855 | ||
856 | smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb); | |
857 | if (smb2_to_name == NULL) { | |
858 | rc = -ENOMEM; | |
859 | goto smb2_rename_path; | |
860 | } | |
3322960c PA |
861 | in_iov.iov_base = smb2_to_name; |
862 | in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX); | |
bb435512 | 863 | rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, |
858e7487 PA |
864 | FILE_OPEN, create_options, ACL_NO_MODE, |
865 | &in_iov, &command, 1, cfile, NULL, NULL); | |
35143eb5 PS |
866 | smb2_rename_path: |
867 | kfree(smb2_to_name); | |
868 | return rc; | |
869 | } | |
568798cc | 870 | |
7435d51b PA |
871 | int smb2_rename_path(const unsigned int xid, |
872 | struct cifs_tcon *tcon, | |
873 | struct dentry *source_dentry, | |
874 | const char *from_name, const char *to_name, | |
875 | struct cifs_sb_info *cifs_sb) | |
568798cc | 876 | { |
8de9e86c | 877 | struct cifsFileInfo *cfile; |
5408990a | 878 | __u32 co = file_create_options(source_dentry); |
8de9e86c | 879 | |
8e77860c | 880 | drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb); |
86f740f2 | 881 | cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile); |
8de9e86c | 882 | |
7435d51b PA |
883 | return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, |
884 | co, DELETE, SMB2_OP_RENAME, cfile); | |
568798cc PS |
885 | } |
886 | ||
5408990a PA |
887 | int smb2_create_hardlink(const unsigned int xid, |
888 | struct cifs_tcon *tcon, | |
889 | struct dentry *source_dentry, | |
890 | const char *from_name, const char *to_name, | |
891 | struct cifs_sb_info *cifs_sb) | |
568798cc | 892 | { |
5408990a PA |
893 | __u32 co = file_create_options(source_dentry); |
894 | ||
7435d51b | 895 | return smb2_set_path_attr(xid, tcon, from_name, to_name, |
5408990a | 896 | cifs_sb, co, FILE_READ_ATTRIBUTES, |
7435d51b | 897 | SMB2_OP_HARDLINK, NULL); |
568798cc | 898 | } |
c839ff24 PS |
899 | |
900 | int | |
901 | smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, | |
902 | const char *full_path, __u64 size, | |
903 | struct cifs_sb_info *cifs_sb, bool set_alloc) | |
904 | { | |
b6f2a0f8 | 905 | struct cifsFileInfo *cfile; |
3322960c PA |
906 | struct kvec in_iov; |
907 | __le64 eof = cpu_to_le64(size); | |
f7bfe04b | 908 | |
3322960c PA |
909 | in_iov.iov_base = &eof; |
910 | in_iov.iov_len = sizeof(eof); | |
b6f2a0f8 | 911 | cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); |
f7bfe04b | 912 | return smb2_compound_op(xid, tcon, cifs_sb, full_path, |
3322960c PA |
913 | FILE_WRITE_DATA, FILE_OPEN, |
914 | 0, ACL_NO_MODE, &in_iov, | |
915 | &(int){SMB2_OP_SET_EOF}, 1, | |
858e7487 | 916 | cfile, NULL, NULL); |
c839ff24 | 917 | } |
1feeaac7 PS |
918 | |
919 | int | |
920 | smb2_set_file_info(struct inode *inode, const char *full_path, | |
921 | FILE_BASIC_INFO *buf, const unsigned int xid) | |
922 | { | |
923 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | |
924 | struct tcon_link *tlink; | |
b6f2a0f8 SP |
925 | struct cifs_tcon *tcon; |
926 | struct cifsFileInfo *cfile; | |
3322960c | 927 | struct kvec in_iov = { .iov_base = buf, .iov_len = sizeof(*buf), }; |
1feeaac7 PS |
928 | int rc; |
929 | ||
18dd8e1a | 930 | if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) && |
fd09b7d3 | 931 | (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) && |
18dd8e1a SF |
932 | (buf->Attributes == 0)) |
933 | return 0; /* would be a no op, no sense sending this */ | |
934 | ||
1feeaac7 PS |
935 | tlink = cifs_sb_tlink(cifs_sb); |
936 | if (IS_ERR(tlink)) | |
937 | return PTR_ERR(tlink); | |
b6f2a0f8 | 938 | tcon = tlink_tcon(tlink); |
18dd8e1a | 939 | |
b6f2a0f8 SP |
940 | cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); |
941 | rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, | |
c3ca78e2 | 942 | FILE_WRITE_ATTRIBUTES, FILE_OPEN, |
3322960c | 943 | 0, ACL_NO_MODE, &in_iov, |
858e7487 PA |
944 | &(int){SMB2_OP_SET_INFO}, 1, |
945 | cfile, NULL, NULL); | |
1feeaac7 PS |
946 | cifs_put_tlink(tlink); |
947 | return rc; | |
948 | } | |
102466f3 PA |
949 | |
950 | struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, | |
951 | struct super_block *sb, | |
952 | const unsigned int xid, | |
953 | struct cifs_tcon *tcon, | |
954 | const char *full_path, | |
955 | struct kvec *iov) | |
956 | { | |
957 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); | |
958 | struct cifsFileInfo *cfile; | |
959 | struct inode *new = NULL; | |
960 | struct kvec in_iov[2]; | |
961 | int cmds[2]; | |
962 | int da, co, cd; | |
963 | int rc; | |
964 | ||
965 | da = SYNCHRONIZE | DELETE | | |
966 | FILE_READ_ATTRIBUTES | | |
967 | FILE_WRITE_ATTRIBUTES; | |
968 | co = CREATE_NOT_DIR | OPEN_REPARSE_POINT; | |
969 | cd = FILE_CREATE; | |
970 | cmds[0] = SMB2_OP_SET_REPARSE; | |
971 | in_iov[0] = *iov; | |
972 | in_iov[1].iov_base = data; | |
973 | in_iov[1].iov_len = sizeof(*data); | |
974 | ||
975 | if (tcon->posix_extensions) { | |
976 | cmds[1] = SMB2_OP_POSIX_QUERY_INFO; | |
977 | cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); | |
978 | rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, | |
979 | da, cd, co, ACL_NO_MODE, in_iov, | |
858e7487 | 980 | cmds, 2, cfile, NULL, NULL); |
102466f3 PA |
981 | if (!rc) { |
982 | rc = smb311_posix_get_inode_info(&new, full_path, | |
983 | data, sb, xid); | |
984 | } | |
985 | } else { | |
986 | cmds[1] = SMB2_OP_QUERY_INFO; | |
987 | cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); | |
988 | rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, | |
989 | da, cd, co, ACL_NO_MODE, in_iov, | |
858e7487 | 990 | cmds, 2, cfile, NULL, NULL); |
102466f3 PA |
991 | if (!rc) { |
992 | rc = cifs_get_inode_info(&new, full_path, | |
993 | data, sb, xid, NULL); | |
994 | } | |
995 | } | |
996 | return rc ? ERR_PTR(rc) : new; | |
997 | } | |
3ded18a9 PA |
998 | |
999 | int smb2_query_reparse_point(const unsigned int xid, | |
1000 | struct cifs_tcon *tcon, | |
1001 | struct cifs_sb_info *cifs_sb, | |
1002 | const char *full_path, | |
1003 | u32 *tag, struct kvec *rsp, | |
1004 | int *rsp_buftype) | |
1005 | { | |
1006 | struct cifs_open_info_data data = {}; | |
1007 | struct cifsFileInfo *cfile; | |
1008 | struct kvec in_iov = { .iov_base = &data, .iov_len = sizeof(data), }; | |
1009 | int rc; | |
1010 | ||
1011 | cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); | |
1012 | ||
1013 | cifs_get_readable_path(tcon, full_path, &cfile); | |
1014 | rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, | |
1015 | FILE_READ_ATTRIBUTES, FILE_OPEN, | |
1016 | OPEN_REPARSE_POINT, ACL_NO_MODE, &in_iov, | |
858e7487 PA |
1017 | &(int){SMB2_OP_GET_REPARSE}, 1, |
1018 | cfile, NULL, NULL); | |
3ded18a9 PA |
1019 | if (rc) |
1020 | goto out; | |
1021 | ||
1022 | *tag = data.reparse.tag; | |
1023 | *rsp = data.reparse.io.iov; | |
1024 | *rsp_buftype = data.reparse.io.buftype; | |
1025 | memset(&data.reparse.io.iov, 0, sizeof(data.reparse.io.iov)); | |
1026 | data.reparse.io.buftype = CIFS_NO_BUFFER; | |
1027 | out: | |
1028 | cifs_free_open_info(&data); | |
1029 | return rc; | |
1030 | } |