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