d2604485d45104a1dc1886ba5944d7a7bb0df736
[gd/samba-autobuild/.git] / source4 / ntvfs / posix / pvfs_setfileinfo.c
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 static 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 */
82 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         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 */
116 static 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 */
224 NTSTATUS 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 */
307 NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
308                           struct ntvfs_request *req, 
309                           union smb_setfileinfo *info)
310 {
311         struct pvfs_state *pvfs = ntvfs->private_data;
312         struct pvfs_file *f;
313         struct pvfs_file_handle *h;
314         struct pvfs_filename newstats;
315         NTSTATUS status;
316         uint32_t access_needed;
317         uint32_t change_mask = 0;
318
319         f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
320         if (!f) {
321                 return NT_STATUS_INVALID_HANDLE;
322         }
323
324         h = f->handle;
325
326         access_needed = pvfs_setfileinfo_access(info);
327         if ((f->access_mask & access_needed) != access_needed) {
328                 return NT_STATUS_ACCESS_DENIED;
329         }
330
331         /* update the file information */
332         status = pvfs_resolve_name_handle(pvfs, h);
333         if (!NT_STATUS_IS_OK(status)) {
334                 return status;
335         }
336
337         /* we take a copy of the current file stats, then update
338            newstats in each of the elements below. At the end we
339            compare, and make any changes needed */
340         newstats = *h->name;
341
342         switch (info->generic.level) {
343         case RAW_SFILEINFO_SETATTR:
344                 if (!null_time(info->setattr.in.write_time)) {
345                         unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
346                 }
347                 if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
348                         newstats.dos.attrib = info->setattr.in.attrib;
349                 }
350                 break;
351
352         case RAW_SFILEINFO_SETATTRE:
353         case RAW_SFILEINFO_STANDARD:
354                 if (!null_time(info->setattre.in.create_time)) {
355                         unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
356                 }
357                 if (!null_time(info->setattre.in.access_time)) {
358                         unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
359                 }
360                 if (!null_time(info->setattre.in.write_time)) {
361                         unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
362                 }
363                 break;
364
365         case RAW_SFILEINFO_EA_SET:
366                 return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd, 
367                                                info->ea_set.in.num_eas,
368                                                info->ea_set.in.eas);
369
370         case RAW_SFILEINFO_BASIC_INFO:
371         case RAW_SFILEINFO_BASIC_INFORMATION:
372                 if (!null_nttime(info->basic_info.in.create_time)) {
373                         newstats.dos.create_time = info->basic_info.in.create_time;
374                 }
375                 if (!null_nttime(info->basic_info.in.access_time)) {
376                         newstats.dos.access_time = info->basic_info.in.access_time;
377                 }
378                 if (!null_nttime(info->basic_info.in.write_time)) {
379                         newstats.dos.write_time = info->basic_info.in.write_time;
380                 }
381                 if (!null_nttime(info->basic_info.in.change_time)) {
382                         newstats.dos.change_time = info->basic_info.in.change_time;
383                 }
384                 if (info->basic_info.in.attrib != 0) {
385                         newstats.dos.attrib = info->basic_info.in.attrib;
386                 }
387                 break;
388
389         case RAW_SFILEINFO_DISPOSITION_INFO:
390         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
391                 return pvfs_set_delete_on_close(pvfs, req, f, 
392                                                 info->disposition_info.in.delete_on_close);
393
394         case RAW_SFILEINFO_ALLOCATION_INFO:
395         case RAW_SFILEINFO_ALLOCATION_INFORMATION:
396                 status = pvfs_break_level2_oplocks(f);
397                 NT_STATUS_NOT_OK_RETURN(status);
398
399                 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
400                 if (newstats.dos.alloc_size < newstats.st.st_size) {
401                         newstats.st.st_size = newstats.dos.alloc_size;
402                 }
403                 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs, 
404                                                                 newstats.dos.alloc_size);
405                 break;
406
407         case RAW_SFILEINFO_END_OF_FILE_INFO:
408         case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
409                 status = pvfs_break_level2_oplocks(f);
410                 NT_STATUS_NOT_OK_RETURN(status);
411
412                 newstats.st.st_size = info->end_of_file_info.in.size;
413                 break;
414
415         case RAW_SFILEINFO_POSITION_INFORMATION:
416                 h->position = info->position_information.in.position;
417                 break;
418
419         case RAW_SFILEINFO_MODE_INFORMATION:
420                 /* this one is a puzzle */
421                 if (info->mode_information.in.mode != 0 &&
422                     info->mode_information.in.mode != 2 &&
423                     info->mode_information.in.mode != 4 &&
424                     info->mode_information.in.mode != 6) {
425                         return NT_STATUS_INVALID_PARAMETER;
426                 }
427                 h->mode = info->mode_information.in.mode;
428                 break;
429
430         case RAW_SFILEINFO_RENAME_INFORMATION:
431         case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
432                 return pvfs_setfileinfo_rename(pvfs, req, h->name, f->handle->fd,
433                                                &h->odb_locking_key,
434                                                info);
435
436         case RAW_SFILEINFO_SEC_DESC:
437                 notify_trigger(pvfs->notify_context, 
438                                NOTIFY_ACTION_MODIFIED, 
439                                FILE_NOTIFY_CHANGE_SECURITY,
440                                h->name->full_name);
441                 return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info);
442
443         default:
444                 return NT_STATUS_INVALID_LEVEL;
445         }
446
447         /* possibly change the file size */
448         if (newstats.st.st_size != h->name->st.st_size) {
449                 if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
450                         return NT_STATUS_FILE_IS_A_DIRECTORY;
451                 }
452                 if (h->name->stream_name) {
453                         status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size);
454                         if (!NT_STATUS_IS_OK(status)) {
455                                 return status;
456                         }
457                         
458                         change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
459                 } else {
460                         int ret;
461                         if (f->access_mask & 
462                             (SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA)) {
463                                 ret = ftruncate(h->fd, newstats.st.st_size);
464                         } else {
465                                 ret = truncate(h->name->full_name, newstats.st.st_size);
466                         }
467                         if (ret == -1) {
468                                 return pvfs_map_errno(pvfs, errno);
469                         }
470                         change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
471                 }
472         }
473
474         /* possibly change the file timestamps */
475         if (newstats.dos.create_time != h->name->dos.create_time) {
476                 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
477         }
478         if (newstats.dos.access_time != h->name->dos.access_time) {
479                 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
480         }
481         if (newstats.dos.write_time != h->name->dos.write_time) {
482                 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
483         }
484         if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
485             (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
486                 struct timeval tv[2];
487
488                 nttime_to_timeval(&tv[0], newstats.dos.access_time);
489                 nttime_to_timeval(&tv[1], newstats.dos.write_time);
490
491                 if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
492                         if (utimes(h->name->full_name, tv) == -1) {
493                                 DEBUG(0,("pvfs_setfileinfo: utimes() failed '%s' - %s\n",
494                                          h->name->full_name, strerror(errno)));
495                                 return pvfs_map_errno(pvfs, errno);
496                         }
497                 }
498         }
499         if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
500                 struct odb_lock *lck;
501
502                 lck = odb_lock(req, h->pvfs->odb_context, &h->odb_locking_key);
503                 if (lck == NULL) {
504                         DEBUG(0,("Unable to lock opendb for write time update\n"));
505                         return NT_STATUS_INTERNAL_ERROR;
506                 }
507
508                 status = odb_set_write_time(lck, newstats.dos.write_time, true);
509                 if (!NT_STATUS_IS_OK(status)) {
510                         DEBUG(0,("Unable to update write time: %s\n",
511                                 nt_errstr(status)));
512                         talloc_free(lck);
513                         return status;
514                 }
515
516                 talloc_free(lck);
517
518                 h->write_time.update_forced = true;
519                 h->write_time.update_on_close = false;
520                 talloc_free(h->write_time.update_event);
521                 h->write_time.update_event = NULL;
522         }
523
524         /* possibly change the attribute */
525         if (newstats.dos.attrib != h->name->dos.attrib) {
526                 mode_t mode;
527                 if ((newstats.dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
528                     !(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
529                         return NT_STATUS_INVALID_PARAMETER;
530                 }
531                 mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
532                 if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
533                         if (fchmod(h->fd, mode) == -1) {
534                                 return pvfs_map_errno(pvfs, errno);
535                         }
536                 }
537                 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
538         }
539
540         *h->name = newstats;
541
542         notify_trigger(pvfs->notify_context, 
543                        NOTIFY_ACTION_MODIFIED, 
544                        change_mask,
545                        h->name->full_name);
546
547         return pvfs_dosattrib_save(pvfs, h->name, h->fd);
548 }
549
550 /*
551   retry an open after a sharing violation
552 */
553 static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r,
554                                    struct ntvfs_module_context *ntvfs,
555                                    struct ntvfs_request *req,
556                                    void *_info,
557                                    void *private_data,
558                                    enum pvfs_wait_notice reason)
559 {
560         union smb_setfileinfo *info = talloc_get_type(_info,
561                                       union smb_setfileinfo);
562         NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
563
564         talloc_free(r);
565
566         switch (reason) {
567         case PVFS_WAIT_CANCEL:
568 /*TODO*/
569                 status = NT_STATUS_CANCELLED;
570                 break;
571         case PVFS_WAIT_TIMEOUT:
572                 /* if it timed out, then give the failure
573                    immediately */
574 /*TODO*/
575                 status = NT_STATUS_SHARING_VIOLATION;
576                 break;
577         case PVFS_WAIT_EVENT:
578
579                 /* try the open again, which could trigger another retry setup
580                    if it wants to, so we have to unmark the async flag so we
581                    will know if it does a second async reply */
582                 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
583
584                 status = pvfs_setpathinfo(ntvfs, req, info);
585                 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
586                         /* the 2nd try also replied async, so we don't send
587                            the reply yet */
588                         return;
589                 }
590
591                 /* re-mark it async, just in case someone up the chain does
592                    paranoid checking */
593                 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
594                 break;
595         }
596
597         /* send the reply up the chain */
598         req->async_states->status = status;
599         req->async_states->send_fn(req);
600 }
601
602 /*
603   setup for a unlink retry after a sharing violation
604   or a non granted oplock
605 */
606 static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs,
607                                              struct ntvfs_request *req,
608                                              union smb_setfileinfo *info,
609                                              struct odb_lock *lck,
610                                              NTSTATUS status)
611 {
612         struct pvfs_state *pvfs = ntvfs->private_data;
613         struct timeval end_time;
614
615         if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
616                 end_time = timeval_add(&req->statistics.request_time,
617                                        0, pvfs->sharing_violation_delay);
618         } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
619                 end_time = timeval_add(&req->statistics.request_time,
620                                        pvfs->oplock_break_timeout, 0);
621         } else {
622                 return NT_STATUS_INTERNAL_ERROR;
623         }
624
625         return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, info, NULL,
626                                     pvfs_retry_setpathinfo);
627 }
628
629 /*
630   set info on a pathname
631 */
632 NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
633                           struct ntvfs_request *req, union smb_setfileinfo *info)
634 {
635         struct pvfs_state *pvfs = ntvfs->private_data;
636         struct pvfs_filename *name;
637         struct pvfs_filename newstats;
638         NTSTATUS status;
639         uint32_t access_needed;
640         uint32_t change_mask = 0;
641         struct odb_lock *lck = NULL;
642         DATA_BLOB odb_locking_key;
643
644         /* resolve the cifs name to a posix name */
645         status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path, 
646                                    PVFS_RESOLVE_STREAMS, &name);
647         if (!NT_STATUS_IS_OK(status)) {
648                 return status;
649         }
650
651         if (!name->exists) {
652                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
653         }
654
655         access_needed = pvfs_setfileinfo_access(info);
656         status = pvfs_access_check_simple(pvfs, req, name, access_needed);
657         if (!NT_STATUS_IS_OK(status)) {
658                 return status;
659         }
660
661         /* we take a copy of the current file stats, then update
662            newstats in each of the elements below. At the end we
663            compare, and make any changes needed */
664         newstats = *name;
665
666         switch (info->generic.level) {
667         case RAW_SFILEINFO_SETATTR:
668                 if (!null_time(info->setattr.in.write_time)) {
669                         unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
670                 }
671                 if (info->setattr.in.attrib == 0) {
672                         newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL;
673                 } else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
674                         newstats.dos.attrib = info->setattr.in.attrib;
675                 }
676                 break;
677
678         case RAW_SFILEINFO_SETATTRE:
679         case RAW_SFILEINFO_STANDARD:
680                 if (!null_time(info->setattre.in.create_time)) {
681                         unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
682                 }
683                 if (!null_time(info->setattre.in.access_time)) {
684                         unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
685                 }
686                 if (!null_time(info->setattre.in.write_time)) {
687                         unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
688                 }
689                 break;
690
691         case RAW_SFILEINFO_EA_SET:
692                 return pvfs_setfileinfo_ea_set(pvfs, name, -1, 
693                                                info->ea_set.in.num_eas,
694                                                info->ea_set.in.eas);
695
696         case RAW_SFILEINFO_BASIC_INFO:
697         case RAW_SFILEINFO_BASIC_INFORMATION:
698                 if (!null_nttime(info->basic_info.in.create_time)) {
699                         newstats.dos.create_time = info->basic_info.in.create_time;
700                 }
701                 if (!null_nttime(info->basic_info.in.access_time)) {
702                         newstats.dos.access_time = info->basic_info.in.access_time;
703                 }
704                 if (!null_nttime(info->basic_info.in.write_time)) {
705                         newstats.dos.write_time = info->basic_info.in.write_time;
706                 }
707                 if (!null_nttime(info->basic_info.in.change_time)) {
708                         newstats.dos.change_time = info->basic_info.in.change_time;
709                 }
710                 if (info->basic_info.in.attrib != 0) {
711                         newstats.dos.attrib = info->basic_info.in.attrib;
712                 }
713                 break;
714
715         case RAW_SFILEINFO_ALLOCATION_INFO:
716         case RAW_SFILEINFO_ALLOCATION_INFORMATION:
717                 status = pvfs_can_update_file_size(pvfs, req, name, &lck);
718                 /*
719                  * on a sharing violation we need to retry when the file is closed by
720                  * the other user, or after 1 second
721                  * on a non granted oplock we need to retry when the file is closed by
722                  * the other user, or after 30 seconds
723                 */
724                 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
725                      NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
726                     (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
727                         return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
728                 }
729                 NT_STATUS_NOT_OK_RETURN(status);
730
731                 if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
732                         /* strange. Increasing the allocation size via setpathinfo 
733                            should be silently ignored */
734                         break;
735                 }
736                 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
737                 if (newstats.dos.alloc_size < newstats.st.st_size) {
738                         newstats.st.st_size = newstats.dos.alloc_size;
739                 }
740                 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs, 
741                                                                 newstats.dos.alloc_size);
742                 break;
743
744         case RAW_SFILEINFO_END_OF_FILE_INFO:
745         case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
746                 status = pvfs_can_update_file_size(pvfs, req, name, &lck);
747                 /*
748                  * on a sharing violation we need to retry when the file is closed by
749                  * the other user, or after 1 second
750                  * on a non granted oplock we need to retry when the file is closed by
751                  * the other user, or after 30 seconds
752                 */
753                 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
754                      NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
755                     (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
756                         return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
757                 }
758                 NT_STATUS_NOT_OK_RETURN(status);
759
760                 newstats.st.st_size = info->end_of_file_info.in.size;
761                 break;
762
763         case RAW_SFILEINFO_MODE_INFORMATION:
764                 if (info->mode_information.in.mode != 0 &&
765                     info->mode_information.in.mode != 2 &&
766                     info->mode_information.in.mode != 4 &&
767                     info->mode_information.in.mode != 6) {
768                         return NT_STATUS_INVALID_PARAMETER;
769                 }
770                 return NT_STATUS_OK;
771
772         case RAW_SFILEINFO_RENAME_INFORMATION:
773         case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
774                 status = pvfs_locking_key(name, name, &odb_locking_key);
775                 NT_STATUS_NOT_OK_RETURN(status);
776                 status = pvfs_setfileinfo_rename(pvfs, req, name, -1,
777                                                  &odb_locking_key, info);
778                 NT_STATUS_NOT_OK_RETURN(status);
779                 return NT_STATUS_OK;
780
781         case RAW_SFILEINFO_DISPOSITION_INFO:
782         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
783         case RAW_SFILEINFO_POSITION_INFORMATION:
784                 return NT_STATUS_OK;
785
786         default:
787                 return NT_STATUS_INVALID_LEVEL;
788         }
789
790         /* possibly change the file size */
791         if (newstats.st.st_size != name->st.st_size) {
792                 if (name->stream_name) {
793                         status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size);
794                         if (!NT_STATUS_IS_OK(status)) {
795                                 return status;
796                         }
797                 } else if (truncate(name->full_name, newstats.st.st_size) == -1) {
798                         return pvfs_map_errno(pvfs, errno);
799                 }
800                 change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
801         }
802
803         /* possibly change the file timestamps */
804         if (newstats.dos.create_time != name->dos.create_time) {
805                 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
806         }
807         if (newstats.dos.access_time != name->dos.access_time) {
808                 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
809         }
810         if (newstats.dos.write_time != name->dos.write_time) {
811                 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
812         }
813         if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
814             (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
815                 struct timeval tv[2];
816
817                 nttime_to_timeval(&tv[0], newstats.dos.access_time);
818                 nttime_to_timeval(&tv[1], newstats.dos.write_time);
819
820                 if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
821                         if (utimes(name->full_name, tv) == -1) {
822                                 DEBUG(0,("pvfs_setpathinfo: utimes() failed '%s' - %s\n",
823                                          name->full_name, strerror(errno)));
824                                 return pvfs_map_errno(pvfs, errno);
825                         }
826                 }
827         }
828         if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
829                 if (lck == NULL) {
830                         DATA_BLOB lkey;
831                         status = pvfs_locking_key(name, name, &lkey);
832                         NT_STATUS_NOT_OK_RETURN(status);
833
834                         lck = odb_lock(req, pvfs->odb_context, &lkey);
835                         data_blob_free(&lkey);
836                         if (lck == NULL) {
837                                 DEBUG(0,("Unable to lock opendb for write time update\n"));
838                                 return NT_STATUS_INTERNAL_ERROR;
839                         }
840                 }
841
842                 status = odb_set_write_time(lck, newstats.dos.write_time, true);
843                 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
844                         /* it could be that nobody has opened the file */
845                 } else if (!NT_STATUS_IS_OK(status)) {
846                         DEBUG(0,("Unable to update write time: %s\n",
847                                 nt_errstr(status)));
848                         return status;
849                 }
850         }
851
852         /* possibly change the attribute */
853         newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
854         if (newstats.dos.attrib != name->dos.attrib) {
855                 mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
856                 if (chmod(name->full_name, mode) == -1) {
857                         return pvfs_map_errno(pvfs, errno);
858                 }
859                 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
860         }
861
862         *name = newstats;
863
864         if (change_mask != 0) {
865                 notify_trigger(pvfs->notify_context, 
866                                NOTIFY_ACTION_MODIFIED, 
867                                change_mask,
868                                name->full_name);
869         }
870
871         return pvfs_dosattrib_save(pvfs, name, -1);
872 }
873