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