Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : POSIX NTVFS backend - setfileinfo
5 :
6 : Copyright (C) Andrew Tridgell 2004
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "vfs_posix.h"
24 : #include "system/time.h"
25 : #include "librpc/gen_ndr/xattr.h"
26 :
27 :
28 : /*
29 : determine what access bits are needed for a call
30 : */
31 2627 : static uint32_t pvfs_setfileinfo_access(union smb_setfileinfo *info)
32 : {
33 0 : uint32_t needed;
34 :
35 2627 : switch (info->generic.level) {
36 287 : case RAW_SFILEINFO_EA_SET:
37 287 : needed = SEC_FILE_WRITE_EA;
38 287 : break;
39 :
40 339 : case RAW_SFILEINFO_DISPOSITION_INFO:
41 : case RAW_SFILEINFO_DISPOSITION_INFORMATION:
42 339 : needed = SEC_STD_DELETE;
43 339 : break;
44 :
45 552 : case RAW_SFILEINFO_END_OF_FILE_INFO:
46 552 : needed = SEC_FILE_WRITE_DATA;
47 552 : break;
48 :
49 11 : case RAW_SFILEINFO_POSITION_INFORMATION:
50 11 : needed = 0;
51 11 : break;
52 :
53 628 : case RAW_SFILEINFO_SEC_DESC:
54 628 : needed = 0;
55 628 : if (info->set_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
56 122 : needed |= SEC_STD_WRITE_OWNER;
57 : }
58 628 : if (info->set_secdesc.in.secinfo_flags & SECINFO_DACL) {
59 628 : needed |= SEC_STD_WRITE_DAC;
60 : }
61 628 : if (info->set_secdesc.in.secinfo_flags & SECINFO_SACL) {
62 35 : needed |= SEC_FLAG_SYSTEM_SECURITY;
63 : }
64 628 : break;
65 :
66 47 : case RAW_SFILEINFO_RENAME_INFORMATION:
67 : case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
68 47 : needed = SEC_STD_DELETE;
69 47 : break;
70 :
71 763 : default:
72 763 : needed = SEC_FILE_WRITE_ATTRIBUTE;
73 763 : break;
74 : }
75 :
76 2627 : return needed;
77 : }
78 :
79 : /*
80 : rename_information level for streams
81 : */
82 11 : static NTSTATUS pvfs_setfileinfo_rename_stream(struct pvfs_state *pvfs,
83 : struct ntvfs_request *req,
84 : struct pvfs_filename *name,
85 : int fd,
86 : DATA_BLOB *odb_locking_key,
87 : union smb_setfileinfo *info)
88 : {
89 0 : NTSTATUS status;
90 11 : struct odb_lock *lck = NULL;
91 :
92 : /* strangely, this gives a sharing violation, not invalid
93 : parameter */
94 11 : if (info->rename_information.in.new_name[0] != ':') {
95 1 : return NT_STATUS_SHARING_VIOLATION;
96 : }
97 :
98 10 : status = pvfs_access_check_simple(pvfs, req, name, SEC_FILE_WRITE_ATTRIBUTE);
99 10 : if (!NT_STATUS_IS_OK(status)) {
100 0 : return status;
101 : }
102 :
103 10 : lck = odb_lock(req, pvfs->odb_context, odb_locking_key);
104 10 : if (lck == NULL) {
105 0 : DEBUG(0,("Unable to lock opendb for can_stat\n"));
106 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
107 : }
108 :
109 :
110 10 : status = pvfs_stream_rename(pvfs, name, fd,
111 10 : info->rename_information.in.new_name+1,
112 10 : info->rename_information.in.overwrite);
113 10 : return status;
114 : }
115 :
116 : /*
117 : rename_information level
118 : */
119 46 : static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs,
120 : struct ntvfs_request *req,
121 : struct pvfs_filename *name,
122 : int fd,
123 : DATA_BLOB *odb_locking_key,
124 : union smb_setfileinfo *info)
125 : {
126 0 : NTSTATUS status;
127 0 : struct pvfs_filename *name2;
128 0 : char *new_name, *p;
129 46 : struct odb_lock *lck = NULL;
130 :
131 : /* renames are only allowed within a directory */
132 46 : if (strchr_m(info->rename_information.in.new_name, '\\') &&
133 6 : (req->ctx->protocol < PROTOCOL_SMB2_02)) {
134 2 : return NT_STATUS_NOT_SUPPORTED;
135 : }
136 :
137 : /* handle stream renames specially */
138 44 : if (name->stream_name) {
139 11 : return pvfs_setfileinfo_rename_stream(pvfs, req, name, fd,
140 : odb_locking_key, info);
141 : }
142 :
143 : /* w2k3 does not appear to allow relative rename. On SMB2, vista sends it sometimes,
144 : but I suspect it is just uninitialised memory */
145 33 : if (info->rename_information.in.root_fid != 0 &&
146 1 : (req->ctx->protocol < PROTOCOL_SMB2_02)) {
147 1 : return NT_STATUS_INVALID_PARAMETER;
148 : }
149 :
150 : /* construct the fully qualified windows name for the new file name */
151 32 : if (req->ctx->protocol >= PROTOCOL_SMB2_02) {
152 : /* SMB2 sends the full path of the new name */
153 3 : new_name = talloc_asprintf(req, "\\%s", info->rename_information.in.new_name);
154 : } else {
155 29 : new_name = talloc_strdup(req, name->original_name);
156 29 : if (new_name == NULL) {
157 0 : return NT_STATUS_NO_MEMORY;
158 : }
159 29 : p = strrchr_m(new_name, '\\');
160 29 : if (p == NULL) {
161 0 : return NT_STATUS_OBJECT_NAME_INVALID;
162 : } else {
163 29 : *p = 0;
164 : }
165 :
166 29 : new_name = talloc_asprintf(req, "%s\\%s", new_name,
167 : info->rename_information.in.new_name);
168 : }
169 32 : if (new_name == NULL) {
170 0 : return NT_STATUS_NO_MEMORY;
171 : }
172 :
173 : /* resolve the new name */
174 32 : status = pvfs_resolve_name(pvfs, req, new_name, 0, &name2);
175 32 : if (!NT_STATUS_IS_OK(status)) {
176 3 : return status;
177 : }
178 :
179 : /* if the destination exists, then check the rename is allowed */
180 29 : if (name2->exists) {
181 8 : if (strcmp(name2->full_name, name->full_name) == 0) {
182 : /* rename to same name is null-op */
183 2 : return NT_STATUS_OK;
184 : }
185 :
186 6 : if (!info->rename_information.in.overwrite) {
187 2 : return NT_STATUS_OBJECT_NAME_COLLISION;
188 : }
189 :
190 4 : status = pvfs_can_delete(pvfs, req, name2, NULL);
191 4 : if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) {
192 1 : return NT_STATUS_ACCESS_DENIED;
193 : }
194 3 : if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
195 1 : return NT_STATUS_ACCESS_DENIED;
196 : }
197 2 : if (!NT_STATUS_IS_OK(status)) {
198 0 : return status;
199 : }
200 : }
201 :
202 23 : status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
203 23 : if (!NT_STATUS_IS_OK(status)) {
204 0 : return status;
205 : }
206 :
207 23 : lck = odb_lock(req, pvfs->odb_context, odb_locking_key);
208 23 : if (lck == NULL) {
209 0 : DEBUG(0,("Unable to lock opendb for can_stat\n"));
210 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
211 : }
212 :
213 23 : status = pvfs_do_rename(pvfs, lck, name, name2->full_name);
214 23 : talloc_free(lck);
215 23 : NT_STATUS_NOT_OK_RETURN(status);
216 23 : if (NT_STATUS_IS_OK(status)) {
217 23 : name->full_name = talloc_steal(name, name2->full_name);
218 23 : name->original_name = talloc_steal(name, name2->original_name);
219 : }
220 :
221 23 : return NT_STATUS_OK;
222 : }
223 :
224 : /*
225 : add a single DOS EA
226 : */
227 66602 : NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs,
228 : struct pvfs_filename *name,
229 : int fd, uint16_t num_eas,
230 : struct ea_struct *eas)
231 : {
232 0 : struct xattr_DosEAs *ealist;
233 0 : int i, j;
234 0 : NTSTATUS status;
235 :
236 66602 : if (num_eas == 0) {
237 66306 : return NT_STATUS_OK;
238 : }
239 :
240 296 : if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
241 0 : return NT_STATUS_NOT_SUPPORTED;
242 : }
243 :
244 296 : ealist = talloc(name, struct xattr_DosEAs);
245 :
246 : /* load the current list */
247 296 : status = pvfs_doseas_load(pvfs, name, fd, ealist);
248 296 : if (!NT_STATUS_IS_OK(status)) {
249 1 : return status;
250 : }
251 :
252 886 : for (j=0;j<num_eas;j++) {
253 591 : struct ea_struct *ea = &eas[j];
254 : /* see if its already there */
255 897 : for (i=0;i<ealist->num_eas;i++) {
256 309 : if (strcasecmp_m(ealist->eas[i].name, ea->name.s) == 0) {
257 3 : ealist->eas[i].value = ea->value;
258 3 : break;
259 : }
260 : }
261 :
262 591 : if (i==ealist->num_eas) {
263 : /* add it */
264 588 : ealist->eas = talloc_realloc(ealist, ealist->eas,
265 : struct xattr_EA,
266 : ealist->num_eas+1);
267 588 : if (ealist->eas == NULL) {
268 0 : return NT_STATUS_NO_MEMORY;
269 : }
270 588 : ealist->eas[i].name = ea->name.s;
271 588 : ealist->eas[i].value = ea->value;
272 588 : ealist->num_eas++;
273 : }
274 : }
275 :
276 : /* pull out any null EAs */
277 892 : for (i=0;i<ealist->num_eas;i++) {
278 597 : if (ealist->eas[i].value.length == 0) {
279 2 : memmove(&ealist->eas[i],
280 2 : &ealist->eas[i+1],
281 2 : (ealist->num_eas-(i+1)) * sizeof(ealist->eas[i]));
282 2 : ealist->num_eas--;
283 2 : i--;
284 : }
285 : }
286 :
287 295 : status = pvfs_doseas_save(pvfs, name, fd, ealist);
288 295 : if (!NT_STATUS_IS_OK(status)) {
289 0 : return status;
290 : }
291 :
292 295 : notify_trigger(pvfs->notify_context,
293 : NOTIFY_ACTION_MODIFIED,
294 : FILE_NOTIFY_CHANGE_EA,
295 295 : name->full_name);
296 :
297 295 : name->dos.ea_size = 4;
298 890 : for (i=0;i<ealist->num_eas;i++) {
299 595 : name->dos.ea_size += 4 + strlen(ealist->eas[i].name)+1 +
300 595 : ealist->eas[i].value.length;
301 : }
302 :
303 : /* update the ea_size attrib */
304 295 : return pvfs_dosattrib_save(pvfs, name, fd);
305 : }
306 :
307 : /*
308 : set info on a open file
309 : */
310 2414 : NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
311 : struct ntvfs_request *req,
312 : union smb_setfileinfo *info)
313 : {
314 2414 : struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
315 : struct pvfs_state);
316 0 : struct pvfs_file *f;
317 0 : struct pvfs_file_handle *h;
318 0 : struct pvfs_filename newstats;
319 0 : NTSTATUS status;
320 0 : uint32_t access_needed;
321 2414 : uint32_t change_mask = 0;
322 :
323 2414 : f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
324 2414 : if (!f) {
325 0 : return NT_STATUS_INVALID_HANDLE;
326 : }
327 :
328 2414 : h = f->handle;
329 :
330 2414 : access_needed = pvfs_setfileinfo_access(info);
331 2414 : if ((f->access_mask & access_needed) != access_needed) {
332 266 : return NT_STATUS_ACCESS_DENIED;
333 : }
334 :
335 : /* update the file information */
336 2148 : status = pvfs_resolve_name_handle(pvfs, h);
337 2148 : if (!NT_STATUS_IS_OK(status)) {
338 0 : return status;
339 : }
340 :
341 : /* we take a copy of the current file stats, then update
342 : newstats in each of the elements below. At the end we
343 : compare, and make any changes needed */
344 2148 : newstats = *h->name;
345 :
346 2148 : switch (info->generic.level) {
347 0 : case RAW_SFILEINFO_SETATTR:
348 0 : if (!null_time(info->setattr.in.write_time)) {
349 0 : unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
350 : }
351 0 : if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
352 0 : newstats.dos.attrib = info->setattr.in.attrib;
353 : }
354 0 : break;
355 :
356 9 : case RAW_SFILEINFO_SETATTRE:
357 : case RAW_SFILEINFO_STANDARD:
358 9 : if (!null_time(info->setattre.in.create_time)) {
359 2 : unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
360 : }
361 9 : if (!null_time(info->setattre.in.access_time)) {
362 2 : unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
363 : }
364 9 : if (!null_time(info->setattre.in.write_time)) {
365 6 : unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
366 : }
367 9 : break;
368 :
369 283 : case RAW_SFILEINFO_EA_SET:
370 1277 : return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd,
371 283 : info->ea_set.in.num_eas,
372 : info->ea_set.in.eas);
373 :
374 524 : case RAW_SFILEINFO_BASIC_INFO:
375 : case RAW_SFILEINFO_BASIC_INFORMATION:
376 524 : if (!null_nttime(info->basic_info.in.create_time)) {
377 323 : newstats.dos.create_time = info->basic_info.in.create_time;
378 : }
379 524 : if (!null_nttime(info->basic_info.in.access_time)) {
380 322 : newstats.dos.access_time = info->basic_info.in.access_time;
381 : }
382 524 : if (!null_nttime(info->basic_info.in.write_time)) {
383 327 : newstats.dos.write_time = info->basic_info.in.write_time;
384 : }
385 524 : if (!null_nttime(info->basic_info.in.change_time)) {
386 43 : newstats.dos.change_time = info->basic_info.in.change_time;
387 : }
388 524 : if (info->basic_info.in.attrib != 0) {
389 113 : newstats.dos.attrib = info->basic_info.in.attrib;
390 : }
391 524 : break;
392 :
393 324 : case RAW_SFILEINFO_DISPOSITION_INFO:
394 : case RAW_SFILEINFO_DISPOSITION_INFORMATION:
395 324 : return pvfs_set_delete_on_close(pvfs, req, f,
396 324 : info->disposition_info.in.delete_on_close);
397 :
398 9 : case RAW_SFILEINFO_ALLOCATION_INFO:
399 : case RAW_SFILEINFO_ALLOCATION_INFORMATION:
400 9 : status = pvfs_break_level2_oplocks(f);
401 9 : NT_STATUS_NOT_OK_RETURN(status);
402 :
403 9 : newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
404 9 : if (newstats.dos.alloc_size < newstats.st.st_size) {
405 3 : newstats.st.st_size = newstats.dos.alloc_size;
406 : }
407 9 : newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
408 : newstats.dos.alloc_size);
409 9 : break;
410 :
411 316 : case RAW_SFILEINFO_END_OF_FILE_INFO:
412 : case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
413 316 : status = pvfs_break_level2_oplocks(f);
414 316 : NT_STATUS_NOT_OK_RETURN(status);
415 :
416 316 : newstats.st.st_size = info->end_of_file_info.in.size;
417 316 : break;
418 :
419 9 : case RAW_SFILEINFO_POSITION_INFORMATION:
420 9 : h->position = info->position_information.in.position;
421 9 : break;
422 :
423 3 : case RAW_SFILEINFO_FULL_EA_INFORMATION:
424 3 : return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd,
425 3 : info->full_ea_information.in.eas.num_eas,
426 : info->full_ea_information.in.eas.eas);
427 :
428 8 : case RAW_SFILEINFO_MODE_INFORMATION:
429 : /* this one is a puzzle */
430 8 : if (info->mode_information.in.mode != 0 &&
431 6 : info->mode_information.in.mode != 2 &&
432 4 : info->mode_information.in.mode != 4 &&
433 4 : info->mode_information.in.mode != 6) {
434 4 : return NT_STATUS_INVALID_PARAMETER;
435 : }
436 4 : h->mode = info->mode_information.in.mode;
437 4 : break;
438 :
439 35 : case RAW_SFILEINFO_RENAME_INFORMATION:
440 : case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
441 35 : return pvfs_setfileinfo_rename(pvfs, req, h->name, f->handle->fd,
442 : &h->odb_locking_key,
443 : info);
444 :
445 628 : case RAW_SFILEINFO_SEC_DESC:
446 628 : notify_trigger(pvfs->notify_context,
447 : NOTIFY_ACTION_MODIFIED,
448 : FILE_NOTIFY_CHANGE_SECURITY,
449 628 : h->name->full_name);
450 628 : return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info);
451 :
452 0 : default:
453 0 : return NT_STATUS_INVALID_LEVEL;
454 : }
455 :
456 : /* possibly change the file size */
457 871 : if (newstats.st.st_size != h->name->st.st_size) {
458 318 : if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
459 0 : return NT_STATUS_FILE_IS_A_DIRECTORY;
460 : }
461 318 : if (h->name->stream_name) {
462 3 : status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size);
463 3 : if (!NT_STATUS_IS_OK(status)) {
464 0 : return status;
465 : }
466 :
467 3 : change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
468 : } else {
469 0 : int ret;
470 315 : if (f->access_mask &
471 : (SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA)) {
472 315 : ret = ftruncate(h->fd, newstats.st.st_size);
473 : } else {
474 0 : ret = truncate(h->name->full_name, newstats.st.st_size);
475 : }
476 315 : if (ret == -1) {
477 0 : return pvfs_map_errno(pvfs, errno);
478 : }
479 315 : change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
480 : }
481 : }
482 :
483 : /* possibly change the file timestamps */
484 871 : if (newstats.dos.create_time != h->name->dos.create_time) {
485 322 : change_mask |= FILE_NOTIFY_CHANGE_CREATION;
486 : }
487 871 : if (newstats.dos.access_time != h->name->dos.access_time) {
488 321 : change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
489 : }
490 871 : if (newstats.dos.write_time != h->name->dos.write_time) {
491 330 : change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
492 : }
493 871 : if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
494 550 : (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
495 0 : struct timeval tv[2];
496 :
497 363 : nttime_to_timeval(&tv[0], newstats.dos.access_time);
498 363 : nttime_to_timeval(&tv[1], newstats.dos.write_time);
499 :
500 363 : if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
501 363 : if (utimes(h->name->full_name, tv) == -1) {
502 0 : DEBUG(0,("pvfs_setfileinfo: utimes() failed '%s' - %s\n",
503 : h->name->full_name, strerror(errno)));
504 0 : return pvfs_map_errno(pvfs, errno);
505 : }
506 : }
507 : }
508 871 : if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
509 0 : struct odb_lock *lck;
510 :
511 330 : lck = odb_lock(req, h->pvfs->odb_context, &h->odb_locking_key);
512 330 : if (lck == NULL) {
513 0 : DEBUG(0,("Unable to lock opendb for write time update\n"));
514 0 : return NT_STATUS_INTERNAL_ERROR;
515 : }
516 :
517 330 : status = odb_set_write_time(lck, newstats.dos.write_time, true);
518 330 : if (!NT_STATUS_IS_OK(status)) {
519 0 : DEBUG(0,("Unable to update write time: %s\n",
520 : nt_errstr(status)));
521 0 : talloc_free(lck);
522 0 : return status;
523 : }
524 :
525 330 : talloc_free(lck);
526 :
527 330 : h->write_time.update_forced = true;
528 330 : h->write_time.update_on_close = false;
529 330 : talloc_free(h->write_time.update_event);
530 330 : h->write_time.update_event = NULL;
531 : }
532 :
533 : /* possibly change the attribute */
534 871 : if (newstats.dos.attrib != h->name->dos.attrib) {
535 0 : mode_t mode;
536 107 : if ((newstats.dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
537 4 : !(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
538 2 : return NT_STATUS_INVALID_PARAMETER;
539 : }
540 105 : mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
541 105 : if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
542 86 : if (pvfs_sys_fchmod(pvfs, h->fd, mode, h->name->allow_override) == -1) {
543 0 : return pvfs_map_errno(pvfs, errno);
544 : }
545 : }
546 105 : change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
547 : }
548 :
549 869 : *h->name = newstats;
550 :
551 869 : notify_trigger(pvfs->notify_context,
552 : NOTIFY_ACTION_MODIFIED,
553 : change_mask,
554 869 : h->name->full_name);
555 :
556 869 : return pvfs_dosattrib_save(pvfs, h->name, h->fd);
557 : }
558 :
559 : /*
560 : retry an open after a sharing violation
561 : */
562 5 : static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r,
563 : struct ntvfs_module_context *ntvfs,
564 : struct ntvfs_request *req,
565 : void *_info,
566 : void *private_data,
567 : enum pvfs_wait_notice reason)
568 : {
569 5 : union smb_setfileinfo *info = talloc_get_type(_info,
570 : union smb_setfileinfo);
571 5 : NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
572 :
573 5 : talloc_free(r);
574 :
575 5 : switch (reason) {
576 0 : case PVFS_WAIT_CANCEL:
577 : /*TODO*/
578 0 : status = NT_STATUS_CANCELLED;
579 5 : break;
580 2 : case PVFS_WAIT_TIMEOUT:
581 : /* if it timed out, then give the failure
582 : immediately */
583 : /*TODO*/
584 2 : status = NT_STATUS_SHARING_VIOLATION;
585 2 : break;
586 3 : case PVFS_WAIT_EVENT:
587 :
588 : /* try the open again, which could trigger another retry setup
589 : if it wants to, so we have to unmark the async flag so we
590 : will know if it does a second async reply */
591 3 : req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
592 :
593 3 : status = pvfs_setpathinfo(ntvfs, req, info);
594 3 : if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
595 : /* the 2nd try also replied async, so we don't send
596 : the reply yet */
597 0 : return;
598 : }
599 :
600 : /* re-mark it async, just in case someone up the chain does
601 : paranoid checking */
602 3 : req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
603 3 : break;
604 : }
605 :
606 : /* send the reply up the chain */
607 5 : req->async_states->status = status;
608 5 : req->async_states->send_fn(req);
609 : }
610 :
611 : /*
612 : setup for a unlink retry after a sharing violation
613 : or a non granted oplock
614 : */
615 5 : static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs,
616 : struct ntvfs_request *req,
617 : union smb_setfileinfo *info,
618 : struct odb_lock *lck,
619 : NTSTATUS status)
620 : {
621 5 : struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
622 : struct pvfs_state);
623 0 : struct timeval end_time;
624 :
625 5 : if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
626 2 : end_time = timeval_add(&req->statistics.request_time,
627 : 0, pvfs->sharing_violation_delay);
628 3 : } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
629 3 : end_time = timeval_add(&req->statistics.request_time,
630 : pvfs->oplock_break_timeout, 0);
631 : } else {
632 0 : return NT_STATUS_INTERNAL_ERROR;
633 : }
634 :
635 5 : return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, info, NULL,
636 : pvfs_retry_setpathinfo);
637 : }
638 :
639 : /*
640 : set info on a pathname
641 : */
642 443 : NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
643 : struct ntvfs_request *req, union smb_setfileinfo *info)
644 : {
645 443 : struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
646 : struct pvfs_state);
647 0 : struct pvfs_filename *name;
648 0 : struct pvfs_filename newstats;
649 0 : NTSTATUS status;
650 0 : uint32_t access_needed;
651 443 : uint32_t change_mask = 0;
652 443 : struct odb_lock *lck = NULL;
653 0 : DATA_BLOB odb_locking_key;
654 :
655 : /* resolve the cifs name to a posix name */
656 443 : status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path,
657 : PVFS_RESOLVE_STREAMS, &name);
658 443 : if (!NT_STATUS_IS_OK(status)) {
659 64 : return status;
660 : }
661 :
662 379 : if (!name->exists) {
663 166 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
664 : }
665 :
666 213 : access_needed = pvfs_setfileinfo_access(info);
667 213 : status = pvfs_access_check_simple(pvfs, req, name, access_needed);
668 213 : if (!NT_STATUS_IS_OK(status)) {
669 0 : return status;
670 : }
671 :
672 : /* we take a copy of the current file stats, then update
673 : newstats in each of the elements below. At the end we
674 : compare, and make any changes needed */
675 213 : newstats = *name;
676 :
677 213 : switch (info->generic.level) {
678 151 : case RAW_SFILEINFO_SETATTR:
679 151 : if (!null_time(info->setattr.in.write_time)) {
680 36 : unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
681 : }
682 151 : if (info->setattr.in.attrib == 0) {
683 77 : newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL;
684 74 : } else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
685 41 : newstats.dos.attrib = info->setattr.in.attrib;
686 : }
687 151 : break;
688 :
689 2 : case RAW_SFILEINFO_SETATTRE:
690 : case RAW_SFILEINFO_STANDARD:
691 2 : if (!null_time(info->setattre.in.create_time)) {
692 0 : unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
693 : }
694 2 : if (!null_time(info->setattre.in.access_time)) {
695 0 : unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
696 : }
697 2 : if (!null_time(info->setattre.in.write_time)) {
698 0 : unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
699 : }
700 2 : break;
701 :
702 4 : case RAW_SFILEINFO_EA_SET:
703 30 : return pvfs_setfileinfo_ea_set(pvfs, name, -1,
704 4 : info->ea_set.in.num_eas,
705 : info->ea_set.in.eas);
706 :
707 22 : case RAW_SFILEINFO_BASIC_INFO:
708 : case RAW_SFILEINFO_BASIC_INFORMATION:
709 22 : if (!null_nttime(info->basic_info.in.create_time)) {
710 1 : newstats.dos.create_time = info->basic_info.in.create_time;
711 : }
712 22 : if (!null_nttime(info->basic_info.in.access_time)) {
713 1 : newstats.dos.access_time = info->basic_info.in.access_time;
714 : }
715 22 : if (!null_nttime(info->basic_info.in.write_time)) {
716 1 : newstats.dos.write_time = info->basic_info.in.write_time;
717 : }
718 22 : if (!null_nttime(info->basic_info.in.change_time)) {
719 1 : newstats.dos.change_time = info->basic_info.in.change_time;
720 : }
721 22 : if (info->basic_info.in.attrib != 0) {
722 19 : newstats.dos.attrib = info->basic_info.in.attrib;
723 : }
724 22 : break;
725 :
726 4 : case RAW_SFILEINFO_ALLOCATION_INFO:
727 : case RAW_SFILEINFO_ALLOCATION_INFORMATION:
728 4 : status = pvfs_can_update_file_size(pvfs, req, name, &lck);
729 : /*
730 : * on a sharing violation we need to retry when the file is closed by
731 : * the other user, or after 1 second
732 : * on a non granted oplock we need to retry when the file is closed by
733 : * the other user, or after 30 seconds
734 : */
735 4 : if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
736 4 : NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
737 1 : (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
738 1 : return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
739 : }
740 3 : NT_STATUS_NOT_OK_RETURN(status);
741 :
742 3 : if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
743 : /* strange. Increasing the allocation size via setpathinfo
744 : should be silently ignored */
745 2 : break;
746 : }
747 1 : newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
748 1 : if (newstats.dos.alloc_size < newstats.st.st_size) {
749 1 : newstats.st.st_size = newstats.dos.alloc_size;
750 : }
751 1 : newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
752 : newstats.dos.alloc_size);
753 1 : break;
754 :
755 9 : case RAW_SFILEINFO_END_OF_FILE_INFO:
756 : case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
757 9 : status = pvfs_can_update_file_size(pvfs, req, name, &lck);
758 : /*
759 : * on a sharing violation we need to retry when the file is closed by
760 : * the other user, or after 1 second
761 : * on a non granted oplock we need to retry when the file is closed by
762 : * the other user, or after 30 seconds
763 : */
764 9 : if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
765 7 : NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
766 4 : (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
767 4 : return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
768 : }
769 5 : NT_STATUS_NOT_OK_RETURN(status);
770 :
771 5 : newstats.st.st_size = info->end_of_file_info.in.size;
772 5 : break;
773 :
774 3 : case RAW_SFILEINFO_MODE_INFORMATION:
775 3 : if (info->mode_information.in.mode != 0 &&
776 2 : info->mode_information.in.mode != 2 &&
777 1 : info->mode_information.in.mode != 4 &&
778 1 : info->mode_information.in.mode != 6) {
779 1 : return NT_STATUS_INVALID_PARAMETER;
780 : }
781 2 : return NT_STATUS_OK;
782 :
783 11 : case RAW_SFILEINFO_RENAME_INFORMATION:
784 : case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
785 11 : status = pvfs_locking_key(name, name, &odb_locking_key);
786 11 : NT_STATUS_NOT_OK_RETURN(status);
787 11 : status = pvfs_setfileinfo_rename(pvfs, req, name, -1,
788 : &odb_locking_key, info);
789 11 : NT_STATUS_NOT_OK_RETURN(status);
790 10 : return NT_STATUS_OK;
791 :
792 7 : case RAW_SFILEINFO_DISPOSITION_INFO:
793 : case RAW_SFILEINFO_DISPOSITION_INFORMATION:
794 : case RAW_SFILEINFO_POSITION_INFORMATION:
795 7 : return NT_STATUS_OK;
796 :
797 0 : default:
798 0 : return NT_STATUS_INVALID_LEVEL;
799 : }
800 :
801 : /* possibly change the file size */
802 183 : if (newstats.st.st_size != name->st.st_size) {
803 6 : if (name->stream_name) {
804 0 : status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size);
805 0 : if (!NT_STATUS_IS_OK(status)) {
806 0 : return status;
807 : }
808 6 : } else if (truncate(name->full_name, newstats.st.st_size) == -1) {
809 0 : return pvfs_map_errno(pvfs, errno);
810 : }
811 6 : change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
812 : }
813 :
814 : /* possibly change the file timestamps */
815 183 : if (newstats.dos.create_time != name->dos.create_time) {
816 1 : change_mask |= FILE_NOTIFY_CHANGE_CREATION;
817 : }
818 183 : if (newstats.dos.access_time != name->dos.access_time) {
819 1 : change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
820 : }
821 183 : if (newstats.dos.write_time != name->dos.write_time) {
822 37 : change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
823 : }
824 183 : if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
825 182 : (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
826 0 : struct timeval tv[2];
827 :
828 37 : nttime_to_timeval(&tv[0], newstats.dos.access_time);
829 37 : nttime_to_timeval(&tv[1], newstats.dos.write_time);
830 :
831 37 : if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
832 37 : if (utimes(name->full_name, tv) == -1) {
833 0 : DEBUG(0,("pvfs_setpathinfo: utimes() failed '%s' - %s\n",
834 : name->full_name, strerror(errno)));
835 0 : return pvfs_map_errno(pvfs, errno);
836 : }
837 : }
838 : }
839 183 : if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
840 37 : if (lck == NULL) {
841 0 : DATA_BLOB lkey;
842 37 : status = pvfs_locking_key(name, name, &lkey);
843 37 : NT_STATUS_NOT_OK_RETURN(status);
844 :
845 37 : lck = odb_lock(req, pvfs->odb_context, &lkey);
846 37 : data_blob_free(&lkey);
847 37 : if (lck == NULL) {
848 0 : DEBUG(0,("Unable to lock opendb for write time update\n"));
849 0 : return NT_STATUS_INTERNAL_ERROR;
850 : }
851 : }
852 :
853 37 : status = odb_set_write_time(lck, newstats.dos.write_time, true);
854 37 : if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
855 : /* it could be that nobody has opened the file */
856 0 : } else if (!NT_STATUS_IS_OK(status)) {
857 0 : DEBUG(0,("Unable to update write time: %s\n",
858 : nt_errstr(status)));
859 0 : return status;
860 : }
861 : }
862 :
863 : /* possibly change the attribute */
864 183 : newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
865 183 : if (newstats.dos.attrib != name->dos.attrib) {
866 135 : mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
867 135 : if (pvfs_sys_chmod(pvfs, name->full_name, mode, name->allow_override) == -1) {
868 0 : return pvfs_map_errno(pvfs, errno);
869 : }
870 135 : change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
871 : }
872 :
873 183 : *name = newstats;
874 :
875 183 : if (change_mask != 0) {
876 174 : notify_trigger(pvfs->notify_context,
877 : NOTIFY_ACTION_MODIFIED,
878 : change_mask,
879 174 : name->full_name);
880 : }
881 :
882 183 : return pvfs_dosattrib_save(pvfs, name, -1);
883 : }
884 :
|