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