]>
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" |
be4cb9e3 | 27 | |
8191576a SF |
28 | static void |
29 | free_set_inf_compound(struct smb_rqst *rqst) | |
30 | { | |
31 | if (rqst[1].rq_iov) | |
32 | SMB2_set_info_free(&rqst[1]); | |
33 | if (rqst[2].rq_iov) | |
34 | SMB2_close_free(&rqst[2]); | |
35 | } | |
36 | ||
37 | ||
a7d5c294 RS |
38 | struct cop_vars { |
39 | struct cifs_open_parms oparms; | |
40 | struct kvec rsp_iov[3]; | |
41 | struct smb_rqst rqst[3]; | |
42 | struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; | |
43 | struct kvec qi_iov[1]; | |
44 | struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; | |
45 | struct kvec close_iov[1]; | |
46 | struct smb2_file_rename_info rename_info; | |
47 | struct smb2_file_link_info link_info; | |
48 | }; | |
49 | ||
b6f2a0f8 SP |
50 | /* |
51 | * note: If cfile is passed, the reference to it is dropped here. | |
52 | * So make sure that you do not reuse cfile after return from this func. | |
53 | */ | |
c5a5f38f RS |
54 | static int |
55 | smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, | |
56 | struct cifs_sb_info *cifs_sb, const char *full_path, | |
57 | __u32 desired_access, __u32 create_disposition, | |
c3ca78e2 | 58 | __u32 create_options, umode_t mode, void *ptr, int command, |
8de9e86c | 59 | struct cifsFileInfo *cfile) |
c5a5f38f | 60 | { |
a7d5c294 RS |
61 | struct cop_vars *vars = NULL; |
62 | struct kvec *rsp_iov; | |
63 | struct smb_rqst *rqst; | |
c5a5f38f RS |
64 | int rc; |
65 | __le16 *utf16_path = NULL; | |
66 | __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; | |
c5a5f38f RS |
67 | struct cifs_fid fid; |
68 | struct cifs_ses *ses = tcon->ses; | |
352d96f3 | 69 | struct TCP_Server_Info *server; |
c5a5f38f | 70 | int num_rqst = 0; |
c5a5f38f | 71 | int resp_buftype[3]; |
c2e0fe3f | 72 | struct smb2_query_info_rsp *qi_rsp = NULL; |
c5a5f38f | 73 | int flags = 0; |
c2e0fe3f | 74 | __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0}; |
bb435512 RS |
75 | unsigned int size[2]; |
76 | void *data[2]; | |
bb435512 | 77 | int len; |
c5a5f38f | 78 | |
a7d5c294 RS |
79 | vars = kzalloc(sizeof(*vars), GFP_ATOMIC); |
80 | if (vars == NULL) | |
81 | return -ENOMEM; | |
82 | rqst = &vars->rqst[0]; | |
83 | rsp_iov = &vars->rsp_iov[0]; | |
84 | ||
352d96f3 AA |
85 | server = cifs_pick_channel(ses); |
86 | ||
c5a5f38f RS |
87 | if (smb3_encryption_required(tcon)) |
88 | flags |= CIFS_TRANSFORM_REQ; | |
89 | ||
c5a5f38f | 90 | resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; |
c5a5f38f | 91 | |
8de9e86c RS |
92 | /* We already have a handle so we can skip the open */ |
93 | if (cfile) | |
94 | goto after_open; | |
95 | ||
c5a5f38f RS |
96 | /* Open */ |
97 | utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); | |
8de9e86c RS |
98 | if (!utf16_path) { |
99 | rc = -ENOMEM; | |
100 | goto finished; | |
101 | } | |
c5a5f38f | 102 | |
a7d5c294 RS |
103 | vars->oparms.tcon = tcon; |
104 | vars->oparms.desired_access = desired_access; | |
105 | vars->oparms.disposition = create_disposition; | |
106 | vars->oparms.create_options = cifs_create_options(cifs_sb, create_options); | |
107 | vars->oparms.fid = &fid; | |
108 | vars->oparms.reconnect = false; | |
109 | vars->oparms.mode = mode; | |
c8c412f9 | 110 | vars->oparms.cifs_sb = cifs_sb; |
a7d5c294 RS |
111 | |
112 | rqst[num_rqst].rq_iov = &vars->open_iov[0]; | |
c5a5f38f | 113 | rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE; |
352d96f3 AA |
114 | rc = SMB2_open_init(tcon, server, |
115 | &rqst[num_rqst], &oplock, &vars->oparms, | |
c5a5f38f RS |
116 | utf16_path); |
117 | kfree(utf16_path); | |
118 | if (rc) | |
119 | goto finished; | |
120 | ||
8de9e86c RS |
121 | smb2_set_next_command(tcon, &rqst[num_rqst]); |
122 | after_open: | |
123 | num_rqst++; | |
124 | rc = 0; | |
c5a5f38f RS |
125 | |
126 | /* Operation */ | |
127 | switch (command) { | |
128 | case SMB2_OP_QUERY_INFO: | |
a7d5c294 | 129 | rqst[num_rqst].rq_iov = &vars->qi_iov[0]; |
c5a5f38f RS |
130 | rqst[num_rqst].rq_nvec = 1; |
131 | ||
496902dc | 132 | if (cfile) |
352d96f3 AA |
133 | rc = SMB2_query_info_init(tcon, server, |
134 | &rqst[num_rqst], | |
496902dc RS |
135 | cfile->fid.persistent_fid, |
136 | cfile->fid.volatile_fid, | |
137 | FILE_ALL_INFORMATION, | |
c5a5f38f RS |
138 | SMB2_O_INFO_FILE, 0, |
139 | sizeof(struct smb2_file_all_info) + | |
f5b05d62 | 140 | PATH_MAX * 2, 0, NULL); |
496902dc | 141 | else { |
352d96f3 AA |
142 | rc = SMB2_query_info_init(tcon, server, |
143 | &rqst[num_rqst], | |
496902dc RS |
144 | COMPOUND_FID, |
145 | COMPOUND_FID, | |
352d96f3 | 146 | FILE_ALL_INFORMATION, |
496902dc RS |
147 | SMB2_O_INFO_FILE, 0, |
148 | sizeof(struct smb2_file_all_info) + | |
149 | PATH_MAX * 2, 0, NULL); | |
150 | if (!rc) { | |
151 | smb2_set_next_command(tcon, &rqst[num_rqst]); | |
152 | smb2_set_related(&rqst[num_rqst]); | |
153 | } | |
154 | } | |
155 | ||
6a5f6592 SF |
156 | if (rc) |
157 | goto finished; | |
158 | num_rqst++; | |
159 | trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid, | |
160 | full_path); | |
161 | break; | |
162 | case SMB2_OP_POSIX_QUERY_INFO: | |
163 | rqst[num_rqst].rq_iov = &vars->qi_iov[0]; | |
164 | rqst[num_rqst].rq_nvec = 1; | |
165 | ||
166 | if (cfile) | |
167 | rc = SMB2_query_info_init(tcon, server, | |
168 | &rqst[num_rqst], | |
169 | cfile->fid.persistent_fid, | |
170 | cfile->fid.volatile_fid, | |
171 | SMB_FIND_FILE_POSIX_INFO, | |
172 | SMB2_O_INFO_FILE, 0, | |
173 | /* TBD: fix following to allow for longer SIDs */ | |
174 | sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + | |
175 | (sizeof(struct cifs_sid) * 2), 0, NULL); | |
176 | else { | |
177 | rc = SMB2_query_info_init(tcon, server, | |
178 | &rqst[num_rqst], | |
179 | COMPOUND_FID, | |
180 | COMPOUND_FID, | |
181 | SMB_FIND_FILE_POSIX_INFO, | |
182 | SMB2_O_INFO_FILE, 0, | |
183 | sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + | |
184 | (sizeof(struct cifs_sid) * 2), 0, NULL); | |
185 | if (!rc) { | |
186 | smb2_set_next_command(tcon, &rqst[num_rqst]); | |
187 | smb2_set_related(&rqst[num_rqst]); | |
188 | } | |
189 | } | |
190 | ||
88a92c91 RS |
191 | if (rc) |
192 | goto finished; | |
496902dc | 193 | num_rqst++; |
e4bd7c4a | 194 | trace_smb3_posix_query_info_compound_enter(xid, ses->Suid, tcon->tid, full_path); |
c5a5f38f | 195 | break; |
47dd9597 | 196 | case SMB2_OP_DELETE: |
8191576a | 197 | trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path); |
47dd9597 | 198 | break; |
f733e393 RS |
199 | case SMB2_OP_MKDIR: |
200 | /* | |
201 | * Directories are created through parameters in the | |
202 | * SMB2_open() call. | |
203 | */ | |
8191576a | 204 | trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path); |
f733e393 | 205 | break; |
c2e0fe3f | 206 | case SMB2_OP_RMDIR: |
a7d5c294 | 207 | rqst[num_rqst].rq_iov = &vars->si_iov[0]; |
c2e0fe3f RS |
208 | rqst[num_rqst].rq_nvec = 1; |
209 | ||
271b9c0c | 210 | size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */ |
c2e0fe3f RS |
211 | data[0] = &delete_pending[0]; |
212 | ||
352d96f3 AA |
213 | rc = SMB2_set_info_init(tcon, server, |
214 | &rqst[num_rqst], COMPOUND_FID, | |
c2e0fe3f RS |
215 | COMPOUND_FID, current->tgid, |
216 | FILE_DISPOSITION_INFORMATION, | |
217 | SMB2_O_INFO_FILE, 0, data, size); | |
88a92c91 RS |
218 | if (rc) |
219 | goto finished; | |
e77fe73c | 220 | smb2_set_next_command(tcon, &rqst[num_rqst]); |
c2e0fe3f | 221 | smb2_set_related(&rqst[num_rqst++]); |
8191576a | 222 | trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path); |
c2e0fe3f | 223 | break; |
f7bfe04b | 224 | case SMB2_OP_SET_EOF: |
a7d5c294 | 225 | rqst[num_rqst].rq_iov = &vars->si_iov[0]; |
f7bfe04b RS |
226 | rqst[num_rqst].rq_nvec = 1; |
227 | ||
228 | size[0] = 8; /* sizeof __le64 */ | |
229 | data[0] = ptr; | |
230 | ||
352d96f3 AA |
231 | rc = SMB2_set_info_init(tcon, server, |
232 | &rqst[num_rqst], COMPOUND_FID, | |
f7bfe04b RS |
233 | COMPOUND_FID, current->tgid, |
234 | FILE_END_OF_FILE_INFORMATION, | |
235 | SMB2_O_INFO_FILE, 0, data, size); | |
88a92c91 RS |
236 | if (rc) |
237 | goto finished; | |
e77fe73c | 238 | smb2_set_next_command(tcon, &rqst[num_rqst]); |
f7bfe04b | 239 | smb2_set_related(&rqst[num_rqst++]); |
8191576a | 240 | trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path); |
f7bfe04b | 241 | break; |
dcbf9103 | 242 | case SMB2_OP_SET_INFO: |
a7d5c294 | 243 | rqst[num_rqst].rq_iov = &vars->si_iov[0]; |
dcbf9103 RS |
244 | rqst[num_rqst].rq_nvec = 1; |
245 | ||
bb435512 | 246 | |
dcbf9103 RS |
247 | size[0] = sizeof(FILE_BASIC_INFO); |
248 | data[0] = ptr; | |
249 | ||
dc9300a6 | 250 | if (cfile) |
352d96f3 AA |
251 | rc = SMB2_set_info_init(tcon, server, |
252 | &rqst[num_rqst], | |
dc9300a6 RS |
253 | cfile->fid.persistent_fid, |
254 | cfile->fid.volatile_fid, current->tgid, | |
255 | FILE_BASIC_INFORMATION, | |
256 | SMB2_O_INFO_FILE, 0, data, size); | |
257 | else { | |
352d96f3 AA |
258 | rc = SMB2_set_info_init(tcon, server, |
259 | &rqst[num_rqst], | |
dc9300a6 RS |
260 | COMPOUND_FID, |
261 | COMPOUND_FID, current->tgid, | |
262 | FILE_BASIC_INFORMATION, | |
263 | SMB2_O_INFO_FILE, 0, data, size); | |
264 | if (!rc) { | |
265 | smb2_set_next_command(tcon, &rqst[num_rqst]); | |
266 | smb2_set_related(&rqst[num_rqst]); | |
267 | } | |
268 | } | |
269 | ||
88a92c91 RS |
270 | if (rc) |
271 | goto finished; | |
dc9300a6 | 272 | num_rqst++; |
8191576a SF |
273 | trace_smb3_set_info_compound_enter(xid, ses->Suid, tcon->tid, |
274 | full_path); | |
dcbf9103 | 275 | break; |
bb435512 | 276 | case SMB2_OP_RENAME: |
a7d5c294 | 277 | rqst[num_rqst].rq_iov = &vars->si_iov[0]; |
bb435512 RS |
278 | rqst[num_rqst].rq_nvec = 2; |
279 | ||
280 | len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); | |
281 | ||
a7d5c294 RS |
282 | vars->rename_info.ReplaceIfExists = 1; |
283 | vars->rename_info.RootDirectory = 0; | |
284 | vars->rename_info.FileNameLength = cpu_to_le32(len); | |
bb435512 RS |
285 | |
286 | size[0] = sizeof(struct smb2_file_rename_info); | |
a7d5c294 | 287 | data[0] = &vars->rename_info; |
bb435512 RS |
288 | |
289 | size[1] = len + 2 /* null */; | |
290 | data[1] = (__le16 *)ptr; | |
291 | ||
8de9e86c | 292 | if (cfile) |
352d96f3 AA |
293 | rc = SMB2_set_info_init(tcon, server, |
294 | &rqst[num_rqst], | |
8de9e86c RS |
295 | cfile->fid.persistent_fid, |
296 | cfile->fid.volatile_fid, | |
297 | current->tgid, FILE_RENAME_INFORMATION, | |
298 | SMB2_O_INFO_FILE, 0, data, size); | |
299 | else { | |
352d96f3 AA |
300 | rc = SMB2_set_info_init(tcon, server, |
301 | &rqst[num_rqst], | |
8de9e86c RS |
302 | COMPOUND_FID, COMPOUND_FID, |
303 | current->tgid, FILE_RENAME_INFORMATION, | |
bb435512 | 304 | SMB2_O_INFO_FILE, 0, data, size); |
dc9300a6 RS |
305 | if (!rc) { |
306 | smb2_set_next_command(tcon, &rqst[num_rqst]); | |
307 | smb2_set_related(&rqst[num_rqst]); | |
308 | } | |
8de9e86c | 309 | } |
88a92c91 RS |
310 | if (rc) |
311 | goto finished; | |
8de9e86c | 312 | num_rqst++; |
8191576a | 313 | trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path); |
bb435512 RS |
314 | break; |
315 | case SMB2_OP_HARDLINK: | |
a7d5c294 | 316 | rqst[num_rqst].rq_iov = &vars->si_iov[0]; |
bb435512 RS |
317 | rqst[num_rqst].rq_nvec = 2; |
318 | ||
319 | len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); | |
320 | ||
a7d5c294 RS |
321 | vars->link_info.ReplaceIfExists = 0; |
322 | vars->link_info.RootDirectory = 0; | |
323 | vars->link_info.FileNameLength = cpu_to_le32(len); | |
bb435512 RS |
324 | |
325 | size[0] = sizeof(struct smb2_file_link_info); | |
a7d5c294 | 326 | data[0] = &vars->link_info; |
bb435512 RS |
327 | |
328 | size[1] = len + 2 /* null */; | |
329 | data[1] = (__le16 *)ptr; | |
330 | ||
352d96f3 AA |
331 | rc = SMB2_set_info_init(tcon, server, |
332 | &rqst[num_rqst], COMPOUND_FID, | |
bb435512 RS |
333 | COMPOUND_FID, current->tgid, |
334 | FILE_LINK_INFORMATION, | |
335 | SMB2_O_INFO_FILE, 0, data, size); | |
88a92c91 RS |
336 | if (rc) |
337 | goto finished; | |
e77fe73c | 338 | smb2_set_next_command(tcon, &rqst[num_rqst]); |
bb435512 | 339 | smb2_set_related(&rqst[num_rqst++]); |
8191576a | 340 | trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path); |
bb435512 | 341 | break; |
c5a5f38f RS |
342 | default: |
343 | cifs_dbg(VFS, "Invalid command\n"); | |
344 | rc = -EINVAL; | |
345 | } | |
346 | if (rc) | |
347 | goto finished; | |
348 | ||
8de9e86c RS |
349 | /* We already have a handle so we can skip the close */ |
350 | if (cfile) | |
351 | goto after_close; | |
c5a5f38f | 352 | /* Close */ |
04ad69c3 | 353 | flags |= CIFS_CP_CREATE_CLOSE_OP; |
a7d5c294 | 354 | rqst[num_rqst].rq_iov = &vars->close_iov[0]; |
c5a5f38f | 355 | rqst[num_rqst].rq_nvec = 1; |
352d96f3 AA |
356 | rc = SMB2_close_init(tcon, server, |
357 | &rqst[num_rqst], COMPOUND_FID, | |
43f8a6a7 | 358 | COMPOUND_FID, false); |
8de9e86c | 359 | smb2_set_related(&rqst[num_rqst]); |
c5a5f38f RS |
360 | if (rc) |
361 | goto finished; | |
8de9e86c RS |
362 | after_close: |
363 | num_rqst++; | |
364 | ||
365 | if (cfile) { | |
352d96f3 AA |
366 | rc = compound_send_recv(xid, ses, server, |
367 | flags, num_rqst - 2, | |
8de9e86c RS |
368 | &rqst[1], &resp_buftype[1], |
369 | &rsp_iov[1]); | |
370 | } else | |
352d96f3 AA |
371 | rc = compound_send_recv(xid, ses, server, |
372 | flags, num_rqst, | |
8de9e86c RS |
373 | rqst, resp_buftype, |
374 | rsp_iov); | |
c5a5f38f RS |
375 | |
376 | finished: | |
8de9e86c RS |
377 | if (cfile) |
378 | cifsFileInfo_put(cfile); | |
379 | ||
c5a5f38f | 380 | SMB2_open_free(&rqst[0]); |
7dcc82c2 | 381 | if (rc == -EREMCHG) { |
a0a3036b | 382 | pr_warn_once("server share %s deleted\n", tcon->treeName); |
7dcc82c2 SF |
383 | tcon->need_reconnect = true; |
384 | } | |
385 | ||
c5a5f38f RS |
386 | switch (command) { |
387 | case SMB2_OP_QUERY_INFO: | |
388 | if (rc == 0) { | |
c2e0fe3f RS |
389 | qi_rsp = (struct smb2_query_info_rsp *) |
390 | rsp_iov[1].iov_base; | |
c5a5f38f | 391 | rc = smb2_validate_and_copy_iov( |
c2e0fe3f RS |
392 | le16_to_cpu(qi_rsp->OutputBufferOffset), |
393 | le32_to_cpu(qi_rsp->OutputBufferLength), | |
c5a5f38f | 394 | &rsp_iov[1], sizeof(struct smb2_file_all_info), |
c2e0fe3f | 395 | ptr); |
c5a5f38f RS |
396 | } |
397 | if (rqst[1].rq_iov) | |
398 | SMB2_query_info_free(&rqst[1]); | |
399 | if (rqst[2].rq_iov) | |
400 | SMB2_close_free(&rqst[2]); | |
8191576a SF |
401 | if (rc) |
402 | trace_smb3_query_info_compound_err(xid, ses->Suid, | |
403 | tcon->tid, rc); | |
404 | else | |
405 | trace_smb3_query_info_compound_done(xid, ses->Suid, | |
406 | tcon->tid); | |
c5a5f38f | 407 | break; |
6a5f6592 SF |
408 | case SMB2_OP_POSIX_QUERY_INFO: |
409 | if (rc == 0) { | |
410 | qi_rsp = (struct smb2_query_info_rsp *) | |
411 | rsp_iov[1].iov_base; | |
412 | rc = smb2_validate_and_copy_iov( | |
413 | le16_to_cpu(qi_rsp->OutputBufferOffset), | |
414 | le32_to_cpu(qi_rsp->OutputBufferLength), | |
415 | &rsp_iov[1], sizeof(struct smb311_posix_qinfo) /* add SIDs */, ptr); | |
416 | } | |
417 | if (rqst[1].rq_iov) | |
418 | SMB2_query_info_free(&rqst[1]); | |
419 | if (rqst[2].rq_iov) | |
420 | SMB2_close_free(&rqst[2]); | |
421 | if (rc) | |
e4bd7c4a | 422 | trace_smb3_posix_query_info_compound_err(xid, ses->Suid, tcon->tid, rc); |
6a5f6592 | 423 | else |
e4bd7c4a | 424 | trace_smb3_posix_query_info_compound_done(xid, ses->Suid, tcon->tid); |
6a5f6592 | 425 | break; |
47dd9597 | 426 | case SMB2_OP_DELETE: |
8191576a SF |
427 | if (rc) |
428 | trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc); | |
429 | else | |
430 | trace_smb3_delete_done(xid, ses->Suid, tcon->tid); | |
431 | if (rqst[1].rq_iov) | |
432 | SMB2_close_free(&rqst[1]); | |
433 | break; | |
f733e393 | 434 | case SMB2_OP_MKDIR: |
8191576a SF |
435 | if (rc) |
436 | trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc); | |
437 | else | |
438 | trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid); | |
f733e393 RS |
439 | if (rqst[1].rq_iov) |
440 | SMB2_close_free(&rqst[1]); | |
441 | break; | |
bb435512 | 442 | case SMB2_OP_HARDLINK: |
8191576a SF |
443 | if (rc) |
444 | trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc); | |
445 | else | |
446 | trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid); | |
447 | free_set_inf_compound(rqst); | |
448 | break; | |
bb435512 | 449 | case SMB2_OP_RENAME: |
8191576a SF |
450 | if (rc) |
451 | trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc); | |
452 | else | |
453 | trace_smb3_rename_done(xid, ses->Suid, tcon->tid); | |
454 | free_set_inf_compound(rqst); | |
455 | break; | |
c2e0fe3f | 456 | case SMB2_OP_RMDIR: |
8191576a SF |
457 | if (rc) |
458 | trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc); | |
459 | else | |
460 | trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid); | |
461 | free_set_inf_compound(rqst); | |
462 | break; | |
dcbf9103 | 463 | case SMB2_OP_SET_EOF: |
8191576a SF |
464 | if (rc) |
465 | trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc); | |
466 | else | |
467 | trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid); | |
468 | free_set_inf_compound(rqst); | |
469 | break; | |
dcbf9103 | 470 | case SMB2_OP_SET_INFO: |
8191576a SF |
471 | if (rc) |
472 | trace_smb3_set_info_compound_err(xid, ses->Suid, | |
473 | tcon->tid, rc); | |
474 | else | |
475 | trace_smb3_set_info_compound_done(xid, ses->Suid, | |
476 | tcon->tid); | |
477 | free_set_inf_compound(rqst); | |
c2e0fe3f | 478 | break; |
c5a5f38f RS |
479 | } |
480 | free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); | |
481 | free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); | |
482 | free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); | |
a7d5c294 | 483 | kfree(vars); |
c5a5f38f RS |
484 | return rc; |
485 | } | |
486 | ||
f0df737e | 487 | void |
be4cb9e3 PS |
488 | move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src) |
489 | { | |
490 | memcpy(dst, src, (size_t)(&src->CurrentByteOffset) - (size_t)src); | |
491 | dst->CurrentByteOffset = src->CurrentByteOffset; | |
492 | dst->Mode = src->Mode; | |
493 | dst->AlignmentRequirement = src->AlignmentRequirement; | |
494 | dst->IndexNumber1 = 0; /* we don't use it */ | |
495 | } | |
496 | ||
497 | int | |
498 | smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, | |
499 | struct cifs_sb_info *cifs_sb, const char *full_path, | |
2e4564b3 | 500 | FILE_ALL_INFO *data, bool *adjust_tz, bool *reparse) |
be4cb9e3 PS |
501 | { |
502 | int rc; | |
503 | struct smb2_file_all_info *smb2_data; | |
61351d6d | 504 | __u32 create_options = 0; |
496902dc | 505 | struct cifsFileInfo *cfile; |
9e81e8ff | 506 | struct cached_fid *cfid = NULL; |
be4cb9e3 PS |
507 | |
508 | *adjust_tz = false; | |
2e4564b3 | 509 | *reparse = false; |
be4cb9e3 | 510 | |
1bbe4997 | 511 | smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2, |
be4cb9e3 PS |
512 | GFP_KERNEL); |
513 | if (smb2_data == NULL) | |
514 | return -ENOMEM; | |
6a9cbdd1 | 515 | |
c9fc5ca4 RS |
516 | if (strcmp(full_path, "")) |
517 | rc = -ENOENT; | |
518 | else | |
7eb59a98 | 519 | rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); |
6a9cbdd1 | 520 | /* If it is a root and its handle is cached then use it */ |
e6eb1950 | 521 | if (!rc) { |
dcb45fd7 | 522 | if (cfid->file_all_info_is_valid) { |
e6eb1950 | 523 | move_smb2_info_to_cifs(data, |
dcb45fd7 | 524 | &cfid->file_all_info); |
e6eb1950 RS |
525 | } else { |
526 | rc = SMB2_query_info(xid, tcon, | |
a63ec83c RS |
527 | cfid->fid.persistent_fid, |
528 | cfid->fid.volatile_fid, smb2_data); | |
e6eb1950 RS |
529 | if (!rc) |
530 | move_smb2_info_to_cifs(data, smb2_data); | |
b0f6df73 | 531 | } |
45c0f1aa | 532 | close_cached_dir(cfid); |
e6eb1950 | 533 | goto out; |
6a9cbdd1 PS |
534 | } |
535 | ||
496902dc | 536 | cifs_get_readable_path(tcon, full_path, &cfile); |
c5a5f38f | 537 | rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, |
61351d6d | 538 | FILE_READ_ATTRIBUTES, FILE_OPEN, create_options, |
c3ca78e2 | 539 | ACL_NO_MODE, smb2_data, SMB2_OP_QUERY_INFO, cfile); |
eb85d94b | 540 | if (rc == -EOPNOTSUPP) { |
2e4564b3 | 541 | *reparse = true; |
61351d6d SF |
542 | create_options |= OPEN_REPARSE_POINT; |
543 | ||
eb85d94b | 544 | /* Failed on a symbolic link - query a reparse point info */ |
b6f2a0f8 | 545 | cifs_get_readable_path(tcon, full_path, &cfile); |
c5a5f38f RS |
546 | rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, |
547 | FILE_READ_ATTRIBUTES, FILE_OPEN, | |
c3ca78e2 | 548 | create_options, ACL_NO_MODE, |
b6f2a0f8 | 549 | smb2_data, SMB2_OP_QUERY_INFO, cfile); |
eb85d94b | 550 | } |
be4cb9e3 PS |
551 | if (rc) |
552 | goto out; | |
553 | ||
554 | move_smb2_info_to_cifs(data, smb2_data); | |
555 | out: | |
556 | kfree(smb2_data); | |
557 | return rc; | |
558 | } | |
a0e73183 | 559 | |
6a5f6592 SF |
560 | |
561 | int | |
562 | smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, | |
563 | struct cifs_sb_info *cifs_sb, const char *full_path, | |
2e4564b3 | 564 | struct smb311_posix_qinfo *data, bool *adjust_tz, bool *reparse) |
6a5f6592 SF |
565 | { |
566 | int rc; | |
567 | __u32 create_options = 0; | |
568 | struct cifsFileInfo *cfile; | |
569 | struct smb311_posix_qinfo *smb2_data; | |
570 | ||
571 | *adjust_tz = false; | |
2e4564b3 | 572 | *reparse = false; |
6a5f6592 SF |
573 | |
574 | /* BB TODO: Make struct larger when add support for parsing owner SIDs */ | |
575 | smb2_data = kzalloc(sizeof(struct smb311_posix_qinfo), | |
576 | GFP_KERNEL); | |
577 | if (smb2_data == NULL) | |
578 | return -ENOMEM; | |
579 | ||
580 | /* | |
581 | * BB TODO: Add support for using the cached root handle. | |
582 | * Create SMB2_query_posix_info worker function to do non-compounded query | |
583 | * when we already have an open file handle for this. For now this is fast enough | |
584 | * (always using the compounded version). | |
585 | */ | |
586 | ||
587 | cifs_get_readable_path(tcon, full_path, &cfile); | |
588 | rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, | |
589 | FILE_READ_ATTRIBUTES, FILE_OPEN, create_options, | |
590 | ACL_NO_MODE, smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile); | |
591 | if (rc == -EOPNOTSUPP) { | |
592 | /* BB TODO: When support for special files added to Samba re-verify this path */ | |
2e4564b3 | 593 | *reparse = true; |
6a5f6592 SF |
594 | create_options |= OPEN_REPARSE_POINT; |
595 | ||
596 | /* Failed on a symbolic link - query a reparse point info */ | |
b6f2a0f8 | 597 | cifs_get_readable_path(tcon, full_path, &cfile); |
6a5f6592 SF |
598 | rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, |
599 | FILE_READ_ATTRIBUTES, FILE_OPEN, | |
600 | create_options, ACL_NO_MODE, | |
b6f2a0f8 | 601 | smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile); |
6a5f6592 SF |
602 | } |
603 | if (rc) | |
604 | goto out; | |
605 | ||
606 | /* TODO: will need to allow for the 2 SIDs when add support for getting owner UID/GID */ | |
607 | memcpy(data, smb2_data, sizeof(struct smb311_posix_qinfo)); | |
608 | ||
609 | out: | |
610 | kfree(smb2_data); | |
611 | return rc; | |
612 | } | |
613 | ||
a0e73183 | 614 | int |
c3ca78e2 SF |
615 | smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode, |
616 | struct cifs_tcon *tcon, const char *name, | |
a0e73183 PS |
617 | struct cifs_sb_info *cifs_sb) |
618 | { | |
f733e393 RS |
619 | return smb2_compound_op(xid, tcon, cifs_sb, name, |
620 | FILE_WRITE_ATTRIBUTES, FILE_CREATE, | |
c3ca78e2 SF |
621 | CREATE_NOT_FILE, mode, NULL, SMB2_OP_MKDIR, |
622 | NULL); | |
a0e73183 PS |
623 | } |
624 | ||
625 | void | |
626 | smb2_mkdir_setinfo(struct inode *inode, const char *name, | |
627 | struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, | |
628 | const unsigned int xid) | |
629 | { | |
630 | FILE_BASIC_INFO data; | |
631 | struct cifsInodeInfo *cifs_i; | |
dc9300a6 | 632 | struct cifsFileInfo *cfile; |
a0e73183 PS |
633 | u32 dosattrs; |
634 | int tmprc; | |
635 | ||
636 | memset(&data, 0, sizeof(data)); | |
637 | cifs_i = CIFS_I(inode); | |
638 | dosattrs = cifs_i->cifsAttrs | ATTR_READONLY; | |
639 | data.Attributes = cpu_to_le32(dosattrs); | |
86f740f2 | 640 | cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile); |
dcbf9103 RS |
641 | tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, |
642 | FILE_WRITE_ATTRIBUTES, FILE_CREATE, | |
c3ca78e2 SF |
643 | CREATE_NOT_FILE, ACL_NO_MODE, |
644 | &data, SMB2_OP_SET_INFO, cfile); | |
a0e73183 PS |
645 | if (tmprc == 0) |
646 | cifs_i->cifsAttrs = dosattrs; | |
647 | } | |
1a500f01 PS |
648 | |
649 | int | |
650 | smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, | |
651 | struct cifs_sb_info *cifs_sb) | |
652 | { | |
c2e0fe3f | 653 | return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, |
c3ca78e2 | 654 | CREATE_NOT_FILE, ACL_NO_MODE, |
8de9e86c | 655 | NULL, SMB2_OP_RMDIR, NULL); |
1a500f01 | 656 | } |
cbe6f439 PS |
657 | |
658 | int | |
659 | smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, | |
660 | struct cifs_sb_info *cifs_sb) | |
661 | { | |
47dd9597 RS |
662 | return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, |
663 | CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, | |
c3ca78e2 | 664 | ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL); |
cbe6f439 | 665 | } |
35143eb5 | 666 | |
568798cc PS |
667 | static int |
668 | smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, | |
669 | const char *from_name, const char *to_name, | |
8de9e86c RS |
670 | struct cifs_sb_info *cifs_sb, __u32 access, int command, |
671 | struct cifsFileInfo *cfile) | |
35143eb5 PS |
672 | { |
673 | __le16 *smb2_to_name = NULL; | |
674 | int rc; | |
675 | ||
676 | smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb); | |
677 | if (smb2_to_name == NULL) { | |
678 | rc = -ENOMEM; | |
679 | goto smb2_rename_path; | |
680 | } | |
bb435512 | 681 | rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, |
c3ca78e2 SF |
682 | FILE_OPEN, 0, ACL_NO_MODE, smb2_to_name, |
683 | command, cfile); | |
35143eb5 PS |
684 | smb2_rename_path: |
685 | kfree(smb2_to_name); | |
686 | return rc; | |
687 | } | |
568798cc PS |
688 | |
689 | int | |
690 | smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, | |
691 | const char *from_name, const char *to_name, | |
692 | struct cifs_sb_info *cifs_sb) | |
693 | { | |
8de9e86c RS |
694 | struct cifsFileInfo *cfile; |
695 | ||
86f740f2 | 696 | cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile); |
8de9e86c RS |
697 | |
698 | return smb2_set_path_attr(xid, tcon, from_name, to_name, | |
699 | cifs_sb, DELETE, SMB2_OP_RENAME, cfile); | |
568798cc PS |
700 | } |
701 | ||
702 | int | |
703 | smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, | |
704 | const char *from_name, const char *to_name, | |
705 | struct cifs_sb_info *cifs_sb) | |
706 | { | |
707 | return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, | |
8de9e86c RS |
708 | FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK, |
709 | NULL); | |
568798cc | 710 | } |
c839ff24 PS |
711 | |
712 | int | |
713 | smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, | |
714 | const char *full_path, __u64 size, | |
715 | struct cifs_sb_info *cifs_sb, bool set_alloc) | |
716 | { | |
717 | __le64 eof = cpu_to_le64(size); | |
b6f2a0f8 | 718 | struct cifsFileInfo *cfile; |
f7bfe04b | 719 | |
b6f2a0f8 | 720 | cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); |
f7bfe04b | 721 | return smb2_compound_op(xid, tcon, cifs_sb, full_path, |
c3ca78e2 | 722 | FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE, |
b6f2a0f8 | 723 | &eof, SMB2_OP_SET_EOF, cfile); |
c839ff24 | 724 | } |
1feeaac7 PS |
725 | |
726 | int | |
727 | smb2_set_file_info(struct inode *inode, const char *full_path, | |
728 | FILE_BASIC_INFO *buf, const unsigned int xid) | |
729 | { | |
730 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | |
731 | struct tcon_link *tlink; | |
b6f2a0f8 SP |
732 | struct cifs_tcon *tcon; |
733 | struct cifsFileInfo *cfile; | |
1feeaac7 PS |
734 | int rc; |
735 | ||
18dd8e1a | 736 | if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) && |
fd09b7d3 | 737 | (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) && |
18dd8e1a SF |
738 | (buf->Attributes == 0)) |
739 | return 0; /* would be a no op, no sense sending this */ | |
740 | ||
1feeaac7 PS |
741 | tlink = cifs_sb_tlink(cifs_sb); |
742 | if (IS_ERR(tlink)) | |
743 | return PTR_ERR(tlink); | |
b6f2a0f8 | 744 | tcon = tlink_tcon(tlink); |
18dd8e1a | 745 | |
b6f2a0f8 SP |
746 | cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); |
747 | rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, | |
c3ca78e2 | 748 | FILE_WRITE_ATTRIBUTES, FILE_OPEN, |
b6f2a0f8 | 749 | 0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile); |
1feeaac7 PS |
750 | cifs_put_tlink(tlink); |
751 | return rc; | |
752 | } |