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