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