04d27c7a2eb4e6c268bf5758b6cbe1ae52966967
[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         ZERO_STRUCT(dosattrib);
358         ZERO_STRUCT(blob);
359
360         dosattrib.version = 3;
361         dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
362                                         XATTR_DOSINFO_CREATE_TIME;
363         dosattrib.info.info3.attrib = dosmode;
364         unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
365                                 smb_fname->st.st_ex_btime);
366
367         DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
368                 (unsigned int)dosmode,
369                 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
370                 smb_fname_str_dbg(smb_fname) ));
371
372         ndr_err = ndr_push_struct_blob(
373                         &blob, talloc_tos(), &dosattrib,
374                         (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
375
376         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
377                 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
378                         ndr_errstr(ndr_err)));
379                 return false;
380         }
381
382         if (blob.data == NULL || blob.length == 0) {
383                 return false;
384         }
385
386         if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
387                              SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
388                              0) == -1) {
389                 bool ret = false;
390                 files_struct *fsp = NULL;
391
392                 if((errno != EPERM) && (errno != EACCES)) {
393                         if (errno == ENOSYS
394 #if defined(ENOTSUP)
395                                 || errno == ENOTSUP) {
396 #else
397                                 ) {
398 #endif
399                                 DEBUG(1,("set_ea_dos_attributes: Cannot set "
400                                          "attribute EA on file %s: Error = %s\n",
401                                          smb_fname_str_dbg(smb_fname),
402                                          strerror(errno) ));
403                                 set_store_dos_attributes(SNUM(conn), False);
404                         }
405                         return false;
406                 }
407
408                 /* We want DOS semantics, ie allow non owner with write permission to change the
409                         bits on a file. Just like file_ntimes below.
410                 */
411
412                 /* Check if we have write access. */
413                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
414                         return false;
415
416                 if (!can_write_to_file(conn, smb_fname)) {
417                         return false;
418                 }
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         files_struct *fsp = NULL;
709
710         if (!CAN_WRITE(conn)) {
711                 errno = EROFS;
712                 return -1;
713         }
714
715         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
716         dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
717
718         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
719                   dosmode, smb_fname_str_dbg(smb_fname)));
720
721         unixmode = smb_fname->st.st_ex_mode;
722
723         get_acl_group_bits(conn, smb_fname->base_name,
724                            &smb_fname->st.st_ex_mode);
725
726         if (S_ISDIR(smb_fname->st.st_ex_mode))
727                 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
728         else
729                 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
730
731         new_create_timespec = smb_fname->st.st_ex_btime;
732
733         old_mode = dos_mode(conn, smb_fname);
734
735         if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
736                 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
737                         lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
738                         if (lret == -1) {
739                                 DEBUG(0, ("set_dos_mode: client has asked to "
740                                           "set FILE_ATTRIBUTE_OFFLINE to "
741                                           "%s/%s but there was an error while "
742                                           "setting it or it is not "
743                                           "supported.\n", parent_dir,
744                                           smb_fname_str_dbg(smb_fname)));
745                         }
746                 }
747         }
748
749         dosmode  &= ~FILE_ATTRIBUTE_OFFLINE;
750         old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
751
752         smb_fname->st.st_ex_btime = new_create_timespec;
753
754 #ifdef HAVE_STAT_DOS_FLAGS
755         {
756                 bool attributes_changed;
757
758                 if (set_stat_dos_flags(conn, smb_fname, dosmode,
759                                        &attributes_changed))
760                 {
761                         if (!newfile && attributes_changed) {
762                                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
763                                     FILE_NOTIFY_CHANGE_ATTRIBUTES,
764                                     smb_fname->base_name);
765                         }
766                         smb_fname->st.st_ex_mode = unixmode;
767                         return 0;
768                 }
769         }
770 #endif
771         /* Store the DOS attributes in an EA by preference. */
772         if (lp_store_dos_attributes(SNUM(conn))) {
773                 /*
774                  * Don't fall back to using UNIX modes. Finally
775                  * follow the smb.conf manpage.
776                  */
777                 if (!set_ea_dos_attribute(conn, smb_fname, dosmode)) {
778                         return -1;
779                 }
780                 if (!newfile) {
781                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
782                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
783                                      smb_fname->base_name);
784                 }
785                 smb_fname->st.st_ex_mode = unixmode;
786                 return 0;
787         }
788
789         unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
790
791         /* preserve the file type bits */
792         mask |= S_IFMT;
793
794         /* preserve the s bits */
795         mask |= (S_ISUID | S_ISGID);
796
797         /* preserve the t bit */
798 #ifdef S_ISVTX
799         mask |= S_ISVTX;
800 #endif
801
802         /* possibly preserve the x bits */
803         if (!MAP_ARCHIVE(conn))
804                 mask |= S_IXUSR;
805         if (!MAP_SYSTEM(conn))
806                 mask |= S_IXGRP;
807         if (!MAP_HIDDEN(conn))
808                 mask |= S_IXOTH;
809
810         unixmode |= (smb_fname->st.st_ex_mode & mask);
811
812         /* if we previously had any r bits set then leave them alone */
813         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
814                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
815                 unixmode |= tmp;
816         }
817
818         /* if we previously had any w bits set then leave them alone 
819                 whilst adding in the new w bits, if the new mode is not rdonly */
820         if (!IS_DOS_READONLY(dosmode)) {
821                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
822         }
823
824         /*
825          * From the chmod 2 man page:
826          *
827          * "If the calling process is not privileged, and the group of the file
828          * does not match the effective group ID of the process or one of its
829          * supplementary group IDs, the S_ISGID bit will be turned off, but
830          * this will not cause an error to be returned."
831          *
832          * Simply refuse to do the chmod in this case.
833          */
834
835         if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
836                         geteuid() != sec_initial_uid() &&
837                         !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
838                 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
839                         "set for directory %s\n",
840                         smb_fname_str_dbg(smb_fname)));
841                 errno = EPERM;
842                 return -1;
843         }
844
845         ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
846         if (ret == 0) {
847                 if(!newfile || (lret != -1)) {
848                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
849                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
850                                      smb_fname->base_name);
851                 }
852                 smb_fname->st.st_ex_mode = unixmode;
853                 return 0;
854         }
855
856         if((errno != EPERM) && (errno != EACCES))
857                 return -1;
858
859         if(!lp_dos_filemode(SNUM(conn)))
860                 return -1;
861
862         /* We want DOS semantics, ie allow non owner with write permission to change the
863                 bits on a file. Just like file_ntimes below.
864         */
865
866         if (!can_write_to_file(conn, smb_fname)) {
867                 errno = EACCES;
868                 return -1;
869         }
870
871         /*
872          * We need to open the file with write access whilst
873          * still in our current user context. This ensures we
874          * are not violating security in doing the fchmod.
875          */
876         if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
877                              &fsp)))
878                 return -1;
879         become_root();
880         ret = SMB_VFS_FCHMOD(fsp, unixmode);
881         unbecome_root();
882         close_file(NULL, fsp, NORMAL_CLOSE);
883         if (!newfile) {
884                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
885                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
886                              smb_fname->base_name);
887         }
888         if (ret == 0) {
889                 smb_fname->st.st_ex_mode = unixmode;
890         }
891
892         return( ret );
893 }
894
895
896 NTSTATUS file_set_sparse(connection_struct *conn,
897                          files_struct *fsp,
898                          bool sparse)
899 {
900         uint32_t old_dosmode;
901         uint32_t new_dosmode;
902         NTSTATUS status;
903
904         if (!CAN_WRITE(conn)) {
905                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
906                         "on readonly share[%s]\n",
907                         smb_fname_str_dbg(fsp->fsp_name),
908                         sparse,
909                         lp_servicename(talloc_tos(), SNUM(conn))));
910                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
911         }
912
913         if (!(fsp->access_mask & FILE_WRITE_DATA) &&
914                         !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
915                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
916                         "access_mask[0x%08X] - access denied\n",
917                         smb_fname_str_dbg(fsp->fsp_name),
918                         sparse,
919                         fsp->access_mask));
920                 return NT_STATUS_ACCESS_DENIED;
921         }
922
923         DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
924                   sparse, smb_fname_str_dbg(fsp->fsp_name)));
925
926         if (!lp_store_dos_attributes(SNUM(conn))) {
927                 return NT_STATUS_INVALID_DEVICE_REQUEST;
928         }
929
930         status = vfs_stat_fsp(fsp);
931         if (!NT_STATUS_IS_OK(status)) {
932                 return status;
933         }
934
935         old_dosmode = dos_mode(conn, fsp->fsp_name);
936
937         if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
938                 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
939         } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
940                 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
941         } else {
942                 return NT_STATUS_OK;
943         }
944
945         /* Store the DOS attributes in an EA. */
946         if (!set_ea_dos_attribute(conn, fsp->fsp_name,
947                                   new_dosmode)) {
948                 if (errno == 0) {
949                         errno = EIO;
950                 }
951                 return map_nt_error_from_unix(errno);
952         }
953
954         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
955                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
956                      fsp->fsp_name->base_name);
957
958         fsp->is_sparse = sparse;
959
960         return NT_STATUS_OK;
961 }
962
963 /*******************************************************************
964  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
965  than POSIX.
966 *******************************************************************/
967
968 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
969                 struct smb_file_time *ft)
970 {
971         int ret = -1;
972
973         errno = 0;
974
975         DEBUG(6, ("file_ntime: actime: %s",
976                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
977         DEBUG(6, ("file_ntime: modtime: %s",
978                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
979         DEBUG(6, ("file_ntime: ctime: %s",
980                   time_to_asc(convert_timespec_to_time_t(ft->ctime))));
981         DEBUG(6, ("file_ntime: createtime: %s",
982                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
983
984         /* Don't update the time on read-only shares */
985         /* We need this as set_filetime (which can be called on
986            close and other paths) can end up calling this function
987            without the NEED_WRITE protection. Found by : 
988            Leo Weppelman <leo@wau.mis.ah.nl>
989         */
990
991         if (!CAN_WRITE(conn)) {
992                 return 0;
993         }
994
995         if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
996                 return 0;
997         }
998
999         if((errno != EPERM) && (errno != EACCES)) {
1000                 return -1;
1001         }
1002
1003         if(!lp_dos_filetimes(SNUM(conn))) {
1004                 return -1;
1005         }
1006
1007         /* We have permission (given by the Samba admin) to
1008            break POSIX semantics and allow a user to change
1009            the time on a file they don't own but can write to
1010            (as DOS does).
1011          */
1012
1013         /* Check if we have write access. */
1014         if (can_write_to_file(conn, smb_fname)) {
1015                 /* We are allowed to become root and change the filetime. */
1016                 become_root();
1017                 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1018                 unbecome_root();
1019         }
1020
1021         return ret;
1022 }
1023
1024 /******************************************************************
1025  Force a "sticky" write time on a pathname. This will always be
1026  returned on all future write time queries and set on close.
1027 ******************************************************************/
1028
1029 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1030 {
1031         if (null_timespec(mtime)) {
1032                 return true;
1033         }
1034
1035         if (!set_sticky_write_time(fileid, mtime)) {
1036                 return false;
1037         }
1038
1039         return true;
1040 }
1041
1042 /******************************************************************
1043  Force a "sticky" write time on an fsp. This will always be
1044  returned on all future write time queries and set on close.
1045 ******************************************************************/
1046
1047 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1048 {
1049         if (null_timespec(mtime)) {
1050                 return true;
1051         }
1052
1053         fsp->write_time_forced = true;
1054         TALLOC_FREE(fsp->update_write_time_event);
1055
1056         return set_sticky_write_time_path(fsp->file_id, mtime);
1057 }
1058
1059 /******************************************************************
1060  Set a create time EA.
1061 ******************************************************************/
1062
1063 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1064                                 const struct smb_filename *psmb_fname,
1065                                 struct timespec create_time)
1066 {
1067         struct smb_filename *smb_fname;
1068         uint32_t dosmode;
1069         int ret;
1070
1071         if (!lp_store_dos_attributes(SNUM(conn))) {
1072                 return NT_STATUS_OK;
1073         }
1074
1075         smb_fname = synthetic_smb_fname(talloc_tos(), psmb_fname->base_name,
1076                                         NULL, &psmb_fname->st);
1077
1078         if (smb_fname == NULL) {
1079                 return NT_STATUS_NO_MEMORY;
1080         }
1081
1082         dosmode = dos_mode(conn, smb_fname);
1083
1084         smb_fname->st.st_ex_btime = create_time;
1085
1086         ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1087         if (ret == -1) {
1088                 map_nt_error_from_unix(errno);
1089         }
1090
1091         DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1092                 smb_fname_str_dbg(smb_fname)));
1093
1094         return NT_STATUS_OK;
1095 }
1096
1097 /******************************************************************
1098  Return a create time.
1099 ******************************************************************/
1100
1101 struct timespec get_create_timespec(connection_struct *conn,
1102                                 struct files_struct *fsp,
1103                                 const struct smb_filename *smb_fname)
1104 {
1105         return smb_fname->st.st_ex_btime;
1106 }
1107
1108 /******************************************************************
1109  Return a change time (may look at EA in future).
1110 ******************************************************************/
1111
1112 struct timespec get_change_timespec(connection_struct *conn,
1113                                 struct files_struct *fsp,
1114                                 const struct smb_filename *smb_fname)
1115 {
1116         return smb_fname->st.st_ex_mtime;
1117 }