s3:pdb_ldap: fix a comment typo
[Samba.git] / source3 / smbd / dosmode.c
blob0f319736758d8662a8d9d16cd9427a5a4a68ad71
1 /*
2 Unix SMB/CIFS implementation.
3 dos mode handling functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) James Peach 2006
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.
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.
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/>.
21 #include "includes.h"
22 #include "librpc/gen_ndr/ndr_xattr.h"
24 static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
26 #if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
27 if (sbuf->st_ex_size > sbuf->st_ex_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
28 return FILE_ATTRIBUTE_SPARSE;
30 #endif
31 return 0;
34 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
36 #ifdef S_ISLNK
37 #if LINKS_READ_ONLY
38 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
39 return aRONLY;
40 #endif
41 #endif
42 return 0;
45 /****************************************************************************
46 Change a dos mode to a unix mode.
47 Base permission for files:
48 if creating file and inheriting (i.e. parent_dir != NULL)
49 apply read/write bits from parent directory.
50 else
51 everybody gets read bit set
52 dos readonly is represented in unix by removing everyone's write bit
53 dos archive is represented in unix by the user's execute bit
54 dos system is represented in unix by the group's execute bit
55 dos hidden is represented in unix by the other's execute bit
56 if !inheriting {
57 Then apply create mask,
58 then add force bits.
60 Base permission for directories:
61 dos directory is represented in unix by unix's dir bit and the exec bit
62 if !inheriting {
63 Then apply create mask,
64 then add force bits.
66 ****************************************************************************/
68 mode_t unix_mode(connection_struct *conn, int dosmode,
69 const struct smb_filename *smb_fname,
70 const char *inherit_from_dir)
72 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
73 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
74 * inheriting. */
76 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
77 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
80 if ((inherit_from_dir != NULL) && lp_inherit_perms(SNUM(conn))) {
81 struct smb_filename *smb_fname_parent = NULL;
82 NTSTATUS status;
84 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
85 smb_fname_str_dbg(smb_fname),
86 inherit_from_dir));
88 status = create_synthetic_smb_fname(talloc_tos(),
89 inherit_from_dir, NULL,
90 NULL, &smb_fname_parent);
91 if (!NT_STATUS_IS_OK(status)) {
92 DEBUG(1,("unix_mode(%s) failed, [dir %s]: %s\n",
93 smb_fname_str_dbg(smb_fname),
94 inherit_from_dir, nt_errstr(status)));
95 return(0);
98 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
99 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
100 smb_fname_str_dbg(smb_fname),
101 inherit_from_dir, strerror(errno)));
102 TALLOC_FREE(smb_fname_parent);
103 return(0); /* *** shouldn't happen! *** */
106 /* Save for later - but explicitly remove setuid bit for safety. */
107 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
108 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
109 smb_fname_str_dbg(smb_fname), (int)dir_mode));
110 /* Clear "result" */
111 result = 0;
112 TALLOC_FREE(smb_fname_parent);
115 if (IS_DOS_DIR(dosmode)) {
116 /* We never make directories read only for the owner as under DOS a user
117 can always create a file in a read-only directory. */
118 result |= (S_IFDIR | S_IWUSR);
120 if (dir_mode) {
121 /* Inherit mode of parent directory. */
122 result |= dir_mode;
123 } else {
124 /* Provisionally add all 'x' bits */
125 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
127 /* Apply directory mask */
128 result &= lp_dir_mask(SNUM(conn));
129 /* Add in force bits */
130 result |= lp_force_dir_mode(SNUM(conn));
132 } else {
133 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
134 result |= S_IXUSR;
136 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
137 result |= S_IXGRP;
139 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
140 result |= S_IXOTH;
142 if (dir_mode) {
143 /* Inherit 666 component of parent directory mode */
144 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
145 } else {
146 /* Apply mode mask */
147 result &= lp_create_mask(SNUM(conn));
148 /* Add in force bits */
149 result |= lp_force_create_mode(SNUM(conn));
153 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
154 (int)result));
155 return(result);
158 /****************************************************************************
159 Change a unix mode to a dos mode.
160 ****************************************************************************/
162 static uint32 dos_mode_from_sbuf(connection_struct *conn,
163 const struct smb_filename *smb_fname)
165 int result = 0;
166 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
168 if (ro_opts == MAP_READONLY_YES) {
169 /* Original Samba method - map inverse of user "w" bit. */
170 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
171 result |= aRONLY;
173 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
174 /* Check actual permissions for read-only. */
175 if (!can_write_to_file(conn, smb_fname)) {
176 result |= aRONLY;
178 } /* Else never set the readonly bit. */
180 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
181 result |= aARCH;
183 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
184 result |= aSYSTEM;
186 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
187 result |= aHIDDEN;
189 if (S_ISDIR(smb_fname->st.st_ex_mode))
190 result = aDIR | (result & aRONLY);
192 result |= set_sparse_flag(&smb_fname->st);
193 result |= set_link_read_only_flag(&smb_fname->st);
195 DEBUG(8,("dos_mode_from_sbuf returning "));
197 if (result & aHIDDEN) DEBUG(8, ("h"));
198 if (result & aRONLY ) DEBUG(8, ("r"));
199 if (result & aSYSTEM) DEBUG(8, ("s"));
200 if (result & aDIR ) DEBUG(8, ("d"));
201 if (result & aARCH ) DEBUG(8, ("a"));
203 DEBUG(8,("\n"));
204 return result;
207 /****************************************************************************
208 Get DOS attributes from an EA.
209 This can also pull the create time into the stat struct inside smb_fname.
210 ****************************************************************************/
212 static bool get_ea_dos_attribute(connection_struct *conn,
213 struct smb_filename *smb_fname,
214 uint32 *pattr)
216 struct xattr_DOSATTRIB dosattrib;
217 enum ndr_err_code ndr_err;
218 DATA_BLOB blob;
219 ssize_t sizeret;
220 fstring attrstr;
221 uint32_t dosattr;
223 if (!lp_store_dos_attributes(SNUM(conn))) {
224 return False;
227 /* Don't reset pattr to zero as we may already have filename-based attributes we
228 need to preserve. */
230 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
231 SAMBA_XATTR_DOS_ATTRIB, attrstr,
232 sizeof(attrstr));
233 if (sizeret == -1) {
234 if (errno == ENOSYS
235 #if defined(ENOTSUP)
236 || errno == ENOTSUP) {
237 #else
239 #endif
240 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute "
241 "from EA on file %s: Error = %s\n",
242 smb_fname_str_dbg(smb_fname),
243 strerror(errno)));
244 set_store_dos_attributes(SNUM(conn), False);
246 return False;
249 blob.data = (uint8_t *)attrstr;
250 blob.length = sizeret;
252 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), NULL, &dosattrib,
253 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
255 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
256 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
258 switch (dosattrib.version) {
259 case 0xFFFF:
260 dosattr = dosattrib.info.compatinfoFFFF.attrib;
261 break;
262 case 1:
263 dosattr = dosattrib.info.info1.attrib;
264 if (!null_nttime(dosattrib.info.info1.create_time)) {
265 struct timespec create_time =
266 nt_time_to_unix_timespec(
267 &dosattrib.info.info1.create_time);
269 update_stat_ex_create_time(&smb_fname->st,
270 create_time);
272 DEBUG(10,("get_ea_dos_attributes: file %s case 1 "
273 "set btime %s\n",
274 smb_fname_str_dbg(smb_fname),
275 time_to_asc(convert_timespec_to_time_t(
276 create_time)) ));
278 break;
279 case 2:
280 dosattr = dosattrib.info.oldinfo2.attrib;
281 /* Don't know what flags to check for this case. */
282 break;
283 case 3:
284 dosattr = dosattrib.info.info3.attrib;
285 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
286 !null_nttime(dosattrib.info.info3.create_time)) {
287 struct timespec create_time =
288 nt_time_to_unix_timespec(
289 &dosattrib.info.info3.create_time);
291 update_stat_ex_create_time(&smb_fname->st,
292 create_time);
294 DEBUG(10,("get_ea_dos_attributes: file %s case 3 "
295 "set btime %s\n",
296 smb_fname_str_dbg(smb_fname),
297 time_to_asc(convert_timespec_to_time_t(
298 create_time)) ));
300 break;
301 default:
302 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on "
303 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
304 attrstr));
305 return false;
308 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
309 dosattr |= aDIR;
311 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
313 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
315 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
316 if (dosattr & aRONLY ) DEBUG(8, ("r"));
317 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
318 if (dosattr & aDIR ) DEBUG(8, ("d"));
319 if (dosattr & aARCH ) DEBUG(8, ("a"));
321 DEBUG(8,("\n"));
323 return True;
326 /****************************************************************************
327 Set DOS attributes in an EA.
328 Also sets the create time.
329 ****************************************************************************/
331 static bool set_ea_dos_attribute(connection_struct *conn,
332 struct smb_filename *smb_fname,
333 uint32 dosmode)
335 struct xattr_DOSATTRIB dosattrib;
336 enum ndr_err_code ndr_err;
337 DATA_BLOB blob;
338 files_struct *fsp = NULL;
339 bool ret = false;
341 if (!lp_store_dos_attributes(SNUM(conn))) {
342 return False;
345 ZERO_STRUCT(dosattrib);
346 ZERO_STRUCT(blob);
348 dosattrib.version = 3;
349 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
350 XATTR_DOSINFO_CREATE_TIME;
351 dosattrib.info.info3.attrib = dosmode;
352 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
353 smb_fname->st.st_ex_btime);
355 ndr_err = ndr_push_struct_blob(
356 &blob, talloc_tos(), NULL, &dosattrib,
357 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
359 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
360 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
361 ndr_errstr(ndr_err)));
362 return false;
365 if (blob.data == NULL || blob.length == 0) {
366 return false;
369 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
370 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
371 0) == -1) {
372 if((errno != EPERM) && (errno != EACCES)) {
373 if (errno == ENOSYS
374 #if defined(ENOTSUP)
375 || errno == ENOTSUP) {
376 #else
378 #endif
379 DEBUG(1,("set_ea_dos_attributes: Cannot set "
380 "attribute EA on file %s: Error = %s\n",
381 smb_fname_str_dbg(smb_fname),
382 strerror(errno) ));
383 set_store_dos_attributes(SNUM(conn), False);
385 return false;
388 /* We want DOS semantics, ie allow non owner with write permission to change the
389 bits on a file. Just like file_ntimes below.
392 /* Check if we have write access. */
393 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
394 return false;
397 * We need to open the file with write access whilst
398 * still in our current user context. This ensures we
399 * are not violating security in doing the setxattr.
402 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
403 &fsp)))
404 return ret;
405 become_root();
406 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
407 SAMBA_XATTR_DOS_ATTRIB, blob.data,
408 blob.length, 0) == 0) {
409 ret = true;
411 unbecome_root();
412 close_file_fchmod(NULL, fsp);
413 return ret;
415 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
416 (unsigned int)dosmode,
417 smb_fname_str_dbg(smb_fname)));
418 return true;
421 /****************************************************************************
422 Change a unix mode to a dos mode for an ms dfs link.
423 ****************************************************************************/
425 uint32 dos_mode_msdfs(connection_struct *conn,
426 const struct smb_filename *smb_fname)
428 uint32 result = 0;
430 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
432 if (!VALID_STAT(smb_fname->st)) {
433 return 0;
436 /* First do any modifications that depend on the path name. */
437 /* hide files with a name starting with a . */
438 if (lp_hide_dot_files(SNUM(conn))) {
439 const char *p = strrchr_m(smb_fname->base_name, '/');
440 if (p) {
441 p++;
442 } else {
443 p = smb_fname->base_name;
446 /* Only . and .. are not hidden. */
447 if (p[0] == '.' && !((p[1] == '\0') ||
448 (p[1] == '.' && p[2] == '\0'))) {
449 result |= aHIDDEN;
453 result |= dos_mode_from_sbuf(conn, smb_fname);
455 /* Optimization : Only call is_hidden_path if it's not already
456 hidden. */
457 if (!(result & aHIDDEN) &&
458 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
459 result |= aHIDDEN;
462 if (get_Protocol() <= PROTOCOL_LANMAN2) {
463 DEBUG(10,("dos_mode_msdfs : filtering result 0x%x\n",
464 (unsigned int)result ));
465 result &= 0xff;
468 DEBUG(8,("dos_mode_msdfs returning "));
470 if (result & aHIDDEN) DEBUG(8, ("h"));
471 if (result & aRONLY ) DEBUG(8, ("r"));
472 if (result & aSYSTEM) DEBUG(8, ("s"));
473 if (result & aDIR ) DEBUG(8, ("d"));
474 if (result & aARCH ) DEBUG(8, ("a"));
475 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
477 DEBUG(8,("\n"));
479 return(result);
482 #ifdef HAVE_STAT_DOS_FLAGS
483 /****************************************************************************
484 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
485 ****************************************************************************/
487 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
489 uint32_t dos_stat_flags = 0;
491 if (dosmode & aARCH)
492 dos_stat_flags |= UF_DOS_ARCHIVE;
493 if (dosmode & aHIDDEN)
494 dos_stat_flags |= UF_DOS_HIDDEN;
495 if (dosmode & aRONLY)
496 dos_stat_flags |= UF_DOS_RO;
497 if (dosmode & aSYSTEM)
498 dos_stat_flags |= UF_DOS_SYSTEM;
499 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
500 dos_stat_flags |= UF_DOS_NOINDEX;
502 return dos_stat_flags;
505 /****************************************************************************
506 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
507 ****************************************************************************/
509 static bool get_stat_dos_flags(connection_struct *conn,
510 const struct smb_filename *smb_fname,
511 uint32_t *dosmode)
513 SMB_ASSERT(VALID_STAT(smb_fname->st));
514 SMB_ASSERT(dosmode);
516 if (!lp_store_dos_attributes(SNUM(conn))) {
517 return false;
520 DEBUG(5, ("Getting stat dos attributes for %s.\n",
521 smb_fname_str_dbg(smb_fname)));
523 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
524 *dosmode |= aARCH;
525 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
526 *dosmode |= aHIDDEN;
527 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
528 *dosmode |= aRONLY;
529 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
530 *dosmode |= aSYSTEM;
531 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
532 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
533 if (S_ISDIR(smb_fname->st.st_ex_mode))
534 *dosmode |= aDIR;
536 *dosmode |= set_sparse_flag(&smb_fname->st);
537 *dosmode |= set_link_read_only_flag(&smb_fname->st);
539 return true;
542 /****************************************************************************
543 Sets DOS attributes, stored in st_ex_flags of the inode.
544 ****************************************************************************/
546 static bool set_stat_dos_flags(connection_struct *conn,
547 const struct smb_filename *smb_fname,
548 uint32_t dosmode,
549 bool *attributes_changed)
551 uint32_t new_flags = 0;
552 int error = 0;
554 SMB_ASSERT(VALID_STAT(smb_fname->st));
555 SMB_ASSERT(attributes_changed);
557 *attributes_changed = false;
559 if (!lp_store_dos_attributes(SNUM(conn))) {
560 return false;
563 DEBUG(5, ("Setting stat dos attributes for %s.\n",
564 smb_fname_str_dbg(smb_fname)));
566 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
567 dos_attributes_to_stat_dos_flags(dosmode);
569 /* Return early if no flags changed. */
570 if (new_flags == smb_fname->st.st_ex_flags)
571 return true;
573 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
574 smb_fname->st.st_ex_flags));
576 /* Set new flags with chflags. */
577 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
578 if (error) {
579 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
580 "file %s! errno=%d\n", new_flags,
581 smb_fname_str_dbg(smb_fname), errno));
582 return false;
585 *attributes_changed = true;
586 return true;
588 #endif /* HAVE_STAT_DOS_FLAGS */
590 /****************************************************************************
591 Change a unix mode to a dos mode.
592 May also read the create timespec into the stat struct in smb_fname
593 if "store dos attributes" is true.
594 ****************************************************************************/
596 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
598 uint32 result = 0;
599 bool offline, used_stat_dos_flags = false;
601 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
603 if (!VALID_STAT(smb_fname->st)) {
604 return 0;
607 /* First do any modifications that depend on the path name. */
608 /* hide files with a name starting with a . */
609 if (lp_hide_dot_files(SNUM(conn))) {
610 const char *p = strrchr_m(smb_fname->base_name,'/');
611 if (p) {
612 p++;
613 } else {
614 p = smb_fname->base_name;
617 /* Only . and .. are not hidden. */
618 if (p[0] == '.' && !((p[1] == '\0') ||
619 (p[1] == '.' && p[2] == '\0'))) {
620 result |= aHIDDEN;
624 #ifdef HAVE_STAT_DOS_FLAGS
625 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
626 #endif
627 if (!used_stat_dos_flags) {
628 /* Get the DOS attributes from an EA by preference. */
629 if (get_ea_dos_attribute(conn, smb_fname, &result)) {
630 result |= set_sparse_flag(&smb_fname->st);
631 } else {
632 result |= dos_mode_from_sbuf(conn, smb_fname);
636 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname->base_name, &smb_fname->st);
637 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
638 result |= FILE_ATTRIBUTE_OFFLINE;
641 /* Optimization : Only call is_hidden_path if it's not already
642 hidden. */
643 if (!(result & aHIDDEN) &&
644 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
645 result |= aHIDDEN;
648 if (get_Protocol() <= PROTOCOL_LANMAN2) {
649 DEBUG(10,("dos_mode : filtering result 0x%x\n",
650 (unsigned int)result ));
651 result &= 0xff;
654 DEBUG(8,("dos_mode returning "));
656 if (result & aHIDDEN) DEBUG(8, ("h"));
657 if (result & aRONLY ) DEBUG(8, ("r"));
658 if (result & aSYSTEM) DEBUG(8, ("s"));
659 if (result & aDIR ) DEBUG(8, ("d"));
660 if (result & aARCH ) DEBUG(8, ("a"));
661 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
663 DEBUG(8,("\n"));
665 return(result);
668 /*******************************************************************
669 chmod a file - but preserve some bits.
670 If "store dos attributes" is also set it will store the create time
671 from the stat struct in smb_fname (in NTTIME format) in the EA
672 attribute also.
673 ********************************************************************/
675 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
676 uint32 dosmode, const char *parent_dir, bool newfile)
678 int mask=0;
679 mode_t tmp;
680 mode_t unixmode;
681 int ret = -1, lret = -1;
682 uint32_t old_mode;
683 struct timespec new_create_timespec;
685 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
686 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
688 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
689 dosmode, smb_fname_str_dbg(smb_fname)));
691 unixmode = smb_fname->st.st_ex_mode;
693 get_acl_group_bits(conn, smb_fname->base_name,
694 &smb_fname->st.st_ex_mode);
696 if (S_ISDIR(smb_fname->st.st_ex_mode))
697 dosmode |= aDIR;
698 else
699 dosmode &= ~aDIR;
701 new_create_timespec = smb_fname->st.st_ex_btime;
703 old_mode = dos_mode(conn, smb_fname);
705 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
706 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
707 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname->base_name);
708 if (lret == -1) {
709 DEBUG(0, ("set_dos_mode: client has asked to "
710 "set FILE_ATTRIBUTE_OFFLINE to "
711 "%s/%s but there was an error while "
712 "setting it or it is not "
713 "supported.\n", parent_dir,
714 smb_fname_str_dbg(smb_fname)));
719 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
720 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
722 if (old_mode == dosmode &&
723 (timespec_compare(&new_create_timespec,
724 &smb_fname->st.st_ex_btime) == 0)) {
725 smb_fname->st.st_ex_mode = unixmode;
726 return(0);
729 smb_fname->st.st_ex_btime = new_create_timespec;
731 #ifdef HAVE_STAT_DOS_FLAGS
733 bool attributes_changed;
735 if (set_stat_dos_flags(conn, smb_fname, dosmode,
736 &attributes_changed))
738 if (!newfile && attributes_changed) {
739 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
740 FILE_NOTIFY_CHANGE_ATTRIBUTES,
741 smb_fname->base_name);
743 smb_fname->st.st_ex_mode = unixmode;
744 return 0;
747 #endif
748 /* Store the DOS attributes in an EA by preference. */
749 if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
750 if (!newfile) {
751 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
752 FILE_NOTIFY_CHANGE_ATTRIBUTES,
753 smb_fname->base_name);
755 smb_fname->st.st_ex_mode = unixmode;
756 return 0;
759 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
761 /* preserve the s bits */
762 mask |= (S_ISUID | S_ISGID);
764 /* preserve the t bit */
765 #ifdef S_ISVTX
766 mask |= S_ISVTX;
767 #endif
769 /* possibly preserve the x bits */
770 if (!MAP_ARCHIVE(conn))
771 mask |= S_IXUSR;
772 if (!MAP_SYSTEM(conn))
773 mask |= S_IXGRP;
774 if (!MAP_HIDDEN(conn))
775 mask |= S_IXOTH;
777 unixmode |= (smb_fname->st.st_ex_mode & mask);
779 /* if we previously had any r bits set then leave them alone */
780 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
781 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
782 unixmode |= tmp;
785 /* if we previously had any w bits set then leave them alone
786 whilst adding in the new w bits, if the new mode is not rdonly */
787 if (!IS_DOS_READONLY(dosmode)) {
788 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
791 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
792 if (ret == 0) {
793 if(!newfile || (lret != -1)) {
794 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
795 FILE_NOTIFY_CHANGE_ATTRIBUTES,
796 smb_fname->base_name);
798 smb_fname->st.st_ex_mode = unixmode;
799 return 0;
802 if((errno != EPERM) && (errno != EACCES))
803 return -1;
805 if(!lp_dos_filemode(SNUM(conn)))
806 return -1;
808 /* We want DOS semantics, ie allow non owner with write permission to change the
809 bits on a file. Just like file_ntimes below.
812 /* Check if we have write access. */
813 if (CAN_WRITE(conn)) {
815 * We need to open the file with write access whilst
816 * still in our current user context. This ensures we
817 * are not violating security in doing the fchmod.
818 * This file open does *not* break any oplocks we are
819 * holding. We need to review this.... may need to
820 * break batch oplocks open by others. JRA.
822 files_struct *fsp;
823 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
824 &fsp)))
825 return -1;
826 become_root();
827 ret = SMB_VFS_FCHMOD(fsp, unixmode);
828 unbecome_root();
829 close_file_fchmod(NULL, fsp);
830 if (!newfile) {
831 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
832 FILE_NOTIFY_CHANGE_ATTRIBUTES,
833 smb_fname->base_name);
835 if (ret == 0) {
836 smb_fname->st.st_ex_mode = unixmode;
840 return( ret );
843 /*******************************************************************
844 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
845 than POSIX.
846 *******************************************************************/
848 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
849 struct smb_file_time *ft)
851 int ret = -1;
853 errno = 0;
855 DEBUG(6, ("file_ntime: actime: %s",
856 time_to_asc(convert_timespec_to_time_t(ft->atime))));
857 DEBUG(6, ("file_ntime: modtime: %s",
858 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
859 DEBUG(6, ("file_ntime: ctime: %s",
860 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
861 DEBUG(6, ("file_ntime: createtime: %s",
862 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
864 /* Don't update the time on read-only shares */
865 /* We need this as set_filetime (which can be called on
866 close and other paths) can end up calling this function
867 without the NEED_WRITE protection. Found by :
868 Leo Weppelman <leo@wau.mis.ah.nl>
871 if (!CAN_WRITE(conn)) {
872 return 0;
875 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
876 return 0;
879 if((errno != EPERM) && (errno != EACCES)) {
880 return -1;
883 if(!lp_dos_filetimes(SNUM(conn))) {
884 return -1;
887 /* We have permission (given by the Samba admin) to
888 break POSIX semantics and allow a user to change
889 the time on a file they don't own but can write to
890 (as DOS does).
893 /* Check if we have write access. */
894 if (can_write_to_file(conn, smb_fname)) {
895 /* We are allowed to become root and change the filetime. */
896 become_root();
897 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
898 unbecome_root();
901 return ret;
904 /******************************************************************
905 Force a "sticky" write time on a pathname. This will always be
906 returned on all future write time queries and set on close.
907 ******************************************************************/
909 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
911 if (null_timespec(mtime)) {
912 return true;
915 if (!set_sticky_write_time(fileid, mtime)) {
916 return false;
919 return true;
922 /******************************************************************
923 Force a "sticky" write time on an fsp. This will always be
924 returned on all future write time queries and set on close.
925 ******************************************************************/
927 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
929 if (null_timespec(mtime)) {
930 return true;
933 fsp->write_time_forced = true;
934 TALLOC_FREE(fsp->update_write_time_event);
936 return set_sticky_write_time_path(fsp->file_id, mtime);
939 /******************************************************************
940 Set a create time EA.
941 ******************************************************************/
943 NTSTATUS set_create_timespec_ea(connection_struct *conn,
944 const struct smb_filename *psmb_fname,
945 struct timespec create_time)
947 NTSTATUS status;
948 struct smb_filename *smb_fname = NULL;
949 uint32_t dosmode;
950 int ret;
952 if (!lp_store_dos_attributes(SNUM(conn))) {
953 return NT_STATUS_OK;
956 status = create_synthetic_smb_fname(talloc_tos(),
957 psmb_fname->base_name,
958 NULL, &psmb_fname->st,
959 &smb_fname);
961 if (!NT_STATUS_IS_OK(status)) {
962 return status;
965 dosmode = dos_mode(conn, smb_fname);
967 smb_fname->st.st_ex_btime = create_time;
969 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
970 if (ret == -1) {
971 map_nt_error_from_unix(errno);
974 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
975 smb_fname_str_dbg(smb_fname)));
977 return NT_STATUS_OK;
980 /******************************************************************
981 Return a create time.
982 ******************************************************************/
984 struct timespec get_create_timespec(connection_struct *conn,
985 struct files_struct *fsp,
986 const struct smb_filename *smb_fname)
988 return smb_fname->st.st_ex_btime;
991 /******************************************************************
992 Return a change time (may look at EA in future).
993 ******************************************************************/
995 struct timespec get_change_timespec(connection_struct *conn,
996 struct files_struct *fsp,
997 const struct smb_filename *smb_fname)
999 return smb_fname->st.st_ex_mtime;