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