file_set_sparse needs to be a handle based call.
[Samba/vl.git] / source3 / smbd / dosmode.c
blob86f78075062d4bf9926ca0e5d12b33233a7e2ac7
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 uint32_t filter_mode_by_protocol(uint32_t mode)
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;
33 return mode;
36 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
38 #ifdef S_ISLNK
39 #if LINKS_READ_ONLY
40 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
41 return aRONLY;
42 #endif
43 #endif
44 return 0;
47 /****************************************************************************
48 Change a dos mode to a unix mode.
49 Base permission for files:
50 if creating file and inheriting (i.e. parent_dir != NULL)
51 apply read/write bits from parent directory.
52 else
53 everybody gets read bit set
54 dos readonly is represented in unix by removing everyone's write bit
55 dos archive is represented in unix by the user's execute bit
56 dos system is represented in unix by the group's execute bit
57 dos hidden is represented in unix by the other's execute bit
58 if !inheriting {
59 Then apply create mask,
60 then add force bits.
62 Base permission for directories:
63 dos directory is represented in unix by unix's dir bit and the exec bit
64 if !inheriting {
65 Then apply create mask,
66 then add force bits.
68 ****************************************************************************/
70 mode_t unix_mode(connection_struct *conn, int dosmode,
71 const struct smb_filename *smb_fname,
72 const char *inherit_from_dir)
74 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
75 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
76 * inheriting. */
78 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
79 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
82 if ((inherit_from_dir != NULL) && lp_inherit_perms(SNUM(conn))) {
83 struct smb_filename *smb_fname_parent = NULL;
84 NTSTATUS status;
86 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
87 smb_fname_str_dbg(smb_fname),
88 inherit_from_dir));
90 status = create_synthetic_smb_fname(talloc_tos(),
91 inherit_from_dir, NULL,
92 NULL, &smb_fname_parent);
93 if (!NT_STATUS_IS_OK(status)) {
94 DEBUG(1,("unix_mode(%s) failed, [dir %s]: %s\n",
95 smb_fname_str_dbg(smb_fname),
96 inherit_from_dir, nt_errstr(status)));
97 return(0);
100 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
101 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
102 smb_fname_str_dbg(smb_fname),
103 inherit_from_dir, strerror(errno)));
104 TALLOC_FREE(smb_fname_parent);
105 return(0); /* *** shouldn't happen! *** */
108 /* Save for later - but explicitly remove setuid bit for safety. */
109 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
110 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
111 smb_fname_str_dbg(smb_fname), (int)dir_mode));
112 /* Clear "result" */
113 result = 0;
114 TALLOC_FREE(smb_fname_parent);
117 if (IS_DOS_DIR(dosmode)) {
118 /* We never make directories read only for the owner as under DOS a user
119 can always create a file in a read-only directory. */
120 result |= (S_IFDIR | S_IWUSR);
122 if (dir_mode) {
123 /* Inherit mode of parent directory. */
124 result |= dir_mode;
125 } else {
126 /* Provisionally add all 'x' bits */
127 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
129 /* Apply directory mask */
130 result &= lp_dir_mask(SNUM(conn));
131 /* Add in force bits */
132 result |= lp_force_dir_mode(SNUM(conn));
134 } else {
135 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
136 result |= S_IXUSR;
138 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
139 result |= S_IXGRP;
141 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
142 result |= S_IXOTH;
144 if (dir_mode) {
145 /* Inherit 666 component of parent directory mode */
146 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
147 } else {
148 /* Apply mode mask */
149 result &= lp_create_mask(SNUM(conn));
150 /* Add in force bits */
151 result |= lp_force_create_mode(SNUM(conn));
155 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
156 (int)result));
157 return(result);
160 /****************************************************************************
161 Change a unix mode to a dos mode.
162 ****************************************************************************/
164 static uint32 dos_mode_from_sbuf(connection_struct *conn,
165 const struct smb_filename *smb_fname)
167 int result = 0;
168 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
170 if (ro_opts == MAP_READONLY_YES) {
171 /* Original Samba method - map inverse of user "w" bit. */
172 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
173 result |= aRONLY;
175 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
176 /* Check actual permissions for read-only. */
177 if (!can_write_to_file(conn, smb_fname)) {
178 result |= aRONLY;
180 } /* Else never set the readonly bit. */
182 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
183 result |= aARCH;
185 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
186 result |= aSYSTEM;
188 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
189 result |= aHIDDEN;
191 if (S_ISDIR(smb_fname->st.st_ex_mode))
192 result = aDIR | (result & aRONLY);
194 result |= set_link_read_only_flag(&smb_fname->st);
196 DEBUG(8,("dos_mode_from_sbuf returning "));
198 if (result & aHIDDEN) DEBUG(8, ("h"));
199 if (result & aRONLY ) DEBUG(8, ("r"));
200 if (result & aSYSTEM) DEBUG(8, ("s"));
201 if (result & aDIR ) DEBUG(8, ("d"));
202 if (result & aARCH ) DEBUG(8, ("a"));
204 DEBUG(8,("\n"));
205 return result;
208 /****************************************************************************
209 Get DOS attributes from an EA.
210 This can also pull the create time into the stat struct inside smb_fname.
211 ****************************************************************************/
213 static bool get_ea_dos_attribute(connection_struct *conn,
214 struct smb_filename *smb_fname,
215 uint32 *pattr)
217 struct xattr_DOSATTRIB dosattrib;
218 enum ndr_err_code ndr_err;
219 DATA_BLOB blob;
220 ssize_t sizeret;
221 fstring attrstr;
222 uint32_t dosattr;
224 if (!lp_store_dos_attributes(SNUM(conn))) {
225 return False;
228 /* Don't reset pattr to zero as we may already have filename-based attributes we
229 need to preserve. */
231 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
232 SAMBA_XATTR_DOS_ATTRIB, attrstr,
233 sizeof(attrstr));
234 if (sizeret == -1) {
235 if (errno == ENOSYS
236 #if defined(ENOTSUP)
237 || errno == ENOTSUP) {
238 #else
240 #endif
241 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
242 "from EA on file %s: Error = %s\n",
243 smb_fname_str_dbg(smb_fname),
244 strerror(errno)));
245 set_store_dos_attributes(SNUM(conn), False);
247 return False;
250 blob.data = (uint8_t *)attrstr;
251 blob.length = sizeret;
253 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
254 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
256 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
257 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
258 "from EA on file %s: Error = %s\n",
259 smb_fname_str_dbg(smb_fname),
260 ndr_errstr(ndr_err)));
261 return false;
264 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
265 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
267 switch (dosattrib.version) {
268 case 0xFFFF:
269 dosattr = dosattrib.info.compatinfoFFFF.attrib;
270 break;
271 case 1:
272 dosattr = dosattrib.info.info1.attrib;
273 if (!null_nttime(dosattrib.info.info1.create_time)) {
274 struct timespec create_time =
275 nt_time_to_unix_timespec(
276 &dosattrib.info.info1.create_time);
278 update_stat_ex_create_time(&smb_fname->st,
279 create_time);
281 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
282 "set btime %s\n",
283 smb_fname_str_dbg(smb_fname),
284 time_to_asc(convert_timespec_to_time_t(
285 create_time)) ));
287 break;
288 case 2:
289 dosattr = dosattrib.info.oldinfo2.attrib;
290 /* Don't know what flags to check for this case. */
291 break;
292 case 3:
293 dosattr = dosattrib.info.info3.attrib;
294 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
295 !null_nttime(dosattrib.info.info3.create_time)) {
296 struct timespec create_time =
297 nt_time_to_unix_timespec(
298 &dosattrib.info.info3.create_time);
300 update_stat_ex_create_time(&smb_fname->st,
301 create_time);
303 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
304 "set btime %s\n",
305 smb_fname_str_dbg(smb_fname),
306 time_to_asc(convert_timespec_to_time_t(
307 create_time)) ));
309 break;
310 default:
311 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
312 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
313 attrstr));
314 return false;
317 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
318 dosattr |= aDIR;
320 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
322 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
324 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
325 if (dosattr & aRONLY ) DEBUG(8, ("r"));
326 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
327 if (dosattr & aDIR ) DEBUG(8, ("d"));
328 if (dosattr & aARCH ) DEBUG(8, ("a"));
330 DEBUG(8,("\n"));
332 return True;
335 /****************************************************************************
336 Set DOS attributes in an EA.
337 Also sets the create time.
338 ****************************************************************************/
340 static bool set_ea_dos_attribute(connection_struct *conn,
341 struct smb_filename *smb_fname,
342 uint32 dosmode)
344 struct xattr_DOSATTRIB dosattrib;
345 enum ndr_err_code ndr_err;
346 DATA_BLOB blob;
347 files_struct *fsp = NULL;
348 bool ret = false;
350 if (!lp_store_dos_attributes(SNUM(conn))) {
351 return False;
354 ZERO_STRUCT(dosattrib);
355 ZERO_STRUCT(blob);
357 dosattrib.version = 3;
358 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
359 XATTR_DOSINFO_CREATE_TIME;
360 dosattrib.info.info3.attrib = dosmode;
361 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
362 smb_fname->st.st_ex_btime);
364 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
365 (unsigned int)dosmode,
366 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
367 smb_fname_str_dbg(smb_fname) ));
369 ndr_err = ndr_push_struct_blob(
370 &blob, talloc_tos(), &dosattrib,
371 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
373 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
374 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
375 ndr_errstr(ndr_err)));
376 return false;
379 if (blob.data == NULL || blob.length == 0) {
380 return false;
383 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
384 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
385 0) == -1) {
386 if((errno != EPERM) && (errno != EACCES)) {
387 if (errno == ENOSYS
388 #if defined(ENOTSUP)
389 || errno == ENOTSUP) {
390 #else
392 #endif
393 DEBUG(1,("set_ea_dos_attributes: Cannot set "
394 "attribute EA on file %s: Error = %s\n",
395 smb_fname_str_dbg(smb_fname),
396 strerror(errno) ));
397 set_store_dos_attributes(SNUM(conn), False);
399 return false;
402 /* We want DOS semantics, ie allow non owner with write permission to change the
403 bits on a file. Just like file_ntimes below.
406 /* Check if we have write access. */
407 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
408 return false;
411 * We need to open the file with write access whilst
412 * still in our current user context. This ensures we
413 * are not violating security in doing the setxattr.
416 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
417 &fsp)))
418 return ret;
419 become_root();
420 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
421 SAMBA_XATTR_DOS_ATTRIB, blob.data,
422 blob.length, 0) == 0) {
423 ret = true;
425 unbecome_root();
426 close_file_fchmod(NULL, fsp);
427 return ret;
429 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
430 (unsigned int)dosmode,
431 smb_fname_str_dbg(smb_fname)));
432 return true;
435 /****************************************************************************
436 Change a unix mode to a dos mode for an ms dfs link.
437 ****************************************************************************/
439 uint32 dos_mode_msdfs(connection_struct *conn,
440 const struct smb_filename *smb_fname)
442 uint32 result = 0;
444 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
446 if (!VALID_STAT(smb_fname->st)) {
447 return 0;
450 /* First do any modifications that depend on the path name. */
451 /* hide files with a name starting with a . */
452 if (lp_hide_dot_files(SNUM(conn))) {
453 const char *p = strrchr_m(smb_fname->base_name, '/');
454 if (p) {
455 p++;
456 } else {
457 p = smb_fname->base_name;
460 /* Only . and .. are not hidden. */
461 if (p[0] == '.' && !((p[1] == '\0') ||
462 (p[1] == '.' && p[2] == '\0'))) {
463 result |= aHIDDEN;
467 result |= dos_mode_from_sbuf(conn, smb_fname);
469 /* Optimization : Only call is_hidden_path if it's not already
470 hidden. */
471 if (!(result & aHIDDEN) &&
472 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
473 result |= aHIDDEN;
476 if (result == 0) {
477 result = FILE_ATTRIBUTE_NORMAL;
480 result = filter_mode_by_protocol(result);
482 DEBUG(8,("dos_mode_msdfs returning "));
484 if (result & aHIDDEN) DEBUG(8, ("h"));
485 if (result & aRONLY ) DEBUG(8, ("r"));
486 if (result & aSYSTEM) DEBUG(8, ("s"));
487 if (result & aDIR ) DEBUG(8, ("d"));
488 if (result & aARCH ) DEBUG(8, ("a"));
489 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
491 DEBUG(8,("\n"));
493 return(result);
496 #ifdef HAVE_STAT_DOS_FLAGS
497 /****************************************************************************
498 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
499 ****************************************************************************/
501 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
503 uint32_t dos_stat_flags = 0;
505 if (dosmode & aARCH)
506 dos_stat_flags |= UF_DOS_ARCHIVE;
507 if (dosmode & aHIDDEN)
508 dos_stat_flags |= UF_DOS_HIDDEN;
509 if (dosmode & aRONLY)
510 dos_stat_flags |= UF_DOS_RO;
511 if (dosmode & aSYSTEM)
512 dos_stat_flags |= UF_DOS_SYSTEM;
513 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
514 dos_stat_flags |= UF_DOS_NOINDEX;
516 return dos_stat_flags;
519 /****************************************************************************
520 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
521 ****************************************************************************/
523 static bool get_stat_dos_flags(connection_struct *conn,
524 const struct smb_filename *smb_fname,
525 uint32_t *dosmode)
527 SMB_ASSERT(VALID_STAT(smb_fname->st));
528 SMB_ASSERT(dosmode);
530 if (!lp_store_dos_attributes(SNUM(conn))) {
531 return false;
534 DEBUG(5, ("Getting stat dos attributes for %s.\n",
535 smb_fname_str_dbg(smb_fname)));
537 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
538 *dosmode |= aARCH;
539 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
540 *dosmode |= aHIDDEN;
541 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
542 *dosmode |= aRONLY;
543 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
544 *dosmode |= aSYSTEM;
545 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
546 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
547 if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
548 *dosmode |= FILE_ATTRIBUTE_SPARSE;
549 if (S_ISDIR(smb_fname->st.st_ex_mode))
550 *dosmode |= aDIR;
552 *dosmode |= set_link_read_only_flag(&smb_fname->st);
554 return true;
557 /****************************************************************************
558 Sets DOS attributes, stored in st_ex_flags of the inode.
559 ****************************************************************************/
561 static bool set_stat_dos_flags(connection_struct *conn,
562 const struct smb_filename *smb_fname,
563 uint32_t dosmode,
564 bool *attributes_changed)
566 uint32_t new_flags = 0;
567 int error = 0;
569 SMB_ASSERT(VALID_STAT(smb_fname->st));
570 SMB_ASSERT(attributes_changed);
572 *attributes_changed = false;
574 if (!lp_store_dos_attributes(SNUM(conn))) {
575 return false;
578 DEBUG(5, ("Setting stat dos attributes for %s.\n",
579 smb_fname_str_dbg(smb_fname)));
581 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
582 dos_attributes_to_stat_dos_flags(dosmode);
584 /* Return early if no flags changed. */
585 if (new_flags == smb_fname->st.st_ex_flags)
586 return true;
588 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
589 smb_fname->st.st_ex_flags));
591 /* Set new flags with chflags. */
592 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
593 if (error) {
594 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
595 "file %s! errno=%d\n", new_flags,
596 smb_fname_str_dbg(smb_fname), errno));
597 return false;
600 *attributes_changed = true;
601 return true;
603 #endif /* HAVE_STAT_DOS_FLAGS */
605 /****************************************************************************
606 Change a unix mode to a dos mode.
607 May also read the create timespec into the stat struct in smb_fname
608 if "store dos attributes" is true.
609 ****************************************************************************/
611 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
613 uint32 result = 0;
614 bool offline, used_stat_dos_flags = false;
616 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
618 if (!VALID_STAT(smb_fname->st)) {
619 return 0;
622 /* First do any modifications that depend on the path name. */
623 /* hide files with a name starting with a . */
624 if (lp_hide_dot_files(SNUM(conn))) {
625 const char *p = strrchr_m(smb_fname->base_name,'/');
626 if (p) {
627 p++;
628 } else {
629 p = smb_fname->base_name;
632 /* Only . and .. are not hidden. */
633 if (p[0] == '.' && !((p[1] == '\0') ||
634 (p[1] == '.' && p[2] == '\0'))) {
635 result |= aHIDDEN;
639 #ifdef HAVE_STAT_DOS_FLAGS
640 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
641 #endif
642 if (!used_stat_dos_flags) {
643 /* Get the DOS attributes from an EA by preference. */
644 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
645 result |= dos_mode_from_sbuf(conn, smb_fname);
649 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname->base_name, &smb_fname->st);
650 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
651 result |= FILE_ATTRIBUTE_OFFLINE;
654 /* Optimization : Only call is_hidden_path if it's not already
655 hidden. */
656 if (!(result & aHIDDEN) &&
657 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
658 result |= aHIDDEN;
661 if (result == 0) {
662 result = FILE_ATTRIBUTE_NORMAL;
665 result = filter_mode_by_protocol(result);
667 DEBUG(8,("dos_mode returning "));
669 if (result & aHIDDEN) DEBUG(8, ("h"));
670 if (result & aRONLY ) DEBUG(8, ("r"));
671 if (result & aSYSTEM) DEBUG(8, ("s"));
672 if (result & aDIR ) DEBUG(8, ("d"));
673 if (result & aARCH ) DEBUG(8, ("a"));
674 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
676 DEBUG(8,("\n"));
678 return(result);
681 /*******************************************************************
682 chmod a file - but preserve some bits.
683 If "store dos attributes" is also set it will store the create time
684 from the stat struct in smb_fname (in NTTIME format) in the EA
685 attribute also.
686 ********************************************************************/
688 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
689 uint32 dosmode, const char *parent_dir, bool newfile)
691 int mask=0;
692 mode_t tmp;
693 mode_t unixmode;
694 int ret = -1, lret = -1;
695 uint32_t old_mode;
696 struct timespec new_create_timespec;
698 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
699 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
701 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
702 dosmode, smb_fname_str_dbg(smb_fname)));
704 unixmode = smb_fname->st.st_ex_mode;
706 get_acl_group_bits(conn, smb_fname->base_name,
707 &smb_fname->st.st_ex_mode);
709 if (S_ISDIR(smb_fname->st.st_ex_mode))
710 dosmode |= aDIR;
711 else
712 dosmode &= ~aDIR;
714 new_create_timespec = smb_fname->st.st_ex_btime;
716 old_mode = dos_mode(conn, smb_fname);
718 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
719 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
720 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname->base_name);
721 if (lret == -1) {
722 DEBUG(0, ("set_dos_mode: client has asked to "
723 "set FILE_ATTRIBUTE_OFFLINE to "
724 "%s/%s but there was an error while "
725 "setting it or it is not "
726 "supported.\n", parent_dir,
727 smb_fname_str_dbg(smb_fname)));
732 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
733 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
735 smb_fname->st.st_ex_btime = new_create_timespec;
737 #ifdef HAVE_STAT_DOS_FLAGS
739 bool attributes_changed;
741 if (set_stat_dos_flags(conn, smb_fname, dosmode,
742 &attributes_changed))
744 if (!newfile && attributes_changed) {
745 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
746 FILE_NOTIFY_CHANGE_ATTRIBUTES,
747 smb_fname->base_name);
749 smb_fname->st.st_ex_mode = unixmode;
750 return 0;
753 #endif
754 /* Store the DOS attributes in an EA by preference. */
755 if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
756 if (!newfile) {
757 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
758 FILE_NOTIFY_CHANGE_ATTRIBUTES,
759 smb_fname->base_name);
761 smb_fname->st.st_ex_mode = unixmode;
762 return 0;
765 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
767 /* preserve the s bits */
768 mask |= (S_ISUID | S_ISGID);
770 /* preserve the t bit */
771 #ifdef S_ISVTX
772 mask |= S_ISVTX;
773 #endif
775 /* possibly preserve the x bits */
776 if (!MAP_ARCHIVE(conn))
777 mask |= S_IXUSR;
778 if (!MAP_SYSTEM(conn))
779 mask |= S_IXGRP;
780 if (!MAP_HIDDEN(conn))
781 mask |= S_IXOTH;
783 unixmode |= (smb_fname->st.st_ex_mode & mask);
785 /* if we previously had any r bits set then leave them alone */
786 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
787 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
788 unixmode |= tmp;
791 /* if we previously had any w bits set then leave them alone
792 whilst adding in the new w bits, if the new mode is not rdonly */
793 if (!IS_DOS_READONLY(dosmode)) {
794 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
797 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
798 if (ret == 0) {
799 if(!newfile || (lret != -1)) {
800 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
801 FILE_NOTIFY_CHANGE_ATTRIBUTES,
802 smb_fname->base_name);
804 smb_fname->st.st_ex_mode = unixmode;
805 return 0;
808 if((errno != EPERM) && (errno != EACCES))
809 return -1;
811 if(!lp_dos_filemode(SNUM(conn)))
812 return -1;
814 /* We want DOS semantics, ie allow non owner with write permission to change the
815 bits on a file. Just like file_ntimes below.
818 /* Check if we have write access. */
819 if (CAN_WRITE(conn)) {
821 * We need to open the file with write access whilst
822 * still in our current user context. This ensures we
823 * are not violating security in doing the fchmod.
824 * This file open does *not* break any oplocks we are
825 * holding. We need to review this.... may need to
826 * break batch oplocks open by others. JRA.
828 files_struct *fsp;
829 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
830 &fsp)))
831 return -1;
832 become_root();
833 ret = SMB_VFS_FCHMOD(fsp, unixmode);
834 unbecome_root();
835 close_file_fchmod(NULL, fsp);
836 if (!newfile) {
837 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
838 FILE_NOTIFY_CHANGE_ATTRIBUTES,
839 smb_fname->base_name);
841 if (ret == 0) {
842 smb_fname->st.st_ex_mode = unixmode;
846 return( ret );
850 NTSTATUS file_set_sparse(connection_struct *conn,
851 files_struct *fsp,
852 bool sparse)
854 uint32_t old_dosmode;
855 uint32_t new_dosmode;
856 NTSTATUS status;
858 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
859 sparse, smb_fname_str_dbg(fsp->fsp_name)));
861 if (!lp_store_dos_attributes(SNUM(conn))) {
862 return NT_STATUS_INVALID_DEVICE_REQUEST;
865 status = vfs_stat_fsp(fsp);
866 if (!NT_STATUS_IS_OK(status)) {
867 return status;
870 old_dosmode = dos_mode(conn, fsp->fsp_name);
872 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
873 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
874 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
875 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
876 } else {
877 return NT_STATUS_OK;
880 /* Store the DOS attributes in an EA. */
881 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
882 new_dosmode)) {
883 if (errno == 0) {
884 errno = EIO;
886 return map_nt_error_from_unix(errno);
889 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
890 FILE_NOTIFY_CHANGE_ATTRIBUTES,
891 smb_fname->base_name);
893 return NT_STATUS_OK;
896 /*******************************************************************
897 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
898 than POSIX.
899 *******************************************************************/
901 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
902 struct smb_file_time *ft)
904 int ret = -1;
906 errno = 0;
908 DEBUG(6, ("file_ntime: actime: %s",
909 time_to_asc(convert_timespec_to_time_t(ft->atime))));
910 DEBUG(6, ("file_ntime: modtime: %s",
911 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
912 DEBUG(6, ("file_ntime: ctime: %s",
913 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
914 DEBUG(6, ("file_ntime: createtime: %s",
915 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
917 /* Don't update the time on read-only shares */
918 /* We need this as set_filetime (which can be called on
919 close and other paths) can end up calling this function
920 without the NEED_WRITE protection. Found by :
921 Leo Weppelman <leo@wau.mis.ah.nl>
924 if (!CAN_WRITE(conn)) {
925 return 0;
928 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
929 return 0;
932 if((errno != EPERM) && (errno != EACCES)) {
933 return -1;
936 if(!lp_dos_filetimes(SNUM(conn))) {
937 return -1;
940 /* We have permission (given by the Samba admin) to
941 break POSIX semantics and allow a user to change
942 the time on a file they don't own but can write to
943 (as DOS does).
946 /* Check if we have write access. */
947 if (can_write_to_file(conn, smb_fname)) {
948 /* We are allowed to become root and change the filetime. */
949 become_root();
950 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
951 unbecome_root();
954 return ret;
957 /******************************************************************
958 Force a "sticky" write time on a pathname. This will always be
959 returned on all future write time queries and set on close.
960 ******************************************************************/
962 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
964 if (null_timespec(mtime)) {
965 return true;
968 if (!set_sticky_write_time(fileid, mtime)) {
969 return false;
972 return true;
975 /******************************************************************
976 Force a "sticky" write time on an fsp. This will always be
977 returned on all future write time queries and set on close.
978 ******************************************************************/
980 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
982 if (null_timespec(mtime)) {
983 return true;
986 fsp->write_time_forced = true;
987 TALLOC_FREE(fsp->update_write_time_event);
989 return set_sticky_write_time_path(fsp->file_id, mtime);
992 /******************************************************************
993 Set a create time EA.
994 ******************************************************************/
996 NTSTATUS set_create_timespec_ea(connection_struct *conn,
997 const struct smb_filename *psmb_fname,
998 struct timespec create_time)
1000 NTSTATUS status;
1001 struct smb_filename *smb_fname = NULL;
1002 uint32_t dosmode;
1003 int ret;
1005 if (!lp_store_dos_attributes(SNUM(conn))) {
1006 return NT_STATUS_OK;
1009 status = create_synthetic_smb_fname(talloc_tos(),
1010 psmb_fname->base_name,
1011 NULL, &psmb_fname->st,
1012 &smb_fname);
1014 if (!NT_STATUS_IS_OK(status)) {
1015 return status;
1018 dosmode = dos_mode(conn, smb_fname);
1020 smb_fname->st.st_ex_btime = create_time;
1022 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1023 if (ret == -1) {
1024 map_nt_error_from_unix(errno);
1027 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1028 smb_fname_str_dbg(smb_fname)));
1030 return NT_STATUS_OK;
1033 /******************************************************************
1034 Return a create time.
1035 ******************************************************************/
1037 struct timespec get_create_timespec(connection_struct *conn,
1038 struct files_struct *fsp,
1039 const struct smb_filename *smb_fname)
1041 return smb_fname->st.st_ex_btime;
1044 /******************************************************************
1045 Return a change time (may look at EA in future).
1046 ******************************************************************/
1048 struct timespec get_change_timespec(connection_struct *conn,
1049 struct files_struct *fsp,
1050 const struct smb_filename *smb_fname)
1052 return smb_fname->st.st_ex_mtime;