source: trunk/server/source4/ntvfs/posix/pvfs_setfileinfo.c

Last change on this file was 745, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated trunk to 3.6.0

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