source: branches/samba-3.5.x/source4/ntvfs/posix/pvfs_setfileinfo.c

Last change on this file was 414, checked in by Herwig Bauernfeind, 16 years ago

Samba 3.5.0: Initial import

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