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