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