Add early return in file_set_dosmode() on a read only share.
[amitay/samba.git] / source3 / smbd / dosmode.c
1 /* 
2    Unix SMB/CIFS implementation.
3    dos mode handling functions
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) James Peach 2006
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "librpc/gen_ndr/ndr_xattr.h"
24 #include "../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         if (!CAN_WRITE(conn)) {
710                 errno = EROFS;
711                 return -1;
712         }
713
714         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
715         dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
716
717         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
718                   dosmode, smb_fname_str_dbg(smb_fname)));
719
720         unixmode = smb_fname->st.st_ex_mode;
721
722         get_acl_group_bits(conn, smb_fname->base_name,
723                            &smb_fname->st.st_ex_mode);
724
725         if (S_ISDIR(smb_fname->st.st_ex_mode))
726                 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
727         else
728                 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
729
730         new_create_timespec = smb_fname->st.st_ex_btime;
731
732         old_mode = dos_mode(conn, smb_fname);
733
734         if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
735                 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
736                         lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
737                         if (lret == -1) {
738                                 DEBUG(0, ("set_dos_mode: client has asked to "
739                                           "set FILE_ATTRIBUTE_OFFLINE to "
740                                           "%s/%s but there was an error while "
741                                           "setting it or it is not "
742                                           "supported.\n", parent_dir,
743                                           smb_fname_str_dbg(smb_fname)));
744                         }
745                 }
746         }
747
748         dosmode  &= ~FILE_ATTRIBUTE_OFFLINE;
749         old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
750
751         smb_fname->st.st_ex_btime = new_create_timespec;
752
753 #ifdef HAVE_STAT_DOS_FLAGS
754         {
755                 bool attributes_changed;
756
757                 if (set_stat_dos_flags(conn, smb_fname, dosmode,
758                                        &attributes_changed))
759                 {
760                         if (!newfile && attributes_changed) {
761                                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
762                                     FILE_NOTIFY_CHANGE_ATTRIBUTES,
763                                     smb_fname->base_name);
764                         }
765                         smb_fname->st.st_ex_mode = unixmode;
766                         return 0;
767                 }
768         }
769 #endif
770         /* Store the DOS attributes in an EA by preference. */
771         if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
772                 if (!newfile) {
773                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
774                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
775                                      smb_fname->base_name);
776                 }
777                 smb_fname->st.st_ex_mode = unixmode;
778                 return 0;
779         }
780
781         unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
782
783         /* preserve the file type bits */
784         mask |= S_IFMT;
785
786         /* preserve the s bits */
787         mask |= (S_ISUID | S_ISGID);
788
789         /* preserve the t bit */
790 #ifdef S_ISVTX
791         mask |= S_ISVTX;
792 #endif
793
794         /* possibly preserve the x bits */
795         if (!MAP_ARCHIVE(conn))
796                 mask |= S_IXUSR;
797         if (!MAP_SYSTEM(conn))
798                 mask |= S_IXGRP;
799         if (!MAP_HIDDEN(conn))
800                 mask |= S_IXOTH;
801
802         unixmode |= (smb_fname->st.st_ex_mode & mask);
803
804         /* if we previously had any r bits set then leave them alone */
805         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
806                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
807                 unixmode |= tmp;
808         }
809
810         /* if we previously had any w bits set then leave them alone 
811                 whilst adding in the new w bits, if the new mode is not rdonly */
812         if (!IS_DOS_READONLY(dosmode)) {
813                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
814         }
815
816         /*
817          * From the chmod 2 man page:
818          *
819          * "If the calling process is not privileged, and the group of the file
820          * does not match the effective group ID of the process or one of its
821          * supplementary group IDs, the S_ISGID bit will be turned off, but
822          * this will not cause an error to be returned."
823          *
824          * Simply refuse to do the chmod in this case.
825          */
826
827         if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
828                         geteuid() != sec_initial_uid() &&
829                         !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
830                 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
831                         "set for directory %s\n",
832                         smb_fname_str_dbg(smb_fname)));
833                 errno = EPERM;
834                 return -1;
835         }
836
837         ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
838         if (ret == 0) {
839                 if(!newfile || (lret != -1)) {
840                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
841                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
842                                      smb_fname->base_name);
843                 }
844                 smb_fname->st.st_ex_mode = unixmode;
845                 return 0;
846         }
847
848         if((errno != EPERM) && (errno != EACCES))
849                 return -1;
850
851         if(!lp_dos_filemode(SNUM(conn)))
852                 return -1;
853
854         /* We want DOS semantics, ie allow non owner with write permission to change the
855                 bits on a file. Just like file_ntimes below.
856         */
857
858         /* Check if we have write access. */
859         if (CAN_WRITE(conn)) {
860                 /*
861                  * We need to open the file with write access whilst
862                  * still in our current user context. This ensures we
863                  * are not violating security in doing the fchmod.
864                  */
865                 files_struct *fsp;
866                 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
867                                      &fsp)))
868                         return -1;
869                 become_root();
870                 ret = SMB_VFS_FCHMOD(fsp, unixmode);
871                 unbecome_root();
872                 close_file(NULL, fsp, NORMAL_CLOSE);
873                 if (!newfile) {
874                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
875                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
876                                      smb_fname->base_name);
877                 }
878                 if (ret == 0) {
879                         smb_fname->st.st_ex_mode = unixmode;
880                 }
881         }
882
883         return( ret );
884 }
885
886
887 NTSTATUS file_set_sparse(connection_struct *conn,
888                          files_struct *fsp,
889                          bool sparse)
890 {
891         uint32_t old_dosmode;
892         uint32_t new_dosmode;
893         NTSTATUS status;
894
895         if (!CAN_WRITE(conn)) {
896                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
897                         "on readonly share[%s]\n",
898                         smb_fname_str_dbg(fsp->fsp_name),
899                         sparse,
900                         lp_servicename(talloc_tos(), SNUM(conn))));
901                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
902         }
903
904         if (!(fsp->access_mask & FILE_WRITE_DATA) &&
905                         !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
906                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
907                         "access_mask[0x%08X] - access denied\n",
908                         smb_fname_str_dbg(fsp->fsp_name),
909                         sparse,
910                         fsp->access_mask));
911                 return NT_STATUS_ACCESS_DENIED;
912         }
913
914         DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
915                   sparse, smb_fname_str_dbg(fsp->fsp_name)));
916
917         if (!lp_store_dos_attributes(SNUM(conn))) {
918                 return NT_STATUS_INVALID_DEVICE_REQUEST;
919         }
920
921         status = vfs_stat_fsp(fsp);
922         if (!NT_STATUS_IS_OK(status)) {
923                 return status;
924         }
925
926         old_dosmode = dos_mode(conn, fsp->fsp_name);
927
928         if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
929                 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
930         } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
931                 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
932         } else {
933                 return NT_STATUS_OK;
934         }
935
936         /* Store the DOS attributes in an EA. */
937         if (!set_ea_dos_attribute(conn, fsp->fsp_name,
938                                   new_dosmode)) {
939                 if (errno == 0) {
940                         errno = EIO;
941                 }
942                 return map_nt_error_from_unix(errno);
943         }
944
945         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
946                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
947                      fsp->fsp_name->base_name);
948
949         fsp->is_sparse = sparse;
950
951         return NT_STATUS_OK;
952 }
953
954 /*******************************************************************
955  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
956  than POSIX.
957 *******************************************************************/
958
959 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
960                 struct smb_file_time *ft)
961 {
962         int ret = -1;
963
964         errno = 0;
965
966         DEBUG(6, ("file_ntime: actime: %s",
967                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
968         DEBUG(6, ("file_ntime: modtime: %s",
969                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
970         DEBUG(6, ("file_ntime: ctime: %s",
971                   time_to_asc(convert_timespec_to_time_t(ft->ctime))));
972         DEBUG(6, ("file_ntime: createtime: %s",
973                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
974
975         /* Don't update the time on read-only shares */
976         /* We need this as set_filetime (which can be called on
977            close and other paths) can end up calling this function
978            without the NEED_WRITE protection. Found by : 
979            Leo Weppelman <leo@wau.mis.ah.nl>
980         */
981
982         if (!CAN_WRITE(conn)) {
983                 return 0;
984         }
985
986         if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
987                 return 0;
988         }
989
990         if((errno != EPERM) && (errno != EACCES)) {
991                 return -1;
992         }
993
994         if(!lp_dos_filetimes(SNUM(conn))) {
995                 return -1;
996         }
997
998         /* We have permission (given by the Samba admin) to
999            break POSIX semantics and allow a user to change
1000            the time on a file they don't own but can write to
1001            (as DOS does).
1002          */
1003
1004         /* Check if we have write access. */
1005         if (can_write_to_file(conn, smb_fname)) {
1006                 /* We are allowed to become root and change the filetime. */
1007                 become_root();
1008                 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1009                 unbecome_root();
1010         }
1011
1012         return ret;
1013 }
1014
1015 /******************************************************************
1016  Force a "sticky" write time on a pathname. This will always be
1017  returned on all future write time queries and set on close.
1018 ******************************************************************/
1019
1020 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1021 {
1022         if (null_timespec(mtime)) {
1023                 return true;
1024         }
1025
1026         if (!set_sticky_write_time(fileid, mtime)) {
1027                 return false;
1028         }
1029
1030         return true;
1031 }
1032
1033 /******************************************************************
1034  Force a "sticky" write time on an fsp. This will always be
1035  returned on all future write time queries and set on close.
1036 ******************************************************************/
1037
1038 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1039 {
1040         if (null_timespec(mtime)) {
1041                 return true;
1042         }
1043
1044         fsp->write_time_forced = true;
1045         TALLOC_FREE(fsp->update_write_time_event);
1046
1047         return set_sticky_write_time_path(fsp->file_id, mtime);
1048 }
1049
1050 /******************************************************************
1051  Set a create time EA.
1052 ******************************************************************/
1053
1054 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1055                                 const struct smb_filename *psmb_fname,
1056                                 struct timespec create_time)
1057 {
1058         struct smb_filename *smb_fname;
1059         uint32_t dosmode;
1060         int ret;
1061
1062         if (!lp_store_dos_attributes(SNUM(conn))) {
1063                 return NT_STATUS_OK;
1064         }
1065
1066         smb_fname = synthetic_smb_fname(talloc_tos(), psmb_fname->base_name,
1067                                         NULL, &psmb_fname->st);
1068
1069         if (smb_fname == NULL) {
1070                 return NT_STATUS_NO_MEMORY;
1071         }
1072
1073         dosmode = dos_mode(conn, smb_fname);
1074
1075         smb_fname->st.st_ex_btime = create_time;
1076
1077         ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1078         if (ret == -1) {
1079                 map_nt_error_from_unix(errno);
1080         }
1081
1082         DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1083                 smb_fname_str_dbg(smb_fname)));
1084
1085         return NT_STATUS_OK;
1086 }
1087
1088 /******************************************************************
1089  Return a create time.
1090 ******************************************************************/
1091
1092 struct timespec get_create_timespec(connection_struct *conn,
1093                                 struct files_struct *fsp,
1094                                 const struct smb_filename *smb_fname)
1095 {
1096         return smb_fname->st.st_ex_btime;
1097 }
1098
1099 /******************************************************************
1100  Return a change time (may look at EA in future).
1101 ******************************************************************/
1102
1103 struct timespec get_change_timespec(connection_struct *conn,
1104                                 struct files_struct *fsp,
1105                                 const struct smb_filename *smb_fname)
1106 {
1107         return smb_fname->st.st_ex_mtime;
1108 }