141001c74a9f87beb4240816622652791631ecd7
[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                                 const 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 NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
264                                   DATA_BLOB blob,
265                                   uint32_t *pattr)
266 {
267         struct xattr_DOSATTRIB dosattrib;
268         enum ndr_err_code ndr_err;
269         uint32_t dosattr;
270
271         ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
272                         (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
273
274         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
275                 DBG_WARNING("bad ndr decode "
276                             "from EA on file %s: Error = %s\n",
277                             smb_fname_str_dbg(smb_fname),
278                             ndr_errstr(ndr_err));
279                 return ndr_map_error2ntstatus(ndr_err);
280         }
281
282         DBG_DEBUG("%s attr = %s\n",
283                   smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
284
285         switch (dosattrib.version) {
286         case 0xFFFF:
287                 dosattr = dosattrib.info.compatinfoFFFF.attrib;
288                 break;
289         case 1:
290                 dosattr = dosattrib.info.info1.attrib;
291                 if (!null_nttime(dosattrib.info.info1.create_time)) {
292                         struct timespec create_time =
293                                 nt_time_to_unix_timespec(
294                                         dosattrib.info.info1.create_time);
295
296                         update_stat_ex_create_time(&smb_fname->st,
297                                                    create_time);
298
299                         DBG_DEBUG("file %s case 1 set btime %s\n",
300                                   smb_fname_str_dbg(smb_fname),
301                                   time_to_asc(convert_timespec_to_time_t(
302                                                       create_time)));
303                 }
304                 break;
305         case 2:
306                 dosattr = dosattrib.info.oldinfo2.attrib;
307                 /* Don't know what flags to check for this case. */
308                 break;
309         case 3:
310                 dosattr = dosattrib.info.info3.attrib;
311                 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
312                     !null_nttime(dosattrib.info.info3.create_time)) {
313                         struct timespec create_time =
314                                 nt_time_to_unix_timespec(
315                                         dosattrib.info.info3.create_time);
316
317                         update_stat_ex_create_time(&smb_fname->st,
318                                                    create_time);
319
320                         DBG_DEBUG("file %s case 3 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         default:
327                 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
328                             smb_fname_str_dbg(smb_fname), blob.data);
329                 /* Should this be INTERNAL_ERROR? */
330                 return NT_STATUS_INVALID_PARAMETER;
331         }
332
333         if (S_ISDIR(smb_fname->st.st_ex_mode)) {
334                 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
335         }
336
337         /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
338         *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
339
340         dos_mode_debug_print(__func__, *pattr);
341
342         return NT_STATUS_OK;
343 }
344
345 NTSTATUS get_ea_dos_attribute(connection_struct *conn,
346                               struct smb_filename *smb_fname,
347                               uint32_t *pattr)
348 {
349         DATA_BLOB blob;
350         ssize_t sizeret;
351         fstring attrstr;
352         NTSTATUS status;
353
354         if (!lp_store_dos_attributes(SNUM(conn))) {
355                 return NT_STATUS_NOT_IMPLEMENTED;
356         }
357
358         /* Don't reset pattr to zero as we may already have filename-based attributes we
359            need to preserve. */
360
361         sizeret = SMB_VFS_GETXATTR(conn, smb_fname,
362                                    SAMBA_XATTR_DOS_ATTRIB, attrstr,
363                                    sizeof(attrstr));
364         if (sizeret == -1 && errno == EACCES) {
365                 int saved_errno = 0;
366
367                 /*
368                  * According to MS-FSA 2.1.5.1.2.1 "Algorithm to Check Access to
369                  * an Existing File" FILE_LIST_DIRECTORY on a directory implies
370                  * FILE_READ_ATTRIBUTES for directory entries. Being able to
371                  * stat() a file implies FILE_LIST_DIRECTORY for the directory
372                  * containing the file.
373                  */
374
375                 if (!VALID_STAT(smb_fname->st)) {
376                         /*
377                          * Safety net: dos_mode() already checks this, but as we
378                          * become root based on this, add an additional layer of
379                          * defense.
380                          */
381                         DBG_ERR("Rejecting root override, invalid stat [%s]\n",
382                                 smb_fname_str_dbg(smb_fname));
383                         return NT_STATUS_ACCESS_DENIED;
384                 }
385
386                 become_root();
387                 sizeret = SMB_VFS_GETXATTR(conn, smb_fname,
388                                            SAMBA_XATTR_DOS_ATTRIB,
389                                            attrstr,
390                                            sizeof(attrstr));
391                 if (sizeret == -1) {
392                         saved_errno = errno;
393                 }
394                 unbecome_root();
395
396                 if (saved_errno != 0) {
397                         errno = saved_errno;
398                 }
399         }
400         if (sizeret == -1) {
401                 DBG_INFO("Cannot get attribute "
402                          "from EA on file %s: Error = %s\n",
403                          smb_fname_str_dbg(smb_fname), strerror(errno));
404                 return map_nt_error_from_unix(errno);
405         }
406
407         blob.data = (uint8_t *)attrstr;
408         blob.length = sizeret;
409
410         status = parse_dos_attribute_blob(smb_fname, blob, pattr);
411         if (!NT_STATUS_IS_OK(status)) {
412                 return status;
413         }
414
415         return NT_STATUS_OK;
416 }
417
418 /****************************************************************************
419  Set DOS attributes in an EA.
420  Also sets the create time.
421 ****************************************************************************/
422
423 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
424                               const struct smb_filename *smb_fname,
425                               uint32_t dosmode)
426 {
427         struct xattr_DOSATTRIB dosattrib;
428         enum ndr_err_code ndr_err;
429         DATA_BLOB blob;
430         int ret;
431
432         if (!lp_store_dos_attributes(SNUM(conn))) {
433                 return NT_STATUS_NOT_IMPLEMENTED;
434         }
435
436         /*
437          * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
438          * vfs_default via DMAPI if that is enabled.
439          */
440         dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
441
442         ZERO_STRUCT(dosattrib);
443         ZERO_STRUCT(blob);
444
445         dosattrib.version = 3;
446         dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
447                                         XATTR_DOSINFO_CREATE_TIME;
448         dosattrib.info.info3.attrib = dosmode;
449         dosattrib.info.info3.create_time = unix_timespec_to_nt_time(
450                                 smb_fname->st.st_ex_btime);
451
452         DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
453                 (unsigned int)dosmode,
454                 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
455                 smb_fname_str_dbg(smb_fname) ));
456
457         ndr_err = ndr_push_struct_blob(
458                         &blob, talloc_tos(), &dosattrib,
459                         (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
460
461         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
462                 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
463                         ndr_errstr(ndr_err)));
464                 return ndr_map_error2ntstatus(ndr_err);
465         }
466
467         if (blob.data == NULL || blob.length == 0) {
468                 /* Should this be INTERNAL_ERROR? */
469                 return NT_STATUS_INVALID_PARAMETER;
470         }
471
472         ret = SMB_VFS_SETXATTR(conn, smb_fname,
473                                SAMBA_XATTR_DOS_ATTRIB,
474                                blob.data, blob.length, 0);
475         if (ret != 0) {
476                 NTSTATUS status = NT_STATUS_OK;
477                 bool need_close = false;
478                 files_struct *fsp = NULL;
479                 bool set_dosmode_ok = false;
480
481                 if ((errno != EPERM) && (errno != EACCES)) {
482                         DBG_INFO("Cannot set "
483                                  "attribute EA on file %s: Error = %s\n",
484                                  smb_fname_str_dbg(smb_fname), strerror(errno));
485                         return map_nt_error_from_unix(errno);
486                 }
487
488                 /* We want DOS semantics, ie allow non owner with write permission to change the
489                         bits on a file. Just like file_ntimes below.
490                 */
491
492                 /* Check if we have write access. */
493                 if (!CAN_WRITE(conn)) {
494                         return NT_STATUS_ACCESS_DENIED;
495                 }
496
497                 status = smbd_check_access_rights(conn, smb_fname, false,
498                                                   FILE_WRITE_ATTRIBUTES);
499                 if (NT_STATUS_IS_OK(status)) {
500                         set_dosmode_ok = true;
501                 }
502
503                 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
504                         set_dosmode_ok = can_write_to_file(conn, smb_fname);
505                 }
506
507                 if (!set_dosmode_ok) {
508                         return NT_STATUS_ACCESS_DENIED;
509                 }
510
511                 /*
512                  * We need to get an open file handle to do the
513                  * metadata operation under root.
514                  */
515
516                 status = get_file_handle_for_metadata(conn,
517                                                 smb_fname,
518                                                 &fsp,
519                                                 &need_close);
520                 if (!NT_STATUS_IS_OK(status)) {
521                         return status;
522                 }
523
524                 become_root();
525                 ret = SMB_VFS_FSETXATTR(fsp,
526                                         SAMBA_XATTR_DOS_ATTRIB,
527                                         blob.data, blob.length, 0);
528                 if (ret == 0) {
529                         status = NT_STATUS_OK;
530                 }
531                 unbecome_root();
532                 if (need_close) {
533                         close_file(NULL, fsp, NORMAL_CLOSE);
534                 }
535                 return status;
536         }
537         DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
538                 (unsigned int)dosmode,
539                 smb_fname_str_dbg(smb_fname)));
540         return NT_STATUS_OK;
541 }
542
543 /****************************************************************************
544  Change a unix mode to a dos mode for an ms dfs link.
545 ****************************************************************************/
546
547 uint32_t dos_mode_msdfs(connection_struct *conn,
548                       const struct smb_filename *smb_fname)
549 {
550         uint32_t result = 0;
551
552         DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
553
554         if (!VALID_STAT(smb_fname->st)) {
555                 return 0;
556         }
557
558         /* First do any modifications that depend on the path name. */
559         /* hide files with a name starting with a . */
560         if (lp_hide_dot_files(SNUM(conn))) {
561                 const char *p = strrchr_m(smb_fname->base_name, '/');
562                 if (p) {
563                         p++;
564                 } else {
565                         p = smb_fname->base_name;
566                 }
567
568                 /* Only . and .. are not hidden. */
569                 if (p[0] == '.' && !((p[1] == '\0') ||
570                                 (p[1] == '.' && p[2] == '\0'))) {
571                         result |= FILE_ATTRIBUTE_HIDDEN;
572                 }
573         }
574
575         result |= dos_mode_from_sbuf(conn, smb_fname);
576
577         /* Optimization : Only call is_hidden_path if it's not already
578            hidden. */
579         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
580             IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
581                 result |= FILE_ATTRIBUTE_HIDDEN;
582         }
583
584         if (result == 0) {
585                 result = FILE_ATTRIBUTE_NORMAL;
586         }
587
588         result = filter_mode_by_protocol(result);
589
590         /*
591          * Add in that it is a reparse point
592          */
593         result |= FILE_ATTRIBUTE_REPARSE_POINT;
594
595         dos_mode_debug_print(__func__, result);
596
597         return(result);
598 }
599
600 /*
601  * check whether a file or directory is flagged as compressed.
602  */
603 static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
604                                           struct smb_filename *smb_fname,
605                                           bool *is_compressed)
606 {
607         NTSTATUS status;
608         uint16_t compression_fmt;
609         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
610         if (tmp_ctx == NULL) {
611                 status = NT_STATUS_NO_MEMORY;
612                 goto err_out;
613         }
614
615         status = SMB_VFS_GET_COMPRESSION(conn, tmp_ctx, NULL, smb_fname,
616                                          &compression_fmt);
617         if (!NT_STATUS_IS_OK(status)) {
618                 goto err_ctx_free;
619         }
620
621         if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
622                 *is_compressed = true;
623         } else {
624                 *is_compressed = false;
625         }
626         status = NT_STATUS_OK;
627
628 err_ctx_free:
629         talloc_free(tmp_ctx);
630 err_out:
631         return status;
632 }
633
634 static uint32_t dos_mode_from_name(connection_struct *conn,
635                                    const struct smb_filename *smb_fname,
636                                    uint32_t dosmode)
637 {
638         const char *p = NULL;
639         uint32_t result = dosmode;
640
641         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
642             lp_hide_dot_files(SNUM(conn)))
643         {
644                 p = strrchr_m(smb_fname->base_name, '/');
645                 if (p) {
646                         p++;
647                 } else {
648                         p = smb_fname->base_name;
649                 }
650
651                 /* Only . and .. are not hidden. */
652                 if ((p[0] == '.') &&
653                     !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0')))
654                 {
655                         result |= FILE_ATTRIBUTE_HIDDEN;
656                 }
657         }
658
659         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
660             IS_HIDDEN_PATH(conn, smb_fname->base_name))
661         {
662                 result |= FILE_ATTRIBUTE_HIDDEN;
663         }
664
665         return result;
666 }
667
668 static uint32_t dos_mode_post(uint32_t dosmode,
669                               connection_struct *conn,
670                               struct smb_filename *smb_fname,
671                               const char *func)
672 {
673         NTSTATUS status;
674
675         /*
676          * According to MS-FSA a stream name does not have
677          * separate DOS attribute metadata, so we must return
678          * the DOS attribute from the base filename. With one caveat,
679          * a non-default stream name can never be a directory.
680          *
681          * As this is common to all streams data stores, we handle
682          * it here instead of inside all stream VFS modules.
683          *
684          * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
685          */
686
687         if (is_ntfs_stream_smb_fname(smb_fname)) {
688                 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
689                 if (!is_ntfs_default_stream_smb_fname(smb_fname)) {
690                         /*
691                          * Non-default stream name, not a posix path.
692                          */
693                         dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
694                 }
695         }
696
697         if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
698                 bool compressed = false;
699
700                 status = dos_mode_check_compressed(conn, smb_fname,
701                                                    &compressed);
702                 if (NT_STATUS_IS_OK(status) && compressed) {
703                         dosmode |= FILE_ATTRIBUTE_COMPRESSED;
704                 }
705         }
706
707         dosmode |= dos_mode_from_name(conn, smb_fname, dosmode);
708
709         if (S_ISDIR(smb_fname->st.st_ex_mode)) {
710                 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
711         } else if (dosmode == 0) {
712                 dosmode = FILE_ATTRIBUTE_NORMAL;
713         }
714
715         dosmode = filter_mode_by_protocol(dosmode);
716
717         dos_mode_debug_print(func, dosmode);
718         return dosmode;
719 }
720
721 /****************************************************************************
722  Change a unix mode to a dos mode.
723  May also read the create timespec into the stat struct in smb_fname
724  if "store dos attributes" is true.
725 ****************************************************************************/
726
727 uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
728 {
729         uint32_t result = 0;
730         NTSTATUS status = NT_STATUS_OK;
731
732         DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
733
734         if (!VALID_STAT(smb_fname->st)) {
735                 return 0;
736         }
737
738         /* Get the DOS attributes via the VFS if we can */
739         status = SMB_VFS_GET_DOS_ATTRIBUTES(conn, smb_fname, &result);
740         if (!NT_STATUS_IS_OK(status)) {
741                 /*
742                  * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
743                  */
744                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
745                         result |= dos_mode_from_sbuf(conn, smb_fname);
746                 }
747         }
748
749         result = dos_mode_post(result, conn, smb_fname, __func__);
750         return result;
751 }
752
753 /*******************************************************************
754  chmod a file - but preserve some bits.
755  If "store dos attributes" is also set it will store the create time
756  from the stat struct in smb_fname (in NTTIME format) in the EA
757  attribute also.
758 ********************************************************************/
759
760 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
761                      uint32_t dosmode, const char *parent_dir, bool newfile)
762 {
763         int mask=0;
764         mode_t tmp;
765         mode_t unixmode;
766         int ret = -1, lret = -1;
767         files_struct *fsp = NULL;
768         bool need_close = false;
769         NTSTATUS status;
770
771         if (!CAN_WRITE(conn)) {
772                 errno = EROFS;
773                 return -1;
774         }
775
776         dosmode &= SAMBA_ATTRIBUTES_MASK;
777
778         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
779                   dosmode, smb_fname_str_dbg(smb_fname)));
780
781         unixmode = smb_fname->st.st_ex_mode;
782
783         get_acl_group_bits(conn, smb_fname,
784                         &smb_fname->st.st_ex_mode);
785
786         if (S_ISDIR(smb_fname->st.st_ex_mode))
787                 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
788         else
789                 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
790
791         /* Store the DOS attributes in an EA by preference. */
792         status = SMB_VFS_SET_DOS_ATTRIBUTES(conn, smb_fname, dosmode);
793         if (NT_STATUS_IS_OK(status)) {
794                 if (!newfile) {
795                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
796                                 FILE_NOTIFY_CHANGE_ATTRIBUTES,
797                                 smb_fname->base_name);
798                 }
799                 smb_fname->st.st_ex_mode = unixmode;
800                 return 0;
801         } else {
802                 /*
803                  * Only fall back to using UNIX modes if
804                  * we get NOT_IMPLEMENTED.
805                  */
806                 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
807                         errno = map_errno_from_nt_status(status);
808                         return -1;
809                 }
810         }
811
812         /* Fall back to UNIX modes. */
813         unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
814
815         /* preserve the file type bits */
816         mask |= S_IFMT;
817
818         /* preserve the s bits */
819         mask |= (S_ISUID | S_ISGID);
820
821         /* preserve the t bit */
822 #ifdef S_ISVTX
823         mask |= S_ISVTX;
824 #endif
825
826         /* possibly preserve the x bits */
827         if (!MAP_ARCHIVE(conn))
828                 mask |= S_IXUSR;
829         if (!MAP_SYSTEM(conn))
830                 mask |= S_IXGRP;
831         if (!MAP_HIDDEN(conn))
832                 mask |= S_IXOTH;
833
834         unixmode |= (smb_fname->st.st_ex_mode & mask);
835
836         /* if we previously had any r bits set then leave them alone */
837         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
838                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
839                 unixmode |= tmp;
840         }
841
842         /* if we previously had any w bits set then leave them alone 
843                 whilst adding in the new w bits, if the new mode is not rdonly */
844         if (!IS_DOS_READONLY(dosmode)) {
845                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
846         }
847
848         /*
849          * From the chmod 2 man page:
850          *
851          * "If the calling process is not privileged, and the group of the file
852          * does not match the effective group ID of the process or one of its
853          * supplementary group IDs, the S_ISGID bit will be turned off, but
854          * this will not cause an error to be returned."
855          *
856          * Simply refuse to do the chmod in this case.
857          */
858
859         if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
860                         geteuid() != sec_initial_uid() &&
861                         !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
862                 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
863                         "set for directory %s\n",
864                         smb_fname_str_dbg(smb_fname)));
865                 errno = EPERM;
866                 return -1;
867         }
868
869         ret = SMB_VFS_CHMOD(conn, smb_fname, unixmode);
870         if (ret == 0) {
871                 if(!newfile || (lret != -1)) {
872                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
873                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
874                                      smb_fname->base_name);
875                 }
876                 smb_fname->st.st_ex_mode = unixmode;
877                 return 0;
878         }
879
880         if((errno != EPERM) && (errno != EACCES))
881                 return -1;
882
883         if(!lp_dos_filemode(SNUM(conn)))
884                 return -1;
885
886         /* We want DOS semantics, ie allow non owner with write permission to change the
887                 bits on a file. Just like file_ntimes below.
888         */
889
890         if (!can_write_to_file(conn, smb_fname)) {
891                 errno = EACCES;
892                 return -1;
893         }
894
895         /*
896          * We need to get an open file handle to do the
897          * metadata operation under root.
898          */
899
900         status = get_file_handle_for_metadata(conn,
901                                               smb_fname,
902                                               &fsp,
903                                               &need_close);
904         if (!NT_STATUS_IS_OK(status)) {
905                 errno = map_errno_from_nt_status(status);
906                 return -1;
907         }
908
909         become_root();
910         ret = SMB_VFS_FCHMOD(fsp, unixmode);
911         unbecome_root();
912         if (need_close) {
913                 close_file(NULL, fsp, NORMAL_CLOSE);
914         }
915         if (!newfile) {
916                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
917                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
918                              smb_fname->base_name);
919         }
920         if (ret == 0) {
921                 smb_fname->st.st_ex_mode = unixmode;
922         }
923
924         return( ret );
925 }
926
927
928 NTSTATUS file_set_sparse(connection_struct *conn,
929                          files_struct *fsp,
930                          bool sparse)
931 {
932         uint32_t old_dosmode;
933         uint32_t new_dosmode;
934         NTSTATUS status;
935
936         if (!CAN_WRITE(conn)) {
937                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
938                         "on readonly share[%s]\n",
939                         smb_fname_str_dbg(fsp->fsp_name),
940                         sparse,
941                         lp_servicename(talloc_tos(), SNUM(conn))));
942                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
943         }
944
945         /*
946          * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
947          * following access flags are granted.
948          */
949         if ((fsp->access_mask & (FILE_WRITE_DATA
950                                 | FILE_WRITE_ATTRIBUTES
951                                 | SEC_FILE_APPEND_DATA)) == 0) {
952                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
953                         "access_mask[0x%08X] - access denied\n",
954                         smb_fname_str_dbg(fsp->fsp_name),
955                         sparse,
956                         fsp->access_mask));
957                 return NT_STATUS_ACCESS_DENIED;
958         }
959
960         if (fsp->is_directory) {
961                 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
962                           (sparse ? "set" : "clear"),
963                           smb_fname_str_dbg(fsp->fsp_name)));
964                 return NT_STATUS_INVALID_PARAMETER;
965         }
966
967         if (IS_IPC(conn) || IS_PRINT(conn)) {
968                 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
969                           (sparse ? "set" : "clear")));
970                 return NT_STATUS_INVALID_PARAMETER;
971         }
972
973         DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
974                   sparse, smb_fname_str_dbg(fsp->fsp_name)));
975
976         if (!lp_store_dos_attributes(SNUM(conn))) {
977                 return NT_STATUS_INVALID_DEVICE_REQUEST;
978         }
979
980         status = vfs_stat_fsp(fsp);
981         if (!NT_STATUS_IS_OK(status)) {
982                 return status;
983         }
984
985         old_dosmode = dos_mode(conn, fsp->fsp_name);
986
987         if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
988                 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
989         } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
990                 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
991         } else {
992                 return NT_STATUS_OK;
993         }
994
995         /* Store the DOS attributes in an EA. */
996         status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
997         if (!NT_STATUS_IS_OK(status)) {
998                 return status;
999         }
1000
1001         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1002                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
1003                      fsp->fsp_name->base_name);
1004
1005         fsp->is_sparse = sparse;
1006
1007         return NT_STATUS_OK;
1008 }
1009
1010 /*******************************************************************
1011  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1012  than POSIX.
1013 *******************************************************************/
1014
1015 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
1016                 struct smb_file_time *ft)
1017 {
1018         int ret = -1;
1019
1020         errno = 0;
1021
1022         DEBUG(6, ("file_ntime: actime: %s",
1023                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
1024         DEBUG(6, ("file_ntime: modtime: %s",
1025                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
1026         DEBUG(6, ("file_ntime: ctime: %s",
1027                   time_to_asc(convert_timespec_to_time_t(ft->ctime))));
1028         DEBUG(6, ("file_ntime: createtime: %s",
1029                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
1030
1031         /* Don't update the time on read-only shares */
1032         /* We need this as set_filetime (which can be called on
1033            close and other paths) can end up calling this function
1034            without the NEED_WRITE protection. Found by : 
1035            Leo Weppelman <leo@wau.mis.ah.nl>
1036         */
1037
1038         if (!CAN_WRITE(conn)) {
1039                 return 0;
1040         }
1041
1042         if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
1043                 return 0;
1044         }
1045
1046         if((errno != EPERM) && (errno != EACCES)) {
1047                 return -1;
1048         }
1049
1050         if(!lp_dos_filetimes(SNUM(conn))) {
1051                 return -1;
1052         }
1053
1054         /* We have permission (given by the Samba admin) to
1055            break POSIX semantics and allow a user to change
1056            the time on a file they don't own but can write to
1057            (as DOS does).
1058          */
1059
1060         /* Check if we have write access. */
1061         if (can_write_to_file(conn, smb_fname)) {
1062                 /* We are allowed to become root and change the filetime. */
1063                 become_root();
1064                 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1065                 unbecome_root();
1066         }
1067
1068         return ret;
1069 }
1070
1071 /******************************************************************
1072  Force a "sticky" write time on a pathname. This will always be
1073  returned on all future write time queries and set on close.
1074 ******************************************************************/
1075
1076 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1077 {
1078         if (null_timespec(mtime)) {
1079                 return true;
1080         }
1081
1082         if (!set_sticky_write_time(fileid, mtime)) {
1083                 return false;
1084         }
1085
1086         return true;
1087 }
1088
1089 /******************************************************************
1090  Force a "sticky" write time on an fsp. This will always be
1091  returned on all future write time queries and set on close.
1092 ******************************************************************/
1093
1094 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1095 {
1096         if (null_timespec(mtime)) {
1097                 return true;
1098         }
1099
1100         fsp->write_time_forced = true;
1101         TALLOC_FREE(fsp->update_write_time_event);
1102
1103         return set_sticky_write_time_path(fsp->file_id, mtime);
1104 }
1105
1106 /******************************************************************
1107  Set a create time EA.
1108 ******************************************************************/
1109
1110 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1111                                 const struct smb_filename *psmb_fname,
1112                                 struct timespec create_time)
1113 {
1114         struct smb_filename *smb_fname;
1115         uint32_t dosmode;
1116         int ret;
1117
1118         if (!lp_store_dos_attributes(SNUM(conn))) {
1119                 return NT_STATUS_OK;
1120         }
1121
1122         smb_fname = synthetic_smb_fname(talloc_tos(),
1123                                         psmb_fname->base_name,
1124                                         NULL,
1125                                         &psmb_fname->st,
1126                                         psmb_fname->flags);
1127
1128         if (smb_fname == NULL) {
1129                 return NT_STATUS_NO_MEMORY;
1130         }
1131
1132         dosmode = dos_mode(conn, smb_fname);
1133
1134         smb_fname->st.st_ex_btime = create_time;
1135
1136         ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1137         if (ret == -1) {
1138                 return map_nt_error_from_unix(errno);
1139         }
1140
1141         DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1142                 smb_fname_str_dbg(smb_fname)));
1143
1144         return NT_STATUS_OK;
1145 }
1146
1147 /******************************************************************
1148  Return a create time.
1149 ******************************************************************/
1150
1151 struct timespec get_create_timespec(connection_struct *conn,
1152                                 struct files_struct *fsp,
1153                                 const struct smb_filename *smb_fname)
1154 {
1155         return smb_fname->st.st_ex_btime;
1156 }
1157
1158 /******************************************************************
1159  Return a change time (may look at EA in future).
1160 ******************************************************************/
1161
1162 struct timespec get_change_timespec(connection_struct *conn,
1163                                 struct files_struct *fsp,
1164                                 const struct smb_filename *smb_fname)
1165 {
1166         return smb_fname->st.st_ex_mtime;
1167 }
1168
1169 /****************************************************************************
1170  Get a real open file handle we can do meta-data operations on. As it's
1171  going to be used under root access only on meta-data we should look for
1172  any existing open file handle first, and use that in preference (also to
1173  avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1174 ****************************************************************************/
1175
1176 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1177                                 const struct smb_filename *smb_fname,
1178                                 files_struct **ret_fsp,
1179                                 bool *need_close)
1180 {
1181         NTSTATUS status;
1182         files_struct *fsp;
1183         struct file_id file_id;
1184         struct smb_filename *smb_fname_cp = NULL;
1185
1186         *need_close = false;
1187
1188         if (!VALID_STAT(smb_fname->st)) {
1189                 return NT_STATUS_INVALID_PARAMETER;
1190         }
1191
1192         file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1193
1194         for(fsp = file_find_di_first(conn->sconn, file_id);
1195                         fsp;
1196                         fsp = file_find_di_next(fsp)) {
1197                 if (fsp->fh->fd != -1) {
1198                         *ret_fsp = fsp;
1199                         return NT_STATUS_OK;
1200                 }
1201         }
1202
1203         smb_fname_cp = cp_smb_filename(talloc_tos(),
1204                                         smb_fname);
1205         if (smb_fname_cp == NULL) {
1206                 return NT_STATUS_NO_MEMORY;
1207         }
1208
1209         /* Opens an INTERNAL_OPEN_ONLY write handle. */
1210         status = SMB_VFS_CREATE_FILE(
1211                 conn,                                   /* conn */
1212                 NULL,                                   /* req */
1213                 0,                                      /* root_dir_fid */
1214                 smb_fname_cp,                           /* fname */
1215                 FILE_WRITE_ATTRIBUTES,                  /* access_mask */
1216                 (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
1217                         FILE_SHARE_DELETE),
1218                 FILE_OPEN,                              /* create_disposition*/
1219                 0,                                      /* create_options */
1220                 0,                                      /* file_attributes */
1221                 INTERNAL_OPEN_ONLY,                     /* oplock_request */
1222                 NULL,                                   /* lease */
1223                 0,                                      /* allocation_size */
1224                 0,                                      /* private_flags */
1225                 NULL,                                   /* sd */
1226                 NULL,                                   /* ea_list */
1227                 ret_fsp,                                /* result */
1228                 NULL,                                   /* pinfo */
1229                 NULL, NULL);                            /* create context */
1230
1231         TALLOC_FREE(smb_fname_cp);
1232
1233         if (NT_STATUS_IS_OK(status)) {
1234                 *need_close = true;
1235         }
1236         return status;
1237 }