Fix bug #7996 - sgid bit lost on folder rename.
[nivanova/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 aRONLY;
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 |= aRONLY;
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 |= aRONLY;
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 |= aSYSTEM;
190
191         if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
192                 result |= aHIDDEN;   
193
194         if (S_ISDIR(smb_fname->st.st_ex_mode))
195                 result = aDIR | (result & aRONLY);
196
197         result |= set_link_read_only_flag(&smb_fname->st);
198
199         DEBUG(8,("dos_mode_from_sbuf returning "));
200
201         if (result & aHIDDEN) DEBUG(8, ("h"));
202         if (result & aRONLY ) DEBUG(8, ("r"));
203         if (result & aSYSTEM) 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 & aHIDDEN) DEBUG(8, ("h"));
329         if (dosattr & aRONLY ) DEBUG(8, ("r"));
330         if (dosattr & aSYSTEM) 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         files_struct *fsp = NULL;
352         bool ret = false;
353
354         if (!lp_store_dos_attributes(SNUM(conn))) {
355                 return False;
356         }
357
358         ZERO_STRUCT(dosattrib);
359         ZERO_STRUCT(blob);
360
361         dosattrib.version = 3;
362         dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
363                                         XATTR_DOSINFO_CREATE_TIME;
364         dosattrib.info.info3.attrib = dosmode;
365         unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
366                                 smb_fname->st.st_ex_btime);
367
368         DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
369                 (unsigned int)dosmode,
370                 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
371                 smb_fname_str_dbg(smb_fname) ));
372
373         ndr_err = ndr_push_struct_blob(
374                         &blob, talloc_tos(), &dosattrib,
375                         (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
376
377         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
378                 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
379                         ndr_errstr(ndr_err)));
380                 return false;
381         }
382
383         if (blob.data == NULL || blob.length == 0) {
384                 return false;
385         }
386
387         if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
388                              SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
389                              0) == -1) {
390                 if((errno != EPERM) && (errno != EACCES)) {
391                         if (errno == ENOSYS
392 #if defined(ENOTSUP)
393                                 || errno == ENOTSUP) {
394 #else
395                                 ) {
396 #endif
397                                 DEBUG(1,("set_ea_dos_attributes: Cannot set "
398                                          "attribute EA on file %s: Error = %s\n",
399                                          smb_fname_str_dbg(smb_fname),
400                                          strerror(errno) ));
401                                 set_store_dos_attributes(SNUM(conn), False);
402                         }
403                         return false;
404                 }
405
406                 /* We want DOS semantics, ie allow non owner with write permission to change the
407                         bits on a file. Just like file_ntimes below.
408                 */
409
410                 /* Check if we have write access. */
411                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
412                         return false;
413
414                 /*
415                  * We need to open the file with write access whilst
416                  * still in our current user context. This ensures we
417                  * are not violating security in doing the setxattr.
418                  */
419
420                 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
421                                                       &fsp)))
422                         return ret;
423                 become_root();
424                 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
425                                      SAMBA_XATTR_DOS_ATTRIB, blob.data,
426                                      blob.length, 0) == 0) {
427                         ret = true;
428                 }
429                 unbecome_root();
430                 close_file(NULL, fsp, NORMAL_CLOSE);
431                 return ret;
432         }
433         DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
434                 (unsigned int)dosmode,
435                 smb_fname_str_dbg(smb_fname)));
436         return true;
437 }
438
439 /****************************************************************************
440  Change a unix mode to a dos mode for an ms dfs link.
441 ****************************************************************************/
442
443 uint32 dos_mode_msdfs(connection_struct *conn,
444                       const struct smb_filename *smb_fname)
445 {
446         uint32 result = 0;
447
448         DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
449
450         if (!VALID_STAT(smb_fname->st)) {
451                 return 0;
452         }
453
454         /* First do any modifications that depend on the path name. */
455         /* hide files with a name starting with a . */
456         if (lp_hide_dot_files(SNUM(conn))) {
457                 const char *p = strrchr_m(smb_fname->base_name, '/');
458                 if (p) {
459                         p++;
460                 } else {
461                         p = smb_fname->base_name;
462                 }
463
464                 /* Only . and .. are not hidden. */
465                 if (p[0] == '.' && !((p[1] == '\0') ||
466                                 (p[1] == '.' && p[2] == '\0'))) {
467                         result |= aHIDDEN;
468                 }
469         }
470
471         result |= dos_mode_from_sbuf(conn, smb_fname);
472
473         /* Optimization : Only call is_hidden_path if it's not already
474            hidden. */
475         if (!(result & aHIDDEN) &&
476             IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
477                 result |= aHIDDEN;
478         }
479
480         if (result == 0) {
481                 result = FILE_ATTRIBUTE_NORMAL;
482         }
483
484         result = filter_mode_by_protocol(result);
485
486         DEBUG(8,("dos_mode_msdfs returning "));
487
488         if (result & aHIDDEN) DEBUG(8, ("h"));
489         if (result & aRONLY ) DEBUG(8, ("r"));
490         if (result & aSYSTEM) DEBUG(8, ("s"));
491         if (result & aDIR   ) DEBUG(8, ("d"));
492         if (result & aARCH  ) DEBUG(8, ("a"));
493         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
494
495         DEBUG(8,("\n"));
496
497         return(result);
498 }
499
500 #ifdef HAVE_STAT_DOS_FLAGS
501 /****************************************************************************
502  Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
503 ****************************************************************************/
504
505 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
506 {
507         uint32_t dos_stat_flags = 0;
508
509         if (dosmode & aARCH)
510                 dos_stat_flags |= UF_DOS_ARCHIVE;
511         if (dosmode & aHIDDEN)
512                 dos_stat_flags |= UF_DOS_HIDDEN;
513         if (dosmode & aRONLY)
514                 dos_stat_flags |= UF_DOS_RO;
515         if (dosmode & aSYSTEM)
516                 dos_stat_flags |= UF_DOS_SYSTEM;
517         if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
518                 dos_stat_flags |= UF_DOS_NOINDEX;
519
520         return dos_stat_flags;
521 }
522
523 /****************************************************************************
524  Gets DOS attributes, accessed via st_ex_flags in the stat struct.
525 ****************************************************************************/
526
527 static bool get_stat_dos_flags(connection_struct *conn,
528                                const struct smb_filename *smb_fname,
529                                uint32_t *dosmode)
530 {
531         SMB_ASSERT(VALID_STAT(smb_fname->st));
532         SMB_ASSERT(dosmode);
533
534         if (!lp_store_dos_attributes(SNUM(conn))) {
535                 return false;
536         }
537
538         DEBUG(5, ("Getting stat dos attributes for %s.\n",
539                   smb_fname_str_dbg(smb_fname)));
540
541         if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
542                 *dosmode |= aARCH;
543         if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
544                 *dosmode |= aHIDDEN;
545         if (smb_fname->st.st_ex_flags & UF_DOS_RO)
546                 *dosmode |= aRONLY;
547         if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
548                 *dosmode |= aSYSTEM;
549         if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
550                 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
551         if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
552                 *dosmode |= FILE_ATTRIBUTE_SPARSE;
553         if (S_ISDIR(smb_fname->st.st_ex_mode))
554                 *dosmode |= aDIR;
555
556         *dosmode |= set_link_read_only_flag(&smb_fname->st);
557
558         return true;
559 }
560
561 /****************************************************************************
562  Sets DOS attributes, stored in st_ex_flags of the inode.
563 ****************************************************************************/
564
565 static bool set_stat_dos_flags(connection_struct *conn,
566                                const struct smb_filename *smb_fname,
567                                uint32_t dosmode,
568                                bool *attributes_changed)
569 {
570         uint32_t new_flags = 0;
571         int error = 0;
572
573         SMB_ASSERT(VALID_STAT(smb_fname->st));
574         SMB_ASSERT(attributes_changed);
575
576         *attributes_changed = false;
577
578         if (!lp_store_dos_attributes(SNUM(conn))) {
579                 return false;
580         }
581
582         DEBUG(5, ("Setting stat dos attributes for %s.\n",
583                   smb_fname_str_dbg(smb_fname)));
584
585         new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
586                      dos_attributes_to_stat_dos_flags(dosmode);
587
588         /* Return early if no flags changed. */
589         if (new_flags == smb_fname->st.st_ex_flags)
590                 return true;
591
592         DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
593                   smb_fname->st.st_ex_flags));
594
595         /* Set new flags with chflags. */
596         error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
597         if (error) {
598                 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
599                           "file %s! errno=%d\n", new_flags,
600                           smb_fname_str_dbg(smb_fname), errno));
601                 return false;
602         }
603
604         *attributes_changed = true;
605         return true;
606 }
607 #endif /* HAVE_STAT_DOS_FLAGS */
608
609 /****************************************************************************
610  Change a unix mode to a dos mode.
611  May also read the create timespec into the stat struct in smb_fname
612  if "store dos attributes" is true.
613 ****************************************************************************/
614
615 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
616 {
617         uint32 result = 0;
618         bool offline, used_stat_dos_flags = false;
619
620         DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
621
622         if (!VALID_STAT(smb_fname->st)) {
623                 return 0;
624         }
625
626         /* First do any modifications that depend on the path name. */
627         /* hide files with a name starting with a . */
628         if (lp_hide_dot_files(SNUM(conn))) {
629                 const char *p = strrchr_m(smb_fname->base_name,'/');
630                 if (p) {
631                         p++;
632                 } else {
633                         p = smb_fname->base_name;
634                 }
635
636                 /* Only . and .. are not hidden. */
637                 if (p[0] == '.' && !((p[1] == '\0') ||
638                                 (p[1] == '.' && p[2] == '\0'))) {
639                         result |= aHIDDEN;
640                 }
641         }
642
643 #ifdef HAVE_STAT_DOS_FLAGS
644         used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
645 #endif
646         if (!used_stat_dos_flags) {
647                 /* Get the DOS attributes from an EA by preference. */
648                 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
649                         result |= dos_mode_from_sbuf(conn, smb_fname);
650                 }
651         }
652
653         offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
654         if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
655                 result |= FILE_ATTRIBUTE_OFFLINE;
656         }
657
658         /* Optimization : Only call is_hidden_path if it's not already
659            hidden. */
660         if (!(result & aHIDDEN) &&
661             IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
662                 result |= aHIDDEN;
663         }
664
665         if (result == 0) {
666                 result = FILE_ATTRIBUTE_NORMAL;
667         }
668
669         result = filter_mode_by_protocol(result);
670
671         DEBUG(8,("dos_mode returning "));
672
673         if (result & aHIDDEN) DEBUG(8, ("h"));
674         if (result & aRONLY ) DEBUG(8, ("r"));
675         if (result & aSYSTEM) DEBUG(8, ("s"));
676         if (result & aDIR   ) DEBUG(8, ("d"));
677         if (result & aARCH  ) DEBUG(8, ("a"));
678         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
679
680         DEBUG(8,("\n"));
681
682         return(result);
683 }
684
685 /*******************************************************************
686  chmod a file - but preserve some bits.
687  If "store dos attributes" is also set it will store the create time
688  from the stat struct in smb_fname (in NTTIME format) in the EA
689  attribute also.
690 ********************************************************************/
691
692 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
693                      uint32 dosmode, const char *parent_dir, bool newfile)
694 {
695         int mask=0;
696         mode_t tmp;
697         mode_t unixmode;
698         int ret = -1, lret = -1;
699         uint32_t old_mode;
700         struct timespec new_create_timespec;
701
702         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
703         dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
704
705         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
706                   dosmode, smb_fname_str_dbg(smb_fname)));
707
708         unixmode = smb_fname->st.st_ex_mode;
709
710         get_acl_group_bits(conn, smb_fname->base_name,
711                            &smb_fname->st.st_ex_mode);
712
713         if (S_ISDIR(smb_fname->st.st_ex_mode))
714                 dosmode |= aDIR;
715         else
716                 dosmode &= ~aDIR;
717
718         new_create_timespec = smb_fname->st.st_ex_btime;
719
720         old_mode = dos_mode(conn, smb_fname);
721
722         if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
723                 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
724                         lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
725                         if (lret == -1) {
726                                 DEBUG(0, ("set_dos_mode: client has asked to "
727                                           "set FILE_ATTRIBUTE_OFFLINE to "
728                                           "%s/%s but there was an error while "
729                                           "setting it or it is not "
730                                           "supported.\n", parent_dir,
731                                           smb_fname_str_dbg(smb_fname)));
732                         }
733                 }
734         }
735
736         dosmode  &= ~FILE_ATTRIBUTE_OFFLINE;
737         old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
738
739         smb_fname->st.st_ex_btime = new_create_timespec;
740
741 #ifdef HAVE_STAT_DOS_FLAGS
742         {
743                 bool attributes_changed;
744
745                 if (set_stat_dos_flags(conn, smb_fname, dosmode,
746                                        &attributes_changed))
747                 {
748                         if (!newfile && attributes_changed) {
749                                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
750                                     FILE_NOTIFY_CHANGE_ATTRIBUTES,
751                                     smb_fname->base_name);
752                         }
753                         smb_fname->st.st_ex_mode = unixmode;
754                         return 0;
755                 }
756         }
757 #endif
758         /* Store the DOS attributes in an EA by preference. */
759         if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
760                 if (!newfile) {
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         unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
770
771         /* preserve the s bits */
772         mask |= (S_ISUID | S_ISGID);
773
774         /* preserve the t bit */
775 #ifdef S_ISVTX
776         mask |= S_ISVTX;
777 #endif
778
779         /* possibly preserve the x bits */
780         if (!MAP_ARCHIVE(conn))
781                 mask |= S_IXUSR;
782         if (!MAP_SYSTEM(conn))
783                 mask |= S_IXGRP;
784         if (!MAP_HIDDEN(conn))
785                 mask |= S_IXOTH;
786
787         unixmode |= (smb_fname->st.st_ex_mode & mask);
788
789         /* if we previously had any r bits set then leave them alone */
790         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
791                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
792                 unixmode |= tmp;
793         }
794
795         /* if we previously had any w bits set then leave them alone 
796                 whilst adding in the new w bits, if the new mode is not rdonly */
797         if (!IS_DOS_READONLY(dosmode)) {
798                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
799         }
800
801         /*
802          * From the chmod 2 man page:
803          *
804          * "If the calling process is not privileged, and the group of the file
805          * does not match the effective group ID of the process or one of its
806          * supplementary group IDs, the S_ISGID bit will be turned off, but
807          * this will not cause an error to be returned."
808          *
809          * Simply refuse to do the chmod in this case.
810          */
811
812         if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
813                         geteuid() != sec_initial_uid() &&
814                         !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
815                 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
816                         "set for directory %s\n",
817                         smb_fname_str_dbg(smb_fname)));
818                 errno = EPERM;
819                 return -1;
820         }
821
822         ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
823         if (ret == 0) {
824                 if(!newfile || (lret != -1)) {
825                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
826                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
827                                      smb_fname->base_name);
828                 }
829                 smb_fname->st.st_ex_mode = unixmode;
830                 return 0;
831         }
832
833         if((errno != EPERM) && (errno != EACCES))
834                 return -1;
835
836         if(!lp_dos_filemode(SNUM(conn)))
837                 return -1;
838
839         /* We want DOS semantics, ie allow non owner with write permission to change the
840                 bits on a file. Just like file_ntimes below.
841         */
842
843         /* Check if we have write access. */
844         if (CAN_WRITE(conn)) {
845                 /*
846                  * We need to open the file with write access whilst
847                  * still in our current user context. This ensures we
848                  * are not violating security in doing the fchmod.
849                  */
850                 files_struct *fsp;
851                 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
852                                      &fsp)))
853                         return -1;
854                 become_root();
855                 ret = SMB_VFS_FCHMOD(fsp, unixmode);
856                 unbecome_root();
857                 close_file(NULL, fsp, NORMAL_CLOSE);
858                 if (!newfile) {
859                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
860                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
861                                      smb_fname->base_name);
862                 }
863                 if (ret == 0) {
864                         smb_fname->st.st_ex_mode = unixmode;
865                 }
866         }
867
868         return( ret );
869 }
870
871
872 NTSTATUS file_set_sparse(connection_struct *conn,
873                          files_struct *fsp,
874                          bool sparse)
875 {
876         uint32_t old_dosmode;
877         uint32_t new_dosmode;
878         NTSTATUS status;
879
880         if (!CAN_WRITE(conn)) {
881                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
882                         "on readonly share[%s]\n",
883                         smb_fname_str_dbg(fsp->fsp_name),
884                         sparse,
885                         lp_servicename(SNUM(conn))));
886                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
887         }
888
889         if (!(fsp->access_mask & FILE_WRITE_DATA) &&
890                         !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
891                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
892                         "access_mask[0x%08X] - access denied\n",
893                         smb_fname_str_dbg(fsp->fsp_name),
894                         sparse,
895                         fsp->access_mask));
896                 return NT_STATUS_ACCESS_DENIED;
897         }
898
899         DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
900                   sparse, smb_fname_str_dbg(fsp->fsp_name)));
901
902         if (!lp_store_dos_attributes(SNUM(conn))) {
903                 return NT_STATUS_INVALID_DEVICE_REQUEST;
904         }
905
906         status = vfs_stat_fsp(fsp);
907         if (!NT_STATUS_IS_OK(status)) {
908                 return status;
909         }
910
911         old_dosmode = dos_mode(conn, fsp->fsp_name);
912
913         if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
914                 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
915         } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
916                 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
917         } else {
918                 return NT_STATUS_OK;
919         }
920
921         /* Store the DOS attributes in an EA. */
922         if (!set_ea_dos_attribute(conn, fsp->fsp_name,
923                                   new_dosmode)) {
924                 if (errno == 0) {
925                         errno = EIO;
926                 }
927                 return map_nt_error_from_unix(errno);
928         }
929
930         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
931                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
932                      fsp->fsp_name->base_name);
933
934         fsp->is_sparse = sparse;
935
936         return NT_STATUS_OK;
937 }
938
939 /*******************************************************************
940  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
941  than POSIX.
942 *******************************************************************/
943
944 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
945                 struct smb_file_time *ft)
946 {
947         int ret = -1;
948
949         errno = 0;
950
951         DEBUG(6, ("file_ntime: actime: %s",
952                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
953         DEBUG(6, ("file_ntime: modtime: %s",
954                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
955         DEBUG(6, ("file_ntime: ctime: %s",
956                   time_to_asc(convert_timespec_to_time_t(ft->ctime))));
957         DEBUG(6, ("file_ntime: createtime: %s",
958                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
959
960         /* Don't update the time on read-only shares */
961         /* We need this as set_filetime (which can be called on
962            close and other paths) can end up calling this function
963            without the NEED_WRITE protection. Found by : 
964            Leo Weppelman <leo@wau.mis.ah.nl>
965         */
966
967         if (!CAN_WRITE(conn)) {
968                 return 0;
969         }
970
971         if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
972                 return 0;
973         }
974
975         if((errno != EPERM) && (errno != EACCES)) {
976                 return -1;
977         }
978
979         if(!lp_dos_filetimes(SNUM(conn))) {
980                 return -1;
981         }
982
983         /* We have permission (given by the Samba admin) to
984            break POSIX semantics and allow a user to change
985            the time on a file they don't own but can write to
986            (as DOS does).
987          */
988
989         /* Check if we have write access. */
990         if (can_write_to_file(conn, smb_fname)) {
991                 /* We are allowed to become root and change the filetime. */
992                 become_root();
993                 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
994                 unbecome_root();
995         }
996
997         return ret;
998 }
999
1000 /******************************************************************
1001  Force a "sticky" write time on a pathname. This will always be
1002  returned on all future write time queries and set on close.
1003 ******************************************************************/
1004
1005 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1006 {
1007         if (null_timespec(mtime)) {
1008                 return true;
1009         }
1010
1011         if (!set_sticky_write_time(fileid, mtime)) {
1012                 return false;
1013         }
1014
1015         return true;
1016 }
1017
1018 /******************************************************************
1019  Force a "sticky" write time on an fsp. This will always be
1020  returned on all future write time queries and set on close.
1021 ******************************************************************/
1022
1023 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1024 {
1025         if (null_timespec(mtime)) {
1026                 return true;
1027         }
1028
1029         fsp->write_time_forced = true;
1030         TALLOC_FREE(fsp->update_write_time_event);
1031
1032         return set_sticky_write_time_path(fsp->file_id, mtime);
1033 }
1034
1035 /******************************************************************
1036  Set a create time EA.
1037 ******************************************************************/
1038
1039 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1040                                 const struct smb_filename *psmb_fname,
1041                                 struct timespec create_time)
1042 {
1043         NTSTATUS status;
1044         struct smb_filename *smb_fname = NULL;
1045         uint32_t dosmode;
1046         int ret;
1047
1048         if (!lp_store_dos_attributes(SNUM(conn))) {
1049                 return NT_STATUS_OK;
1050         }
1051
1052         status = create_synthetic_smb_fname(talloc_tos(),
1053                                 psmb_fname->base_name,
1054                                 NULL, &psmb_fname->st,
1055                                 &smb_fname);
1056
1057         if (!NT_STATUS_IS_OK(status)) {
1058                 return status;
1059         }
1060
1061         dosmode = dos_mode(conn, smb_fname);
1062
1063         smb_fname->st.st_ex_btime = create_time;
1064
1065         ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1066         if (ret == -1) {
1067                 map_nt_error_from_unix(errno);
1068         }
1069
1070         DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1071                 smb_fname_str_dbg(smb_fname)));
1072
1073         return NT_STATUS_OK;
1074 }
1075
1076 /******************************************************************
1077  Return a create time.
1078 ******************************************************************/
1079
1080 struct timespec get_create_timespec(connection_struct *conn,
1081                                 struct files_struct *fsp,
1082                                 const struct smb_filename *smb_fname)
1083 {
1084         return smb_fname->st.st_ex_btime;
1085 }
1086
1087 /******************************************************************
1088  Return a change time (may look at EA in future).
1089 ******************************************************************/
1090
1091 struct timespec get_change_timespec(connection_struct *conn,
1092                                 struct files_struct *fsp,
1093                                 const struct smb_filename *smb_fname)
1094 {
1095         return smb_fname->st.st_ex_mtime;
1096 }