VFS: Modify chmod to take a const struct smb_filename * instead of const char *
[sfrench/samba-autobuild/.git] / source3 / smbd / dosmode.c
1 /* 
2    Unix SMB/CIFS implementation.
3    dos mode handling functions
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) James Peach 2006
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "librpc/gen_ndr/ndr_xattr.h"
24 #include "librpc/gen_ndr/ioctl.h"
25 #include "../libcli/security/security.h"
26 #include "smbd/smbd.h"
27 #include "lib/param/loadparm.h"
28
29 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
30                                 struct smb_filename *smb_fname,
31                                 files_struct **ret_fsp,
32                                 bool *need_close);
33
34 static void dos_mode_debug_print(const char *func, uint32_t mode)
35 {
36         fstring modestr;
37
38         if (DEBUGLEVEL < DBGLVL_INFO) {
39                 return;
40         }
41
42         modestr[0] = '\0';
43
44         if (mode & FILE_ATTRIBUTE_HIDDEN) {
45                 fstrcat(modestr, "h");
46         }
47         if (mode & FILE_ATTRIBUTE_READONLY) {
48                 fstrcat(modestr, "r");
49         }
50         if (mode & FILE_ATTRIBUTE_SYSTEM) {
51                 fstrcat(modestr, "s");
52         }
53         if (mode & FILE_ATTRIBUTE_DIRECTORY) {
54                 fstrcat(modestr, "d");
55         }
56         if (mode & FILE_ATTRIBUTE_ARCHIVE) {
57                 fstrcat(modestr, "a");
58         }
59         if (mode & FILE_ATTRIBUTE_SPARSE) {
60                 fstrcat(modestr, "[sparse]");
61         }
62         if (mode & FILE_ATTRIBUTE_OFFLINE) {
63                 fstrcat(modestr, "[offline]");
64         }
65         if (mode & FILE_ATTRIBUTE_COMPRESSED) {
66                 fstrcat(modestr, "[compressed]");
67         }
68
69         DBG_INFO("%s returning (0x%x): \"%s\"\n", func, (unsigned)mode,
70                  modestr);
71 }
72
73 static uint32_t filter_mode_by_protocol(uint32_t mode)
74 {
75         if (get_Protocol() <= PROTOCOL_LANMAN2) {
76                 DEBUG(10,("filter_mode_by_protocol: "
77                         "filtering result 0x%x to 0x%x\n",
78                         (unsigned int)mode,
79                         (unsigned int)(mode & 0x3f) ));
80                 mode &= 0x3f;
81         }
82         return mode;
83 }
84
85 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
86 {
87 #ifdef S_ISLNK
88 #if LINKS_READ_ONLY
89         if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
90                 return FILE_ATTRIBUTE_READONLY;
91 #endif
92 #endif
93         return 0;
94 }
95
96 /****************************************************************************
97  Change a dos mode to a unix mode.
98     Base permission for files:
99          if creating file and inheriting (i.e. parent_dir != NULL)
100            apply read/write bits from parent directory.
101          else   
102            everybody gets read bit set
103          dos readonly is represented in unix by removing everyone's write bit
104          dos archive is represented in unix by the user's execute bit
105          dos system is represented in unix by the group's execute bit
106          dos hidden is represented in unix by the other's execute bit
107          if !inheriting {
108            Then apply create mask,
109            then add force bits.
110          }
111     Base permission for directories:
112          dos directory is represented in unix by unix's dir bit and the exec bit
113          if !inheriting {
114            Then apply create mask,
115            then add force bits.
116          }
117 ****************************************************************************/
118
119 mode_t unix_mode(connection_struct *conn, int dosmode,
120                  const struct smb_filename *smb_fname,
121                  const char *inherit_from_dir)
122 {
123         mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
124         mode_t dir_mode = 0; /* Mode of the inherit_from directory if
125                               * inheriting. */
126
127         if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
128                 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
129         }
130
131         if ((inherit_from_dir != NULL) && lp_inherit_permissions(SNUM(conn))) {
132                 struct smb_filename *smb_fname_parent;
133
134                 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
135                           smb_fname_str_dbg(smb_fname),
136                           inherit_from_dir));
137
138                 smb_fname_parent = synthetic_smb_fname(
139                         talloc_tos(), inherit_from_dir, NULL, NULL);
140                 if (smb_fname_parent == NULL) {
141                         DEBUG(1,("unix_mode(%s) failed, [dir %s]: No memory\n",
142                                  smb_fname_str_dbg(smb_fname),
143                                  inherit_from_dir));
144                         return(0);
145                 }
146
147                 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
148                         DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
149                                  smb_fname_str_dbg(smb_fname),
150                                  inherit_from_dir, strerror(errno)));
151                         TALLOC_FREE(smb_fname_parent);
152                         return(0);      /* *** shouldn't happen! *** */
153                 }
154
155                 /* Save for later - but explicitly remove setuid bit for safety. */
156                 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
157                 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
158                          smb_fname_str_dbg(smb_fname), (int)dir_mode));
159                 /* Clear "result" */
160                 result = 0;
161                 TALLOC_FREE(smb_fname_parent);
162         } 
163
164         if (IS_DOS_DIR(dosmode)) {
165                 /* We never make directories read only for the owner as under DOS a user
166                 can always create a file in a read-only directory. */
167                 result |= (S_IFDIR | S_IWUSR);
168
169                 if (dir_mode) {
170                         /* Inherit mode of parent directory. */
171                         result |= dir_mode;
172                 } else {
173                         /* Provisionally add all 'x' bits */
174                         result |= (S_IXUSR | S_IXGRP | S_IXOTH);                 
175
176                         /* Apply directory mask */
177                         result &= lp_directory_mask(SNUM(conn));
178                         /* Add in force bits */
179                         result |= lp_force_directory_mode(SNUM(conn));
180                 }
181         } else { 
182                 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
183                         result |= S_IXUSR;
184
185                 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
186                         result |= S_IXGRP;
187
188                 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
189                         result |= S_IXOTH;  
190
191                 if (dir_mode) {
192                         /* Inherit 666 component of parent directory mode */
193                         result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
194                 } else {
195                         /* Apply mode mask */
196                         result &= lp_create_mask(SNUM(conn));
197                         /* Add in force bits */
198                         result |= lp_force_create_mode(SNUM(conn));
199                 }
200         }
201
202         DBG_INFO("unix_mode(%s) returning 0%o\n",
203                  smb_fname_str_dbg(smb_fname), (int)result);
204
205         return(result);
206 }
207
208 /****************************************************************************
209  Change a unix mode to a dos mode.
210 ****************************************************************************/
211
212 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
213                                  const struct smb_filename *smb_fname)
214 {
215         int result = 0;
216         enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
217
218 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
219         /* if we can find out if a file is immutable we should report it r/o */
220         if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
221                 result |= FILE_ATTRIBUTE_READONLY;
222         }
223 #endif
224         if (ro_opts == MAP_READONLY_YES) {
225                 /* Original Samba method - map inverse of user "w" bit. */
226                 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
227                         result |= FILE_ATTRIBUTE_READONLY;
228                 }
229         } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
230                 /* Check actual permissions for read-only. */
231                 if (!can_write_to_file(conn, smb_fname)) {
232                         result |= FILE_ATTRIBUTE_READONLY;
233                 }
234         } /* Else never set the readonly bit. */
235
236         if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
237                 result |= FILE_ATTRIBUTE_ARCHIVE;
238
239         if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
240                 result |= FILE_ATTRIBUTE_SYSTEM;
241
242         if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
243                 result |= FILE_ATTRIBUTE_HIDDEN;
244
245         if (S_ISDIR(smb_fname->st.st_ex_mode))
246                 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
247
248         result |= set_link_read_only_flag(&smb_fname->st);
249
250         dos_mode_debug_print(__func__, result);
251
252         return result;
253 }
254
255 /****************************************************************************
256  Get DOS attributes from an EA.
257  This can also pull the create time into the stat struct inside smb_fname.
258 ****************************************************************************/
259
260 static bool get_ea_dos_attribute(connection_struct *conn,
261                                  struct smb_filename *smb_fname,
262                                  uint32_t *pattr)
263 {
264         struct xattr_DOSATTRIB dosattrib;
265         enum ndr_err_code ndr_err;
266         DATA_BLOB blob;
267         ssize_t sizeret;
268         fstring attrstr;
269         uint32_t dosattr;
270
271         if (!lp_store_dos_attributes(SNUM(conn))) {
272                 return False;
273         }
274
275         /* Don't reset pattr to zero as we may already have filename-based attributes we
276            need to preserve. */
277
278         sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
279                                    SAMBA_XATTR_DOS_ATTRIB, attrstr,
280                                    sizeof(attrstr));
281         if (sizeret == -1) {
282                 DBG_INFO("Cannot get attribute "
283                          "from EA on file %s: Error = %s\n",
284                          smb_fname_str_dbg(smb_fname), strerror(errno));
285                 return False;
286         }
287
288         blob.data = (uint8_t *)attrstr;
289         blob.length = sizeret;
290
291         ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
292                         (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
293
294         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
295                 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
296                          "from EA on file %s: Error = %s\n",
297                          smb_fname_str_dbg(smb_fname),
298                          ndr_errstr(ndr_err)));
299                 return false;
300         }
301
302         DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
303                   smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
304
305         switch (dosattrib.version) {
306                 case 0xFFFF:
307                         dosattr = dosattrib.info.compatinfoFFFF.attrib;
308                         break;
309                 case 1:
310                         dosattr = dosattrib.info.info1.attrib;
311                         if (!null_nttime(dosattrib.info.info1.create_time)) {
312                                 struct timespec create_time =
313                                         nt_time_to_unix_timespec(
314                                                 dosattrib.info.info1.create_time);
315
316                                 update_stat_ex_create_time(&smb_fname->st,
317                                                         create_time);
318
319                                 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
320                                         "set btime %s\n",
321                                         smb_fname_str_dbg(smb_fname),
322                                         time_to_asc(convert_timespec_to_time_t(
323                                                 create_time)) ));
324                         }
325                         break;
326                 case 2:
327                         dosattr = dosattrib.info.oldinfo2.attrib;
328                         /* Don't know what flags to check for this case. */
329                         break;
330                 case 3:
331                         dosattr = dosattrib.info.info3.attrib;
332                         if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
333                                         !null_nttime(dosattrib.info.info3.create_time)) {
334                                 struct timespec create_time =
335                                         nt_time_to_unix_timespec(
336                                                 dosattrib.info.info3.create_time);
337
338                                 update_stat_ex_create_time(&smb_fname->st,
339                                                         create_time);
340
341                                 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
342                                         "set btime %s\n",
343                                         smb_fname_str_dbg(smb_fname),
344                                         time_to_asc(convert_timespec_to_time_t(
345                                                 create_time)) ));
346                         }
347                         break;
348                 default:
349                         DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
350                                  "file %s - %s\n", smb_fname_str_dbg(smb_fname),
351                                  attrstr));
352                         return false;
353         }
354
355         if (S_ISDIR(smb_fname->st.st_ex_mode)) {
356                 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
357         }
358         /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
359         *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
360
361         dos_mode_debug_print(__func__, *pattr);
362
363         return True;
364 }
365
366 /****************************************************************************
367  Set DOS attributes in an EA.
368  Also sets the create time.
369 ****************************************************************************/
370
371 static bool set_ea_dos_attribute(connection_struct *conn,
372                                  struct smb_filename *smb_fname,
373                                  uint32_t dosmode)
374 {
375         struct xattr_DOSATTRIB dosattrib;
376         enum ndr_err_code ndr_err;
377         DATA_BLOB blob;
378
379         ZERO_STRUCT(dosattrib);
380         ZERO_STRUCT(blob);
381
382         dosattrib.version = 3;
383         dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
384                                         XATTR_DOSINFO_CREATE_TIME;
385         dosattrib.info.info3.attrib = dosmode;
386         dosattrib.info.info3.create_time = unix_timespec_to_nt_time(
387                                 smb_fname->st.st_ex_btime);
388
389         DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
390                 (unsigned int)dosmode,
391                 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
392                 smb_fname_str_dbg(smb_fname) ));
393
394         ndr_err = ndr_push_struct_blob(
395                         &blob, talloc_tos(), &dosattrib,
396                         (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
397
398         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
399                 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
400                         ndr_errstr(ndr_err)));
401                 return false;
402         }
403
404         if (blob.data == NULL || blob.length == 0) {
405                 return false;
406         }
407
408         if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
409                              SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
410                              0) == -1) {
411                 bool ret = false;
412                 bool need_close = false;
413                 files_struct *fsp = NULL;
414
415                 if((errno != EPERM) && (errno != EACCES)) {
416                         DBG_INFO("Cannot set "
417                                  "attribute EA on file %s: Error = %s\n",
418                                  smb_fname_str_dbg(smb_fname), strerror(errno));
419                         return false;
420                 }
421
422                 /* We want DOS semantics, ie allow non owner with write permission to change the
423                         bits on a file. Just like file_ntimes below.
424                 */
425
426                 /* Check if we have write access. */
427                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
428                         return false;
429
430                 if (!can_write_to_file(conn, smb_fname)) {
431                         return false;
432                 }
433
434                 /*
435                  * We need to get an open file handle to do the
436                  * metadata operation under root.
437                  */
438
439                 if (!NT_STATUS_IS_OK(get_file_handle_for_metadata(conn,
440                                                 smb_fname,
441                                                 &fsp,
442                                                 &need_close))) {
443                         return false;
444                 }
445
446                 become_root();
447                 if (SMB_VFS_FSETXATTR(fsp,
448                                      SAMBA_XATTR_DOS_ATTRIB, blob.data,
449                                      blob.length, 0) == 0) {
450                         ret = true;
451                 }
452                 unbecome_root();
453                 if (need_close) {
454                         close_file(NULL, fsp, NORMAL_CLOSE);
455                 }
456                 return ret;
457         }
458         DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
459                 (unsigned int)dosmode,
460                 smb_fname_str_dbg(smb_fname)));
461         return true;
462 }
463
464 /****************************************************************************
465  Change a unix mode to a dos mode for an ms dfs link.
466 ****************************************************************************/
467
468 uint32_t dos_mode_msdfs(connection_struct *conn,
469                       const struct smb_filename *smb_fname)
470 {
471         uint32_t result = 0;
472
473         DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
474
475         if (!VALID_STAT(smb_fname->st)) {
476                 return 0;
477         }
478
479         /* First do any modifications that depend on the path name. */
480         /* hide files with a name starting with a . */
481         if (lp_hide_dot_files(SNUM(conn))) {
482                 const char *p = strrchr_m(smb_fname->base_name, '/');
483                 if (p) {
484                         p++;
485                 } else {
486                         p = smb_fname->base_name;
487                 }
488
489                 /* Only . and .. are not hidden. */
490                 if (p[0] == '.' && !((p[1] == '\0') ||
491                                 (p[1] == '.' && p[2] == '\0'))) {
492                         result |= FILE_ATTRIBUTE_HIDDEN;
493                 }
494         }
495
496         result |= dos_mode_from_sbuf(conn, smb_fname);
497
498         /* Optimization : Only call is_hidden_path if it's not already
499            hidden. */
500         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
501             IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
502                 result |= FILE_ATTRIBUTE_HIDDEN;
503         }
504
505         if (result == 0) {
506                 result = FILE_ATTRIBUTE_NORMAL;
507         }
508
509         result = filter_mode_by_protocol(result);
510
511         /*
512          * Add in that it is a reparse point
513          */
514         result |= FILE_ATTRIBUTE_REPARSE_POINT;
515
516         dos_mode_debug_print(__func__, result);
517
518         return(result);
519 }
520
521 /*
522  * check whether a file or directory is flagged as compressed.
523  */
524 static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
525                                           struct smb_filename *smb_fname,
526                                           bool *is_compressed)
527 {
528         NTSTATUS status;
529         uint16_t compression_fmt;
530         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
531         if (tmp_ctx == NULL) {
532                 status = NT_STATUS_NO_MEMORY;
533                 goto err_out;
534         }
535
536         status = SMB_VFS_GET_COMPRESSION(conn, tmp_ctx, NULL, smb_fname,
537                                          &compression_fmt);
538         if (!NT_STATUS_IS_OK(status)) {
539                 goto err_ctx_free;
540         }
541
542         if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
543                 *is_compressed = true;
544         } else {
545                 *is_compressed = false;
546         }
547         status = NT_STATUS_OK;
548
549 err_ctx_free:
550         talloc_free(tmp_ctx);
551 err_out:
552         return status;
553 }
554
555 /****************************************************************************
556  Change a unix mode to a dos mode.
557  May also read the create timespec into the stat struct in smb_fname
558  if "store dos attributes" is true.
559 ****************************************************************************/
560
561 uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
562 {
563         uint32_t result = 0;
564         bool offline;
565
566         DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
567
568         if (!VALID_STAT(smb_fname->st)) {
569                 return 0;
570         }
571
572         /* First do any modifications that depend on the path name. */
573         /* hide files with a name starting with a . */
574         if (lp_hide_dot_files(SNUM(conn))) {
575                 const char *p = strrchr_m(smb_fname->base_name,'/');
576                 if (p) {
577                         p++;
578                 } else {
579                         p = smb_fname->base_name;
580                 }
581
582                 /* Only . and .. are not hidden. */
583                 if (p[0] == '.' && !((p[1] == '\0') ||
584                                 (p[1] == '.' && p[2] == '\0'))) {
585                         result |= FILE_ATTRIBUTE_HIDDEN;
586                 }
587         }
588
589         /* Get the DOS attributes from an EA by preference. */
590         if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
591                 result |= dos_mode_from_sbuf(conn, smb_fname);
592         }
593
594         offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
595         if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
596                 result |= FILE_ATTRIBUTE_OFFLINE;
597         }
598
599         if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
600                 bool compressed = false;
601                 NTSTATUS status = dos_mode_check_compressed(conn, smb_fname,
602                                                             &compressed);
603                 if (NT_STATUS_IS_OK(status) && compressed) {
604                         result |= FILE_ATTRIBUTE_COMPRESSED;
605                 }
606         }
607
608         /* Optimization : Only call is_hidden_path if it's not already
609            hidden. */
610         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
611             IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
612                 result |= FILE_ATTRIBUTE_HIDDEN;
613         }
614
615         if (result == 0) {
616                 result = FILE_ATTRIBUTE_NORMAL;
617         }
618
619         result = filter_mode_by_protocol(result);
620
621         dos_mode_debug_print(__func__, result);
622
623         return result;
624 }
625
626 /*******************************************************************
627  chmod a file - but preserve some bits.
628  If "store dos attributes" is also set it will store the create time
629  from the stat struct in smb_fname (in NTTIME format) in the EA
630  attribute also.
631 ********************************************************************/
632
633 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
634                      uint32_t dosmode, const char *parent_dir, bool newfile)
635 {
636         int mask=0;
637         mode_t tmp;
638         mode_t unixmode;
639         int ret = -1, lret = -1;
640         uint32_t old_mode;
641         struct timespec new_create_timespec;
642         files_struct *fsp = NULL;
643         bool need_close = false;
644         NTSTATUS status;
645
646         if (!CAN_WRITE(conn)) {
647                 errno = EROFS;
648                 return -1;
649         }
650
651         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
652         dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
653
654         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
655                   dosmode, smb_fname_str_dbg(smb_fname)));
656
657         unixmode = smb_fname->st.st_ex_mode;
658
659         get_acl_group_bits(conn, smb_fname->base_name,
660                            &smb_fname->st.st_ex_mode);
661
662         if (S_ISDIR(smb_fname->st.st_ex_mode))
663                 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
664         else
665                 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
666
667         new_create_timespec = smb_fname->st.st_ex_btime;
668
669         old_mode = dos_mode(conn, smb_fname);
670
671         if ((dosmode & FILE_ATTRIBUTE_OFFLINE) &&
672             !(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
673                 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
674                 if (lret == -1) {
675                         if (errno == ENOTSUP) {
676                                 DEBUG(10, ("Setting FILE_ATTRIBUTE_OFFLINE for "
677                                            "%s/%s is not supported.\n",
678                                            parent_dir,
679                                            smb_fname_str_dbg(smb_fname)));
680                         } else {
681                                 DEBUG(0, ("An error occurred while setting "
682                                           "FILE_ATTRIBUTE_OFFLINE for "
683                                           "%s/%s: %s", parent_dir,
684                                           smb_fname_str_dbg(smb_fname),
685                                           strerror(errno)));
686                         }
687                 }
688         }
689
690         dosmode  &= ~FILE_ATTRIBUTE_OFFLINE;
691         old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
692
693         smb_fname->st.st_ex_btime = new_create_timespec;
694
695         /* Store the DOS attributes in an EA by preference. */
696         if (lp_store_dos_attributes(SNUM(conn))) {
697                 /*
698                  * Don't fall back to using UNIX modes. Finally
699                  * follow the smb.conf manpage.
700                  */
701                 if (!set_ea_dos_attribute(conn, smb_fname, dosmode)) {
702                         return -1;
703                 }
704                 if (!newfile) {
705                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
706                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
707                                      smb_fname->base_name);
708                 }
709                 smb_fname->st.st_ex_mode = unixmode;
710                 return 0;
711         }
712
713         unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
714
715         /* preserve the file type bits */
716         mask |= S_IFMT;
717
718         /* preserve the s bits */
719         mask |= (S_ISUID | S_ISGID);
720
721         /* preserve the t bit */
722 #ifdef S_ISVTX
723         mask |= S_ISVTX;
724 #endif
725
726         /* possibly preserve the x bits */
727         if (!MAP_ARCHIVE(conn))
728                 mask |= S_IXUSR;
729         if (!MAP_SYSTEM(conn))
730                 mask |= S_IXGRP;
731         if (!MAP_HIDDEN(conn))
732                 mask |= S_IXOTH;
733
734         unixmode |= (smb_fname->st.st_ex_mode & mask);
735
736         /* if we previously had any r bits set then leave them alone */
737         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
738                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
739                 unixmode |= tmp;
740         }
741
742         /* if we previously had any w bits set then leave them alone 
743                 whilst adding in the new w bits, if the new mode is not rdonly */
744         if (!IS_DOS_READONLY(dosmode)) {
745                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
746         }
747
748         /*
749          * From the chmod 2 man page:
750          *
751          * "If the calling process is not privileged, and the group of the file
752          * does not match the effective group ID of the process or one of its
753          * supplementary group IDs, the S_ISGID bit will be turned off, but
754          * this will not cause an error to be returned."
755          *
756          * Simply refuse to do the chmod in this case.
757          */
758
759         if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
760                         geteuid() != sec_initial_uid() &&
761                         !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
762                 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
763                         "set for directory %s\n",
764                         smb_fname_str_dbg(smb_fname)));
765                 errno = EPERM;
766                 return -1;
767         }
768
769         ret = SMB_VFS_CHMOD(conn, smb_fname, unixmode);
770         if (ret == 0) {
771                 if(!newfile || (lret != -1)) {
772                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
773                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
774                                      smb_fname->base_name);
775                 }
776                 smb_fname->st.st_ex_mode = unixmode;
777                 return 0;
778         }
779
780         if((errno != EPERM) && (errno != EACCES))
781                 return -1;
782
783         if(!lp_dos_filemode(SNUM(conn)))
784                 return -1;
785
786         /* We want DOS semantics, ie allow non owner with write permission to change the
787                 bits on a file. Just like file_ntimes below.
788         */
789
790         if (!can_write_to_file(conn, smb_fname)) {
791                 errno = EACCES;
792                 return -1;
793         }
794
795         /*
796          * We need to get an open file handle to do the
797          * metadata operation under root.
798          */
799
800         status = get_file_handle_for_metadata(conn,
801                                               smb_fname,
802                                               &fsp,
803                                               &need_close);
804         if (!NT_STATUS_IS_OK(status)) {
805                 errno = map_errno_from_nt_status(status);
806                 return -1;
807         }
808
809         become_root();
810         ret = SMB_VFS_FCHMOD(fsp, unixmode);
811         unbecome_root();
812         if (need_close) {
813                 close_file(NULL, fsp, NORMAL_CLOSE);
814         }
815         if (!newfile) {
816                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
817                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
818                              smb_fname->base_name);
819         }
820         if (ret == 0) {
821                 smb_fname->st.st_ex_mode = unixmode;
822         }
823
824         return( ret );
825 }
826
827
828 NTSTATUS file_set_sparse(connection_struct *conn,
829                          files_struct *fsp,
830                          bool sparse)
831 {
832         uint32_t old_dosmode;
833         uint32_t new_dosmode;
834         NTSTATUS status;
835
836         if (!CAN_WRITE(conn)) {
837                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
838                         "on readonly share[%s]\n",
839                         smb_fname_str_dbg(fsp->fsp_name),
840                         sparse,
841                         lp_servicename(talloc_tos(), SNUM(conn))));
842                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
843         }
844
845         /*
846          * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
847          * following access flags are granted.
848          */
849         if ((fsp->access_mask & (FILE_WRITE_DATA
850                                 | FILE_WRITE_ATTRIBUTES
851                                 | SEC_FILE_APPEND_DATA)) == 0) {
852                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
853                         "access_mask[0x%08X] - access denied\n",
854                         smb_fname_str_dbg(fsp->fsp_name),
855                         sparse,
856                         fsp->access_mask));
857                 return NT_STATUS_ACCESS_DENIED;
858         }
859
860         if (fsp->is_directory) {
861                 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
862                           (sparse ? "set" : "clear"),
863                           smb_fname_str_dbg(fsp->fsp_name)));
864                 return NT_STATUS_INVALID_PARAMETER;
865         }
866
867         if (IS_IPC(conn) || IS_PRINT(conn)) {
868                 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
869                           (sparse ? "set" : "clear")));
870                 return NT_STATUS_INVALID_PARAMETER;
871         }
872
873         DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
874                   sparse, smb_fname_str_dbg(fsp->fsp_name)));
875
876         if (!lp_store_dos_attributes(SNUM(conn))) {
877                 return NT_STATUS_INVALID_DEVICE_REQUEST;
878         }
879
880         status = vfs_stat_fsp(fsp);
881         if (!NT_STATUS_IS_OK(status)) {
882                 return status;
883         }
884
885         old_dosmode = dos_mode(conn, fsp->fsp_name);
886
887         if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
888                 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
889         } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
890                 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
891         } else {
892                 return NT_STATUS_OK;
893         }
894
895         /* Store the DOS attributes in an EA. */
896         if (!set_ea_dos_attribute(conn, fsp->fsp_name,
897                                   new_dosmode)) {
898                 if (errno == 0) {
899                         errno = EIO;
900                 }
901                 return map_nt_error_from_unix(errno);
902         }
903
904         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
905                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
906                      fsp->fsp_name->base_name);
907
908         fsp->is_sparse = sparse;
909
910         return NT_STATUS_OK;
911 }
912
913 /*******************************************************************
914  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
915  than POSIX.
916 *******************************************************************/
917
918 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
919                 struct smb_file_time *ft)
920 {
921         int ret = -1;
922
923         errno = 0;
924
925         DEBUG(6, ("file_ntime: actime: %s",
926                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
927         DEBUG(6, ("file_ntime: modtime: %s",
928                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
929         DEBUG(6, ("file_ntime: ctime: %s",
930                   time_to_asc(convert_timespec_to_time_t(ft->ctime))));
931         DEBUG(6, ("file_ntime: createtime: %s",
932                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
933
934         /* Don't update the time on read-only shares */
935         /* We need this as set_filetime (which can be called on
936            close and other paths) can end up calling this function
937            without the NEED_WRITE protection. Found by : 
938            Leo Weppelman <leo@wau.mis.ah.nl>
939         */
940
941         if (!CAN_WRITE(conn)) {
942                 return 0;
943         }
944
945         if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
946                 return 0;
947         }
948
949         if((errno != EPERM) && (errno != EACCES)) {
950                 return -1;
951         }
952
953         if(!lp_dos_filetimes(SNUM(conn))) {
954                 return -1;
955         }
956
957         /* We have permission (given by the Samba admin) to
958            break POSIX semantics and allow a user to change
959            the time on a file they don't own but can write to
960            (as DOS does).
961          */
962
963         /* Check if we have write access. */
964         if (can_write_to_file(conn, smb_fname)) {
965                 /* We are allowed to become root and change the filetime. */
966                 become_root();
967                 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
968                 unbecome_root();
969         }
970
971         return ret;
972 }
973
974 /******************************************************************
975  Force a "sticky" write time on a pathname. This will always be
976  returned on all future write time queries and set on close.
977 ******************************************************************/
978
979 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
980 {
981         if (null_timespec(mtime)) {
982                 return true;
983         }
984
985         if (!set_sticky_write_time(fileid, mtime)) {
986                 return false;
987         }
988
989         return true;
990 }
991
992 /******************************************************************
993  Force a "sticky" write time on an fsp. This will always be
994  returned on all future write time queries and set on close.
995 ******************************************************************/
996
997 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
998 {
999         if (null_timespec(mtime)) {
1000                 return true;
1001         }
1002
1003         fsp->write_time_forced = true;
1004         TALLOC_FREE(fsp->update_write_time_event);
1005
1006         return set_sticky_write_time_path(fsp->file_id, mtime);
1007 }
1008
1009 /******************************************************************
1010  Set a create time EA.
1011 ******************************************************************/
1012
1013 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1014                                 const struct smb_filename *psmb_fname,
1015                                 struct timespec create_time)
1016 {
1017         struct smb_filename *smb_fname;
1018         uint32_t dosmode;
1019         int ret;
1020
1021         if (!lp_store_dos_attributes(SNUM(conn))) {
1022                 return NT_STATUS_OK;
1023         }
1024
1025         smb_fname = synthetic_smb_fname(talloc_tos(), psmb_fname->base_name,
1026                                         NULL, &psmb_fname->st);
1027
1028         if (smb_fname == NULL) {
1029                 return NT_STATUS_NO_MEMORY;
1030         }
1031
1032         dosmode = dos_mode(conn, smb_fname);
1033
1034         smb_fname->st.st_ex_btime = create_time;
1035
1036         ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1037         if (ret == -1) {
1038                 map_nt_error_from_unix(errno);
1039         }
1040
1041         DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1042                 smb_fname_str_dbg(smb_fname)));
1043
1044         return NT_STATUS_OK;
1045 }
1046
1047 /******************************************************************
1048  Return a create time.
1049 ******************************************************************/
1050
1051 struct timespec get_create_timespec(connection_struct *conn,
1052                                 struct files_struct *fsp,
1053                                 const struct smb_filename *smb_fname)
1054 {
1055         return smb_fname->st.st_ex_btime;
1056 }
1057
1058 /******************************************************************
1059  Return a change time (may look at EA in future).
1060 ******************************************************************/
1061
1062 struct timespec get_change_timespec(connection_struct *conn,
1063                                 struct files_struct *fsp,
1064                                 const struct smb_filename *smb_fname)
1065 {
1066         return smb_fname->st.st_ex_mtime;
1067 }
1068
1069 /****************************************************************************
1070  Get a real open file handle we can do meta-data operations on. As it's
1071  going to be used under root access only on meta-data we should look for
1072  any existing open file handle first, and use that in preference (also to
1073  avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1074 ****************************************************************************/
1075
1076 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1077                                 struct smb_filename *smb_fname,
1078                                 files_struct **ret_fsp,
1079                                 bool *need_close)
1080 {
1081         NTSTATUS status;
1082         files_struct *fsp;
1083         struct file_id file_id;
1084
1085         *need_close = false;
1086
1087         if (!VALID_STAT(smb_fname->st)) {
1088                 return NT_STATUS_INVALID_PARAMETER;
1089         }
1090
1091         file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1092
1093         for(fsp = file_find_di_first(conn->sconn, file_id);
1094                         fsp;
1095                         fsp = file_find_di_next(fsp)) {
1096                 if (fsp->fh->fd != -1) {
1097                         *ret_fsp = fsp;
1098                         return NT_STATUS_OK;
1099                 }
1100         }
1101
1102         /* Opens an INTERNAL_OPEN_ONLY write handle. */
1103         status = SMB_VFS_CREATE_FILE(
1104                 conn,                                   /* conn */
1105                 NULL,                                   /* req */
1106                 0,                                      /* root_dir_fid */
1107                 smb_fname,                              /* fname */
1108                 FILE_WRITE_DATA,                        /* access_mask */
1109                 (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
1110                         FILE_SHARE_DELETE),
1111                 FILE_OPEN,                              /* create_disposition*/
1112                 0,                                      /* create_options */
1113                 0,                                      /* file_attributes */
1114                 INTERNAL_OPEN_ONLY,                     /* oplock_request */
1115                 NULL,                                   /* lease */
1116                 0,                                      /* allocation_size */
1117                 0,                                      /* private_flags */
1118                 NULL,                                   /* sd */
1119                 NULL,                                   /* ea_list */
1120                 ret_fsp,                                /* result */
1121                 NULL,                                   /* pinfo */
1122                 NULL, NULL);                            /* create context */
1123
1124         if (NT_STATUS_IS_OK(status)) {
1125                 *need_close = true;
1126         }
1127         return status;
1128 }