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