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