s4-pvfs: change the handling of access checking on create
[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         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, req, 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 = 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 */
554 static 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 */
607 static 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 */
634 NTSTATUS 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