s3-netlogon: Implement NetrServerGetTrustInfo and NetrGetForestTrustInformation
[Samba.git] / source3 / smbd / dosmode.c
blobc0bf168e5381906c5aad98083342a7a5146db7d3
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"
23 #include "../libcli/security/security.h"
25 static uint32_t filter_mode_by_protocol(uint32_t mode)
27 if (get_Protocol() <= PROTOCOL_LANMAN2) {
28 DEBUG(10,("filter_mode_by_protocol: "
29 "filtering result 0x%x to 0x%x\n",
30 (unsigned int)mode,
31 (unsigned int)(mode & 0x3f) ));
32 mode &= 0x3f;
34 return mode;
37 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
39 #ifdef S_ISLNK
40 #if LINKS_READ_ONLY
41 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
42 return aRONLY;
43 #endif
44 #endif
45 return 0;
48 /****************************************************************************
49 Change a dos mode to a unix mode.
50 Base permission for files:
51 if creating file and inheriting (i.e. parent_dir != NULL)
52 apply read/write bits from parent directory.
53 else
54 everybody gets read bit set
55 dos readonly is represented in unix by removing everyone's write bit
56 dos archive is represented in unix by the user's execute bit
57 dos system is represented in unix by the group's execute bit
58 dos hidden is represented in unix by the other's execute bit
59 if !inheriting {
60 Then apply create mask,
61 then add force bits.
63 Base permission for directories:
64 dos directory is represented in unix by unix's dir bit and the exec bit
65 if !inheriting {
66 Then apply create mask,
67 then add force bits.
69 ****************************************************************************/
71 mode_t unix_mode(connection_struct *conn, int dosmode,
72 const struct smb_filename *smb_fname,
73 const char *inherit_from_dir)
75 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
76 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
77 * inheriting. */
79 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
80 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
83 if ((inherit_from_dir != NULL) && lp_inherit_perms(SNUM(conn))) {
84 struct smb_filename *smb_fname_parent = NULL;
85 NTSTATUS status;
87 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
88 smb_fname_str_dbg(smb_fname),
89 inherit_from_dir));
91 status = create_synthetic_smb_fname(talloc_tos(),
92 inherit_from_dir, NULL,
93 NULL, &smb_fname_parent);
94 if (!NT_STATUS_IS_OK(status)) {
95 DEBUG(1,("unix_mode(%s) failed, [dir %s]: %s\n",
96 smb_fname_str_dbg(smb_fname),
97 inherit_from_dir, nt_errstr(status)));
98 return(0);
101 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
102 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
103 smb_fname_str_dbg(smb_fname),
104 inherit_from_dir, strerror(errno)));
105 TALLOC_FREE(smb_fname_parent);
106 return(0); /* *** shouldn't happen! *** */
109 /* Save for later - but explicitly remove setuid bit for safety. */
110 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
111 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
112 smb_fname_str_dbg(smb_fname), (int)dir_mode));
113 /* Clear "result" */
114 result = 0;
115 TALLOC_FREE(smb_fname_parent);
118 if (IS_DOS_DIR(dosmode)) {
119 /* We never make directories read only for the owner as under DOS a user
120 can always create a file in a read-only directory. */
121 result |= (S_IFDIR | S_IWUSR);
123 if (dir_mode) {
124 /* Inherit mode of parent directory. */
125 result |= dir_mode;
126 } else {
127 /* Provisionally add all 'x' bits */
128 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
130 /* Apply directory mask */
131 result &= lp_dir_mask(SNUM(conn));
132 /* Add in force bits */
133 result |= lp_force_dir_mode(SNUM(conn));
135 } else {
136 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
137 result |= S_IXUSR;
139 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
140 result |= S_IXGRP;
142 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
143 result |= S_IXOTH;
145 if (dir_mode) {
146 /* Inherit 666 component of parent directory mode */
147 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
148 } else {
149 /* Apply mode mask */
150 result &= lp_create_mask(SNUM(conn));
151 /* Add in force bits */
152 result |= lp_force_create_mode(SNUM(conn));
156 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
157 (int)result));
158 return(result);
161 /****************************************************************************
162 Change a unix mode to a dos mode.
163 ****************************************************************************/
165 static uint32 dos_mode_from_sbuf(connection_struct *conn,
166 const struct smb_filename *smb_fname)
168 int result = 0;
169 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
171 if (ro_opts == MAP_READONLY_YES) {
172 /* Original Samba method - map inverse of user "w" bit. */
173 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
174 result |= aRONLY;
176 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
177 /* Check actual permissions for read-only. */
178 if (!can_write_to_file(conn, smb_fname)) {
179 result |= aRONLY;
181 } /* Else never set the readonly bit. */
183 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
184 result |= aARCH;
186 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
187 result |= aSYSTEM;
189 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
190 result |= aHIDDEN;
192 if (S_ISDIR(smb_fname->st.st_ex_mode))
193 result = aDIR | (result & aRONLY);
195 result |= set_link_read_only_flag(&smb_fname->st);
197 DEBUG(8,("dos_mode_from_sbuf returning "));
199 if (result & aHIDDEN) DEBUG(8, ("h"));
200 if (result & aRONLY ) DEBUG(8, ("r"));
201 if (result & aSYSTEM) DEBUG(8, ("s"));
202 if (result & aDIR ) DEBUG(8, ("d"));
203 if (result & aARCH ) DEBUG(8, ("a"));
205 DEBUG(8,("\n"));
206 return result;
209 /****************************************************************************
210 Get DOS attributes from an EA.
211 This can also pull the create time into the stat struct inside smb_fname.
212 ****************************************************************************/
214 static bool get_ea_dos_attribute(connection_struct *conn,
215 struct smb_filename *smb_fname,
216 uint32 *pattr)
218 struct xattr_DOSATTRIB dosattrib;
219 enum ndr_err_code ndr_err;
220 DATA_BLOB blob;
221 ssize_t sizeret;
222 fstring attrstr;
223 uint32_t dosattr;
225 if (!lp_store_dos_attributes(SNUM(conn))) {
226 return False;
229 /* Don't reset pattr to zero as we may already have filename-based attributes we
230 need to preserve. */
232 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
233 SAMBA_XATTR_DOS_ATTRIB, attrstr,
234 sizeof(attrstr));
235 if (sizeret == -1) {
236 if (errno == ENOSYS
237 #if defined(ENOTSUP)
238 || errno == ENOTSUP) {
239 #else
241 #endif
242 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
243 "from EA on file %s: Error = %s\n",
244 smb_fname_str_dbg(smb_fname),
245 strerror(errno)));
246 set_store_dos_attributes(SNUM(conn), False);
248 return False;
251 blob.data = (uint8_t *)attrstr;
252 blob.length = sizeret;
254 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
255 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
257 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
258 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
259 "from EA on file %s: Error = %s\n",
260 smb_fname_str_dbg(smb_fname),
261 ndr_errstr(ndr_err)));
262 return false;
265 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
266 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
268 switch (dosattrib.version) {
269 case 0xFFFF:
270 dosattr = dosattrib.info.compatinfoFFFF.attrib;
271 break;
272 case 1:
273 dosattr = dosattrib.info.info1.attrib;
274 if (!null_nttime(dosattrib.info.info1.create_time)) {
275 struct timespec create_time =
276 nt_time_to_unix_timespec(
277 &dosattrib.info.info1.create_time);
279 update_stat_ex_create_time(&smb_fname->st,
280 create_time);
282 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
283 "set btime %s\n",
284 smb_fname_str_dbg(smb_fname),
285 time_to_asc(convert_timespec_to_time_t(
286 create_time)) ));
288 break;
289 case 2:
290 dosattr = dosattrib.info.oldinfo2.attrib;
291 /* Don't know what flags to check for this case. */
292 break;
293 case 3:
294 dosattr = dosattrib.info.info3.attrib;
295 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
296 !null_nttime(dosattrib.info.info3.create_time)) {
297 struct timespec create_time =
298 nt_time_to_unix_timespec(
299 &dosattrib.info.info3.create_time);
301 update_stat_ex_create_time(&smb_fname->st,
302 create_time);
304 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
305 "set btime %s\n",
306 smb_fname_str_dbg(smb_fname),
307 time_to_asc(convert_timespec_to_time_t(
308 create_time)) ));
310 break;
311 default:
312 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
313 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
314 attrstr));
315 return false;
318 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
319 dosattr |= aDIR;
321 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
322 *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
324 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
326 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
327 if (dosattr & aRONLY ) DEBUG(8, ("r"));
328 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
329 if (dosattr & aDIR ) DEBUG(8, ("d"));
330 if (dosattr & aARCH ) DEBUG(8, ("a"));
332 DEBUG(8,("\n"));
334 return True;
337 /****************************************************************************
338 Set DOS attributes in an EA.
339 Also sets the create time.
340 ****************************************************************************/
342 static bool set_ea_dos_attribute(connection_struct *conn,
343 struct smb_filename *smb_fname,
344 uint32 dosmode)
346 struct xattr_DOSATTRIB dosattrib;
347 enum ndr_err_code ndr_err;
348 DATA_BLOB blob;
349 files_struct *fsp = NULL;
350 bool ret = false;
352 if (!lp_store_dos_attributes(SNUM(conn))) {
353 return False;
356 ZERO_STRUCT(dosattrib);
357 ZERO_STRUCT(blob);
359 dosattrib.version = 3;
360 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
361 XATTR_DOSINFO_CREATE_TIME;
362 dosattrib.info.info3.attrib = dosmode;
363 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
364 smb_fname->st.st_ex_btime);
366 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
367 (unsigned int)dosmode,
368 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
369 smb_fname_str_dbg(smb_fname) ));
371 ndr_err = ndr_push_struct_blob(
372 &blob, talloc_tos(), &dosattrib,
373 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
375 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
376 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
377 ndr_errstr(ndr_err)));
378 return false;
381 if (blob.data == NULL || blob.length == 0) {
382 return false;
385 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
386 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
387 0) == -1) {
388 if((errno != EPERM) && (errno != EACCES)) {
389 if (errno == ENOSYS
390 #if defined(ENOTSUP)
391 || errno == ENOTSUP) {
392 #else
394 #endif
395 DEBUG(1,("set_ea_dos_attributes: Cannot set "
396 "attribute EA on file %s: Error = %s\n",
397 smb_fname_str_dbg(smb_fname),
398 strerror(errno) ));
399 set_store_dos_attributes(SNUM(conn), False);
401 return false;
404 /* We want DOS semantics, ie allow non owner with write permission to change the
405 bits on a file. Just like file_ntimes below.
408 /* Check if we have write access. */
409 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
410 return false;
413 * We need to open the file with write access whilst
414 * still in our current user context. This ensures we
415 * are not violating security in doing the setxattr.
418 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
419 &fsp)))
420 return ret;
421 become_root();
422 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
423 SAMBA_XATTR_DOS_ATTRIB, blob.data,
424 blob.length, 0) == 0) {
425 ret = true;
427 unbecome_root();
428 close_file(NULL, fsp, NORMAL_CLOSE);
429 return ret;
431 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
432 (unsigned int)dosmode,
433 smb_fname_str_dbg(smb_fname)));
434 return true;
437 /****************************************************************************
438 Change a unix mode to a dos mode for an ms dfs link.
439 ****************************************************************************/
441 uint32 dos_mode_msdfs(connection_struct *conn,
442 const struct smb_filename *smb_fname)
444 uint32 result = 0;
446 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
448 if (!VALID_STAT(smb_fname->st)) {
449 return 0;
452 /* First do any modifications that depend on the path name. */
453 /* hide files with a name starting with a . */
454 if (lp_hide_dot_files(SNUM(conn))) {
455 const char *p = strrchr_m(smb_fname->base_name, '/');
456 if (p) {
457 p++;
458 } else {
459 p = smb_fname->base_name;
462 /* Only . and .. are not hidden. */
463 if (p[0] == '.' && !((p[1] == '\0') ||
464 (p[1] == '.' && p[2] == '\0'))) {
465 result |= aHIDDEN;
469 result |= dos_mode_from_sbuf(conn, smb_fname);
471 /* Optimization : Only call is_hidden_path if it's not already
472 hidden. */
473 if (!(result & aHIDDEN) &&
474 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
475 result |= aHIDDEN;
478 if (result == 0) {
479 result = FILE_ATTRIBUTE_NORMAL;
482 result = filter_mode_by_protocol(result);
484 DEBUG(8,("dos_mode_msdfs returning "));
486 if (result & aHIDDEN) DEBUG(8, ("h"));
487 if (result & aRONLY ) DEBUG(8, ("r"));
488 if (result & aSYSTEM) DEBUG(8, ("s"));
489 if (result & aDIR ) DEBUG(8, ("d"));
490 if (result & aARCH ) DEBUG(8, ("a"));
491 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
493 DEBUG(8,("\n"));
495 return(result);
498 #ifdef HAVE_STAT_DOS_FLAGS
499 /****************************************************************************
500 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
501 ****************************************************************************/
503 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
505 uint32_t dos_stat_flags = 0;
507 if (dosmode & aARCH)
508 dos_stat_flags |= UF_DOS_ARCHIVE;
509 if (dosmode & aHIDDEN)
510 dos_stat_flags |= UF_DOS_HIDDEN;
511 if (dosmode & aRONLY)
512 dos_stat_flags |= UF_DOS_RO;
513 if (dosmode & aSYSTEM)
514 dos_stat_flags |= UF_DOS_SYSTEM;
515 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
516 dos_stat_flags |= UF_DOS_NOINDEX;
518 return dos_stat_flags;
521 /****************************************************************************
522 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
523 ****************************************************************************/
525 static bool get_stat_dos_flags(connection_struct *conn,
526 const struct smb_filename *smb_fname,
527 uint32_t *dosmode)
529 SMB_ASSERT(VALID_STAT(smb_fname->st));
530 SMB_ASSERT(dosmode);
532 if (!lp_store_dos_attributes(SNUM(conn))) {
533 return false;
536 DEBUG(5, ("Getting stat dos attributes for %s.\n",
537 smb_fname_str_dbg(smb_fname)));
539 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
540 *dosmode |= aARCH;
541 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
542 *dosmode |= aHIDDEN;
543 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
544 *dosmode |= aRONLY;
545 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
546 *dosmode |= aSYSTEM;
547 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
548 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
549 if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
550 *dosmode |= FILE_ATTRIBUTE_SPARSE;
551 if (S_ISDIR(smb_fname->st.st_ex_mode))
552 *dosmode |= aDIR;
554 *dosmode |= set_link_read_only_flag(&smb_fname->st);
556 return true;
559 /****************************************************************************
560 Sets DOS attributes, stored in st_ex_flags of the inode.
561 ****************************************************************************/
563 static bool set_stat_dos_flags(connection_struct *conn,
564 const struct smb_filename *smb_fname,
565 uint32_t dosmode,
566 bool *attributes_changed)
568 uint32_t new_flags = 0;
569 int error = 0;
571 SMB_ASSERT(VALID_STAT(smb_fname->st));
572 SMB_ASSERT(attributes_changed);
574 *attributes_changed = false;
576 if (!lp_store_dos_attributes(SNUM(conn))) {
577 return false;
580 DEBUG(5, ("Setting stat dos attributes for %s.\n",
581 smb_fname_str_dbg(smb_fname)));
583 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
584 dos_attributes_to_stat_dos_flags(dosmode);
586 /* Return early if no flags changed. */
587 if (new_flags == smb_fname->st.st_ex_flags)
588 return true;
590 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
591 smb_fname->st.st_ex_flags));
593 /* Set new flags with chflags. */
594 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
595 if (error) {
596 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
597 "file %s! errno=%d\n", new_flags,
598 smb_fname_str_dbg(smb_fname), errno));
599 return false;
602 *attributes_changed = true;
603 return true;
605 #endif /* HAVE_STAT_DOS_FLAGS */
607 /****************************************************************************
608 Change a unix mode to a dos mode.
609 May also read the create timespec into the stat struct in smb_fname
610 if "store dos attributes" is true.
611 ****************************************************************************/
613 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
615 uint32 result = 0;
616 bool offline, used_stat_dos_flags = false;
618 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
620 if (!VALID_STAT(smb_fname->st)) {
621 return 0;
624 /* First do any modifications that depend on the path name. */
625 /* hide files with a name starting with a . */
626 if (lp_hide_dot_files(SNUM(conn))) {
627 const char *p = strrchr_m(smb_fname->base_name,'/');
628 if (p) {
629 p++;
630 } else {
631 p = smb_fname->base_name;
634 /* Only . and .. are not hidden. */
635 if (p[0] == '.' && !((p[1] == '\0') ||
636 (p[1] == '.' && p[2] == '\0'))) {
637 result |= aHIDDEN;
641 #ifdef HAVE_STAT_DOS_FLAGS
642 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
643 #endif
644 if (!used_stat_dos_flags) {
645 /* Get the DOS attributes from an EA by preference. */
646 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
647 result |= dos_mode_from_sbuf(conn, smb_fname);
651 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname->base_name, &smb_fname->st);
652 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
653 result |= FILE_ATTRIBUTE_OFFLINE;
656 /* Optimization : Only call is_hidden_path if it's not already
657 hidden. */
658 if (!(result & aHIDDEN) &&
659 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
660 result |= aHIDDEN;
663 if (result == 0) {
664 result = FILE_ATTRIBUTE_NORMAL;
667 result = filter_mode_by_protocol(result);
669 DEBUG(8,("dos_mode returning "));
671 if (result & aHIDDEN) DEBUG(8, ("h"));
672 if (result & aRONLY ) DEBUG(8, ("r"));
673 if (result & aSYSTEM) DEBUG(8, ("s"));
674 if (result & aDIR ) DEBUG(8, ("d"));
675 if (result & aARCH ) DEBUG(8, ("a"));
676 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
678 DEBUG(8,("\n"));
680 return(result);
683 /*******************************************************************
684 chmod a file - but preserve some bits.
685 If "store dos attributes" is also set it will store the create time
686 from the stat struct in smb_fname (in NTTIME format) in the EA
687 attribute also.
688 ********************************************************************/
690 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
691 uint32 dosmode, const char *parent_dir, bool newfile)
693 int mask=0;
694 mode_t tmp;
695 mode_t unixmode;
696 int ret = -1, lret = -1;
697 uint32_t old_mode;
698 struct timespec new_create_timespec;
700 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
701 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
703 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
704 dosmode, smb_fname_str_dbg(smb_fname)));
706 unixmode = smb_fname->st.st_ex_mode;
708 get_acl_group_bits(conn, smb_fname->base_name,
709 &smb_fname->st.st_ex_mode);
711 if (S_ISDIR(smb_fname->st.st_ex_mode))
712 dosmode |= aDIR;
713 else
714 dosmode &= ~aDIR;
716 new_create_timespec = smb_fname->st.st_ex_btime;
718 old_mode = dos_mode(conn, smb_fname);
720 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
721 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
722 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname->base_name);
723 if (lret == -1) {
724 DEBUG(0, ("set_dos_mode: client has asked to "
725 "set FILE_ATTRIBUTE_OFFLINE to "
726 "%s/%s but there was an error while "
727 "setting it or it is not "
728 "supported.\n", parent_dir,
729 smb_fname_str_dbg(smb_fname)));
734 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
735 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
737 smb_fname->st.st_ex_btime = new_create_timespec;
739 #ifdef HAVE_STAT_DOS_FLAGS
741 bool attributes_changed;
743 if (set_stat_dos_flags(conn, smb_fname, dosmode,
744 &attributes_changed))
746 if (!newfile && attributes_changed) {
747 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
748 FILE_NOTIFY_CHANGE_ATTRIBUTES,
749 smb_fname->base_name);
751 smb_fname->st.st_ex_mode = unixmode;
752 return 0;
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);
763 smb_fname->st.st_ex_mode = unixmode;
764 return 0;
767 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
769 /* preserve the s bits */
770 mask |= (S_ISUID | S_ISGID);
772 /* preserve the t bit */
773 #ifdef S_ISVTX
774 mask |= S_ISVTX;
775 #endif
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;
785 unixmode |= (smb_fname->st.st_ex_mode & mask);
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;
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));
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);
806 smb_fname->st.st_ex_mode = unixmode;
807 return 0;
810 if((errno != EPERM) && (errno != EACCES))
811 return -1;
813 if(!lp_dos_filemode(SNUM(conn)))
814 return -1;
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.
820 /* Check if we have write access. */
821 if (CAN_WRITE(conn)) {
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.
827 files_struct *fsp;
828 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
829 &fsp)))
830 return -1;
831 become_root();
832 ret = SMB_VFS_FCHMOD(fsp, unixmode);
833 unbecome_root();
834 close_file(NULL, fsp, NORMAL_CLOSE);
835 if (!newfile) {
836 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
837 FILE_NOTIFY_CHANGE_ATTRIBUTES,
838 smb_fname->base_name);
840 if (ret == 0) {
841 smb_fname->st.st_ex_mode = unixmode;
845 return( ret );
849 NTSTATUS file_set_sparse(connection_struct *conn,
850 files_struct *fsp,
851 bool sparse)
853 uint32_t old_dosmode;
854 uint32_t new_dosmode;
855 NTSTATUS status;
857 if (!CAN_WRITE(conn)) {
858 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
859 "on readonly share[%s]\n",
860 smb_fname_str_dbg(fsp->fsp_name),
861 sparse,
862 lp_servicename(SNUM(conn))));
863 return NT_STATUS_MEDIA_WRITE_PROTECTED;
866 if (!(fsp->access_mask & FILE_WRITE_DATA) &&
867 !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
868 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
869 "access_mask[0x%08X] - access denied\n",
870 smb_fname_str_dbg(fsp->fsp_name),
871 sparse,
872 fsp->access_mask));
873 return NT_STATUS_ACCESS_DENIED;
876 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
877 sparse, smb_fname_str_dbg(fsp->fsp_name)));
879 if (!lp_store_dos_attributes(SNUM(conn))) {
880 return NT_STATUS_INVALID_DEVICE_REQUEST;
883 status = vfs_stat_fsp(fsp);
884 if (!NT_STATUS_IS_OK(status)) {
885 return status;
888 old_dosmode = dos_mode(conn, fsp->fsp_name);
890 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
891 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
892 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
893 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
894 } else {
895 return NT_STATUS_OK;
898 /* Store the DOS attributes in an EA. */
899 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
900 new_dosmode)) {
901 if (errno == 0) {
902 errno = EIO;
904 return map_nt_error_from_unix(errno);
907 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
908 FILE_NOTIFY_CHANGE_ATTRIBUTES,
909 fsp->fsp_name->base_name);
911 fsp->is_sparse = sparse;
913 return NT_STATUS_OK;
916 /*******************************************************************
917 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
918 than POSIX.
919 *******************************************************************/
921 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
922 struct smb_file_time *ft)
924 int ret = -1;
926 errno = 0;
928 DEBUG(6, ("file_ntime: actime: %s",
929 time_to_asc(convert_timespec_to_time_t(ft->atime))));
930 DEBUG(6, ("file_ntime: modtime: %s",
931 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
932 DEBUG(6, ("file_ntime: ctime: %s",
933 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
934 DEBUG(6, ("file_ntime: createtime: %s",
935 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
937 /* Don't update the time on read-only shares */
938 /* We need this as set_filetime (which can be called on
939 close and other paths) can end up calling this function
940 without the NEED_WRITE protection. Found by :
941 Leo Weppelman <leo@wau.mis.ah.nl>
944 if (!CAN_WRITE(conn)) {
945 return 0;
948 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
949 return 0;
952 if((errno != EPERM) && (errno != EACCES)) {
953 return -1;
956 if(!lp_dos_filetimes(SNUM(conn))) {
957 return -1;
960 /* We have permission (given by the Samba admin) to
961 break POSIX semantics and allow a user to change
962 the time on a file they don't own but can write to
963 (as DOS does).
966 /* Check if we have write access. */
967 if (can_write_to_file(conn, smb_fname)) {
968 /* We are allowed to become root and change the filetime. */
969 become_root();
970 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
971 unbecome_root();
974 return ret;
977 /******************************************************************
978 Force a "sticky" write time on a pathname. This will always be
979 returned on all future write time queries and set on close.
980 ******************************************************************/
982 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
984 if (null_timespec(mtime)) {
985 return true;
988 if (!set_sticky_write_time(fileid, mtime)) {
989 return false;
992 return true;
995 /******************************************************************
996 Force a "sticky" write time on an fsp. This will always be
997 returned on all future write time queries and set on close.
998 ******************************************************************/
1000 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1002 if (null_timespec(mtime)) {
1003 return true;
1006 fsp->write_time_forced = true;
1007 TALLOC_FREE(fsp->update_write_time_event);
1009 return set_sticky_write_time_path(fsp->file_id, mtime);
1012 /******************************************************************
1013 Set a create time EA.
1014 ******************************************************************/
1016 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1017 const struct smb_filename *psmb_fname,
1018 struct timespec create_time)
1020 NTSTATUS status;
1021 struct smb_filename *smb_fname = NULL;
1022 uint32_t dosmode;
1023 int ret;
1025 if (!lp_store_dos_attributes(SNUM(conn))) {
1026 return NT_STATUS_OK;
1029 status = create_synthetic_smb_fname(talloc_tos(),
1030 psmb_fname->base_name,
1031 NULL, &psmb_fname->st,
1032 &smb_fname);
1034 if (!NT_STATUS_IS_OK(status)) {
1035 return status;
1038 dosmode = dos_mode(conn, smb_fname);
1040 smb_fname->st.st_ex_btime = create_time;
1042 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1043 if (ret == -1) {
1044 map_nt_error_from_unix(errno);
1047 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1048 smb_fname_str_dbg(smb_fname)));
1050 return NT_STATUS_OK;
1053 /******************************************************************
1054 Return a create time.
1055 ******************************************************************/
1057 struct timespec get_create_timespec(connection_struct *conn,
1058 struct files_struct *fsp,
1059 const struct smb_filename *smb_fname)
1061 return smb_fname->st.st_ex_btime;
1064 /******************************************************************
1065 Return a change time (may look at EA in future).
1066 ******************************************************************/
1068 struct timespec get_change_timespec(connection_struct *conn,
1069 struct files_struct *fsp,
1070 const struct smb_filename *smb_fname)
1072 return smb_fname->st.st_ex_mtime;