lib: replace: Add strsep function (missing on Solaris).
[Samba.git] / source3 / smbd / dosmode.c
blob72acd4edccc462a960f3eef26b7ae9d874dd3b4a
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 "system/filesys.h"
23 #include "librpc/gen_ndr/ndr_xattr.h"
24 #include "librpc/gen_ndr/ioctl.h"
25 #include "../libcli/security/security.h"
26 #include "smbd/smbd.h"
27 #include "lib/param/loadparm.h"
29 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
30 struct smb_filename *smb_fname,
31 files_struct **ret_fsp,
32 bool *need_close);
34 static void dos_mode_debug_print(uint32_t mode)
36 DEBUG(8,("dos_mode returning "));
38 if (mode & FILE_ATTRIBUTE_HIDDEN) {
39 DEBUG(8, ("h"));
41 if (mode & FILE_ATTRIBUTE_READONLY) {
42 DEBUG(8, ("r"));
44 if (mode & FILE_ATTRIBUTE_SYSTEM) {
45 DEBUG(8, ("s"));
47 if (mode & FILE_ATTRIBUTE_DIRECTORY) {
48 DEBUG(8, ("d"));
50 if (mode & FILE_ATTRIBUTE_ARCHIVE) {
51 DEBUG(8, ("a"));
53 if (mode & FILE_ATTRIBUTE_SPARSE) {
54 DEBUG(8, ("[sparse]"));
56 if (mode & FILE_ATTRIBUTE_OFFLINE) {
57 DEBUG(8, ("[offline]"));
59 if (mode & FILE_ATTRIBUTE_COMPRESSED) {
60 DEBUG(8, ("[compressed]"));
63 DEBUG(8,("\n"));
66 static uint32_t filter_mode_by_protocol(uint32_t mode)
68 if (get_Protocol() <= PROTOCOL_LANMAN2) {
69 DEBUG(10,("filter_mode_by_protocol: "
70 "filtering result 0x%x to 0x%x\n",
71 (unsigned int)mode,
72 (unsigned int)(mode & 0x3f) ));
73 mode &= 0x3f;
75 return mode;
78 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
80 #ifdef S_ISLNK
81 #if LINKS_READ_ONLY
82 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
83 return FILE_ATTRIBUTE_READONLY;
84 #endif
85 #endif
86 return 0;
89 /****************************************************************************
90 Change a dos mode to a unix mode.
91 Base permission for files:
92 if creating file and inheriting (i.e. parent_dir != NULL)
93 apply read/write bits from parent directory.
94 else
95 everybody gets read bit set
96 dos readonly is represented in unix by removing everyone's write bit
97 dos archive is represented in unix by the user's execute bit
98 dos system is represented in unix by the group's execute bit
99 dos hidden is represented in unix by the other's execute bit
100 if !inheriting {
101 Then apply create mask,
102 then add force bits.
104 Base permission for directories:
105 dos directory is represented in unix by unix's dir bit and the exec bit
106 if !inheriting {
107 Then apply create mask,
108 then add force bits.
110 ****************************************************************************/
112 mode_t unix_mode(connection_struct *conn, int dosmode,
113 const struct smb_filename *smb_fname,
114 const char *inherit_from_dir)
116 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
117 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
118 * inheriting. */
120 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
121 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
124 if ((inherit_from_dir != NULL) && lp_inherit_permissions(SNUM(conn))) {
125 struct smb_filename *smb_fname_parent;
127 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
128 smb_fname_str_dbg(smb_fname),
129 inherit_from_dir));
131 smb_fname_parent = synthetic_smb_fname(
132 talloc_tos(), inherit_from_dir, NULL, NULL);
133 if (smb_fname_parent == NULL) {
134 DEBUG(1,("unix_mode(%s) failed, [dir %s]: No memory\n",
135 smb_fname_str_dbg(smb_fname),
136 inherit_from_dir));
137 return(0);
140 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
141 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
142 smb_fname_str_dbg(smb_fname),
143 inherit_from_dir, strerror(errno)));
144 TALLOC_FREE(smb_fname_parent);
145 return(0); /* *** shouldn't happen! *** */
148 /* Save for later - but explicitly remove setuid bit for safety. */
149 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
150 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
151 smb_fname_str_dbg(smb_fname), (int)dir_mode));
152 /* Clear "result" */
153 result = 0;
154 TALLOC_FREE(smb_fname_parent);
157 if (IS_DOS_DIR(dosmode)) {
158 /* We never make directories read only for the owner as under DOS a user
159 can always create a file in a read-only directory. */
160 result |= (S_IFDIR | S_IWUSR);
162 if (dir_mode) {
163 /* Inherit mode of parent directory. */
164 result |= dir_mode;
165 } else {
166 /* Provisionally add all 'x' bits */
167 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
169 /* Apply directory mask */
170 result &= lp_directory_mask(SNUM(conn));
171 /* Add in force bits */
172 result |= lp_force_directory_mode(SNUM(conn));
174 } else {
175 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
176 result |= S_IXUSR;
178 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
179 result |= S_IXGRP;
181 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
182 result |= S_IXOTH;
184 if (dir_mode) {
185 /* Inherit 666 component of parent directory mode */
186 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
187 } else {
188 /* Apply mode mask */
189 result &= lp_create_mask(SNUM(conn));
190 /* Add in force bits */
191 result |= lp_force_create_mode(SNUM(conn));
195 DBG_INFO("unix_mode(%s) returning 0%o\n",
196 smb_fname_str_dbg(smb_fname), (int)result);
198 return(result);
201 /****************************************************************************
202 Change a unix mode to a dos mode.
203 ****************************************************************************/
205 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
206 const struct smb_filename *smb_fname)
208 int result = 0;
209 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
211 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
212 /* if we can find out if a file is immutable we should report it r/o */
213 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
214 result |= FILE_ATTRIBUTE_READONLY;
216 #endif
217 if (ro_opts == MAP_READONLY_YES) {
218 /* Original Samba method - map inverse of user "w" bit. */
219 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
220 result |= FILE_ATTRIBUTE_READONLY;
222 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
223 /* Check actual permissions for read-only. */
224 if (!can_write_to_file(conn, smb_fname)) {
225 result |= FILE_ATTRIBUTE_READONLY;
227 } /* Else never set the readonly bit. */
229 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
230 result |= FILE_ATTRIBUTE_ARCHIVE;
232 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
233 result |= FILE_ATTRIBUTE_SYSTEM;
235 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
236 result |= FILE_ATTRIBUTE_HIDDEN;
238 if (S_ISDIR(smb_fname->st.st_ex_mode))
239 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
241 result |= set_link_read_only_flag(&smb_fname->st);
243 DEBUG(8,("dos_mode_from_sbuf returning "));
245 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
246 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
247 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
248 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
249 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
251 DEBUG(8,("\n"));
252 return result;
255 /****************************************************************************
256 Get DOS attributes from an EA.
257 This can also pull the create time into the stat struct inside smb_fname.
258 ****************************************************************************/
260 static bool get_ea_dos_attribute(connection_struct *conn,
261 struct smb_filename *smb_fname,
262 uint32_t *pattr)
264 struct xattr_DOSATTRIB dosattrib;
265 enum ndr_err_code ndr_err;
266 DATA_BLOB blob;
267 ssize_t sizeret;
268 fstring attrstr;
269 uint32_t dosattr;
271 if (!lp_store_dos_attributes(SNUM(conn))) {
272 return False;
275 /* Don't reset pattr to zero as we may already have filename-based attributes we
276 need to preserve. */
278 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
279 SAMBA_XATTR_DOS_ATTRIB, attrstr,
280 sizeof(attrstr));
281 if (sizeret == -1) {
282 if (errno == ENOSYS
283 #if defined(ENOTSUP)
284 || errno == ENOTSUP) {
285 #else
287 #endif
288 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
289 "from EA on file %s: Error = %s\n",
290 smb_fname_str_dbg(smb_fname),
291 strerror(errno)));
292 set_store_dos_attributes(SNUM(conn), False);
294 return False;
297 blob.data = (uint8_t *)attrstr;
298 blob.length = sizeret;
300 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
301 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
303 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
304 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
305 "from EA on file %s: Error = %s\n",
306 smb_fname_str_dbg(smb_fname),
307 ndr_errstr(ndr_err)));
308 return false;
311 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
312 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
314 switch (dosattrib.version) {
315 case 0xFFFF:
316 dosattr = dosattrib.info.compatinfoFFFF.attrib;
317 break;
318 case 1:
319 dosattr = dosattrib.info.info1.attrib;
320 if (!null_nttime(dosattrib.info.info1.create_time)) {
321 struct timespec create_time =
322 nt_time_to_unix_timespec(
323 dosattrib.info.info1.create_time);
325 update_stat_ex_create_time(&smb_fname->st,
326 create_time);
328 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
329 "set btime %s\n",
330 smb_fname_str_dbg(smb_fname),
331 time_to_asc(convert_timespec_to_time_t(
332 create_time)) ));
334 break;
335 case 2:
336 dosattr = dosattrib.info.oldinfo2.attrib;
337 /* Don't know what flags to check for this case. */
338 break;
339 case 3:
340 dosattr = dosattrib.info.info3.attrib;
341 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
342 !null_nttime(dosattrib.info.info3.create_time)) {
343 struct timespec create_time =
344 nt_time_to_unix_timespec(
345 dosattrib.info.info3.create_time);
347 update_stat_ex_create_time(&smb_fname->st,
348 create_time);
350 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
351 "set btime %s\n",
352 smb_fname_str_dbg(smb_fname),
353 time_to_asc(convert_timespec_to_time_t(
354 create_time)) ));
356 break;
357 default:
358 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
359 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
360 attrstr));
361 return false;
364 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
365 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
367 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
368 *pattr = (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
370 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
372 if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
373 if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
374 if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
375 if (dosattr & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
376 if (dosattr & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
378 DEBUG(8,("\n"));
380 return True;
383 /****************************************************************************
384 Set DOS attributes in an EA.
385 Also sets the create time.
386 ****************************************************************************/
388 static bool set_ea_dos_attribute(connection_struct *conn,
389 struct smb_filename *smb_fname,
390 uint32_t dosmode)
392 struct xattr_DOSATTRIB dosattrib;
393 enum ndr_err_code ndr_err;
394 DATA_BLOB blob;
396 ZERO_STRUCT(dosattrib);
397 ZERO_STRUCT(blob);
399 dosattrib.version = 3;
400 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
401 XATTR_DOSINFO_CREATE_TIME;
402 dosattrib.info.info3.attrib = dosmode;
403 dosattrib.info.info3.create_time = unix_timespec_to_nt_time(
404 smb_fname->st.st_ex_btime);
406 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
407 (unsigned int)dosmode,
408 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
409 smb_fname_str_dbg(smb_fname) ));
411 ndr_err = ndr_push_struct_blob(
412 &blob, talloc_tos(), &dosattrib,
413 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
415 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
416 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
417 ndr_errstr(ndr_err)));
418 return false;
421 if (blob.data == NULL || blob.length == 0) {
422 return false;
425 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
426 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
427 0) == -1) {
428 bool ret = false;
429 bool need_close = false;
430 files_struct *fsp = NULL;
432 if((errno != EPERM) && (errno != EACCES)) {
433 if (errno == ENOSYS
434 #if defined(ENOTSUP)
435 || errno == ENOTSUP) {
436 #else
438 #endif
439 DEBUG(1,("set_ea_dos_attributes: Cannot set "
440 "attribute EA on file %s: Error = %s\n",
441 smb_fname_str_dbg(smb_fname),
442 strerror(errno) ));
443 set_store_dos_attributes(SNUM(conn), False);
445 return false;
448 /* We want DOS semantics, ie allow non owner with write permission to change the
449 bits on a file. Just like file_ntimes below.
452 /* Check if we have write access. */
453 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
454 return false;
456 if (!can_write_to_file(conn, smb_fname)) {
457 return false;
461 * We need to get an open file handle to do the
462 * metadata operation under root.
465 if (!NT_STATUS_IS_OK(get_file_handle_for_metadata(conn,
466 smb_fname,
467 &fsp,
468 &need_close))) {
469 return false;
472 become_root();
473 if (SMB_VFS_FSETXATTR(fsp,
474 SAMBA_XATTR_DOS_ATTRIB, blob.data,
475 blob.length, 0) == 0) {
476 ret = true;
478 unbecome_root();
479 if (need_close) {
480 close_file(NULL, fsp, NORMAL_CLOSE);
482 return ret;
484 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
485 (unsigned int)dosmode,
486 smb_fname_str_dbg(smb_fname)));
487 return true;
490 /****************************************************************************
491 Change a unix mode to a dos mode for an ms dfs link.
492 ****************************************************************************/
494 uint32_t dos_mode_msdfs(connection_struct *conn,
495 const struct smb_filename *smb_fname)
497 uint32_t result = 0;
499 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
501 if (!VALID_STAT(smb_fname->st)) {
502 return 0;
505 /* First do any modifications that depend on the path name. */
506 /* hide files with a name starting with a . */
507 if (lp_hide_dot_files(SNUM(conn))) {
508 const char *p = strrchr_m(smb_fname->base_name, '/');
509 if (p) {
510 p++;
511 } else {
512 p = smb_fname->base_name;
515 /* Only . and .. are not hidden. */
516 if (p[0] == '.' && !((p[1] == '\0') ||
517 (p[1] == '.' && p[2] == '\0'))) {
518 result |= FILE_ATTRIBUTE_HIDDEN;
522 result |= dos_mode_from_sbuf(conn, smb_fname);
524 /* Optimization : Only call is_hidden_path if it's not already
525 hidden. */
526 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
527 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
528 result |= FILE_ATTRIBUTE_HIDDEN;
531 if (result == 0) {
532 result = FILE_ATTRIBUTE_NORMAL;
535 result = filter_mode_by_protocol(result);
538 * Add in that it is a reparse point
540 result |= FILE_ATTRIBUTE_REPARSE_POINT;
542 DEBUG(8,("dos_mode_msdfs returning "));
544 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
545 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
546 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
547 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
548 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
549 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
551 DEBUG(8,("\n"));
553 return(result);
557 * check whether a file or directory is flagged as compressed.
559 static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
560 struct smb_filename *smb_fname,
561 bool *is_compressed)
563 NTSTATUS status;
564 uint16_t compression_fmt;
565 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
566 if (tmp_ctx == NULL) {
567 status = NT_STATUS_NO_MEMORY;
568 goto err_out;
571 status = SMB_VFS_GET_COMPRESSION(conn, tmp_ctx, NULL, smb_fname,
572 &compression_fmt);
573 if (!NT_STATUS_IS_OK(status)) {
574 goto err_ctx_free;
577 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
578 *is_compressed = true;
579 } else {
580 *is_compressed = false;
582 status = NT_STATUS_OK;
584 err_ctx_free:
585 talloc_free(tmp_ctx);
586 err_out:
587 return status;
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_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
598 uint32_t result = 0;
599 bool offline;
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 |= FILE_ATTRIBUTE_HIDDEN;
624 /* Get the DOS attributes from an EA by preference. */
625 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
626 result |= dos_mode_from_sbuf(conn, smb_fname);
629 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
630 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
631 result |= FILE_ATTRIBUTE_OFFLINE;
634 if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
635 bool compressed = false;
636 NTSTATUS status = dos_mode_check_compressed(conn, smb_fname,
637 &compressed);
638 if (NT_STATUS_IS_OK(status) && compressed) {
639 result |= FILE_ATTRIBUTE_COMPRESSED;
643 /* Optimization : Only call is_hidden_path if it's not already
644 hidden. */
645 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
646 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
647 result |= FILE_ATTRIBUTE_HIDDEN;
650 if (result == 0) {
651 result = FILE_ATTRIBUTE_NORMAL;
654 result = filter_mode_by_protocol(result);
656 dos_mode_debug_print(result);
658 return result;
661 /*******************************************************************
662 chmod a file - but preserve some bits.
663 If "store dos attributes" is also set it will store the create time
664 from the stat struct in smb_fname (in NTTIME format) in the EA
665 attribute also.
666 ********************************************************************/
668 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
669 uint32_t dosmode, const char *parent_dir, bool newfile)
671 int mask=0;
672 mode_t tmp;
673 mode_t unixmode;
674 int ret = -1, lret = -1;
675 uint32_t old_mode;
676 struct timespec new_create_timespec;
677 files_struct *fsp = NULL;
678 bool need_close = false;
679 NTSTATUS status;
681 if (!CAN_WRITE(conn)) {
682 errno = EROFS;
683 return -1;
686 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
687 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
689 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
690 dosmode, smb_fname_str_dbg(smb_fname)));
692 unixmode = smb_fname->st.st_ex_mode;
694 get_acl_group_bits(conn, smb_fname->base_name,
695 &smb_fname->st.st_ex_mode);
697 if (S_ISDIR(smb_fname->st.st_ex_mode))
698 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
699 else
700 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
702 new_create_timespec = smb_fname->st.st_ex_btime;
704 old_mode = dos_mode(conn, smb_fname);
706 if ((dosmode & FILE_ATTRIBUTE_OFFLINE) &&
707 !(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
708 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
709 if (lret == -1) {
710 if (errno == ENOTSUP) {
711 DEBUG(10, ("Setting FILE_ATTRIBUTE_OFFLINE for "
712 "%s/%s is not supported.\n",
713 parent_dir,
714 smb_fname_str_dbg(smb_fname)));
715 } else {
716 DEBUG(0, ("An error occurred while setting "
717 "FILE_ATTRIBUTE_OFFLINE for "
718 "%s/%s: %s", parent_dir,
719 smb_fname_str_dbg(smb_fname),
720 strerror(errno)));
725 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
726 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
728 smb_fname->st.st_ex_btime = new_create_timespec;
730 /* Store the DOS attributes in an EA by preference. */
731 if (lp_store_dos_attributes(SNUM(conn))) {
733 * Don't fall back to using UNIX modes. Finally
734 * follow the smb.conf manpage.
736 if (!set_ea_dos_attribute(conn, smb_fname, dosmode)) {
737 return -1;
739 if (!newfile) {
740 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
741 FILE_NOTIFY_CHANGE_ATTRIBUTES,
742 smb_fname->base_name);
744 smb_fname->st.st_ex_mode = unixmode;
745 return 0;
748 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
750 /* preserve the file type bits */
751 mask |= S_IFMT;
753 /* preserve the s bits */
754 mask |= (S_ISUID | S_ISGID);
756 /* preserve the t bit */
757 #ifdef S_ISVTX
758 mask |= S_ISVTX;
759 #endif
761 /* possibly preserve the x bits */
762 if (!MAP_ARCHIVE(conn))
763 mask |= S_IXUSR;
764 if (!MAP_SYSTEM(conn))
765 mask |= S_IXGRP;
766 if (!MAP_HIDDEN(conn))
767 mask |= S_IXOTH;
769 unixmode |= (smb_fname->st.st_ex_mode & mask);
771 /* if we previously had any r bits set then leave them alone */
772 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
773 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
774 unixmode |= tmp;
777 /* if we previously had any w bits set then leave them alone
778 whilst adding in the new w bits, if the new mode is not rdonly */
779 if (!IS_DOS_READONLY(dosmode)) {
780 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
784 * From the chmod 2 man page:
786 * "If the calling process is not privileged, and the group of the file
787 * does not match the effective group ID of the process or one of its
788 * supplementary group IDs, the S_ISGID bit will be turned off, but
789 * this will not cause an error to be returned."
791 * Simply refuse to do the chmod in this case.
794 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
795 geteuid() != sec_initial_uid() &&
796 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
797 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
798 "set for directory %s\n",
799 smb_fname_str_dbg(smb_fname)));
800 errno = EPERM;
801 return -1;
804 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
805 if (ret == 0) {
806 if(!newfile || (lret != -1)) {
807 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
808 FILE_NOTIFY_CHANGE_ATTRIBUTES,
809 smb_fname->base_name);
811 smb_fname->st.st_ex_mode = unixmode;
812 return 0;
815 if((errno != EPERM) && (errno != EACCES))
816 return -1;
818 if(!lp_dos_filemode(SNUM(conn)))
819 return -1;
821 /* We want DOS semantics, ie allow non owner with write permission to change the
822 bits on a file. Just like file_ntimes below.
825 if (!can_write_to_file(conn, smb_fname)) {
826 errno = EACCES;
827 return -1;
831 * We need to get an open file handle to do the
832 * metadata operation under root.
835 status = get_file_handle_for_metadata(conn,
836 smb_fname,
837 &fsp,
838 &need_close);
839 if (!NT_STATUS_IS_OK(status)) {
840 errno = map_errno_from_nt_status(status);
841 return -1;
844 become_root();
845 ret = SMB_VFS_FCHMOD(fsp, unixmode);
846 unbecome_root();
847 if (need_close) {
848 close_file(NULL, fsp, NORMAL_CLOSE);
850 if (!newfile) {
851 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
852 FILE_NOTIFY_CHANGE_ATTRIBUTES,
853 smb_fname->base_name);
855 if (ret == 0) {
856 smb_fname->st.st_ex_mode = unixmode;
859 return( ret );
863 NTSTATUS file_set_sparse(connection_struct *conn,
864 files_struct *fsp,
865 bool sparse)
867 uint32_t old_dosmode;
868 uint32_t new_dosmode;
869 NTSTATUS status;
871 if (!CAN_WRITE(conn)) {
872 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
873 "on readonly share[%s]\n",
874 smb_fname_str_dbg(fsp->fsp_name),
875 sparse,
876 lp_servicename(talloc_tos(), SNUM(conn))));
877 return NT_STATUS_MEDIA_WRITE_PROTECTED;
881 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
882 * following access flags are granted.
884 if ((fsp->access_mask & (FILE_WRITE_DATA
885 | FILE_WRITE_ATTRIBUTES
886 | SEC_FILE_APPEND_DATA)) == 0) {
887 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
888 "access_mask[0x%08X] - access denied\n",
889 smb_fname_str_dbg(fsp->fsp_name),
890 sparse,
891 fsp->access_mask));
892 return NT_STATUS_ACCESS_DENIED;
895 if (fsp->is_directory) {
896 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
897 (sparse ? "set" : "clear"),
898 smb_fname_str_dbg(fsp->fsp_name)));
899 return NT_STATUS_INVALID_PARAMETER;
902 if (IS_IPC(conn) || IS_PRINT(conn)) {
903 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
904 (sparse ? "set" : "clear")));
905 return NT_STATUS_INVALID_PARAMETER;
908 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
909 sparse, smb_fname_str_dbg(fsp->fsp_name)));
911 if (!lp_store_dos_attributes(SNUM(conn))) {
912 return NT_STATUS_INVALID_DEVICE_REQUEST;
915 status = vfs_stat_fsp(fsp);
916 if (!NT_STATUS_IS_OK(status)) {
917 return status;
920 old_dosmode = dos_mode(conn, fsp->fsp_name);
922 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
923 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
924 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
925 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
926 } else {
927 return NT_STATUS_OK;
930 /* Store the DOS attributes in an EA. */
931 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
932 new_dosmode)) {
933 if (errno == 0) {
934 errno = EIO;
936 return map_nt_error_from_unix(errno);
939 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
940 FILE_NOTIFY_CHANGE_ATTRIBUTES,
941 fsp->fsp_name->base_name);
943 fsp->is_sparse = sparse;
945 return NT_STATUS_OK;
948 /*******************************************************************
949 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
950 than POSIX.
951 *******************************************************************/
953 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
954 struct smb_file_time *ft)
956 int ret = -1;
958 errno = 0;
960 DEBUG(6, ("file_ntime: actime: %s",
961 time_to_asc(convert_timespec_to_time_t(ft->atime))));
962 DEBUG(6, ("file_ntime: modtime: %s",
963 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
964 DEBUG(6, ("file_ntime: ctime: %s",
965 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
966 DEBUG(6, ("file_ntime: createtime: %s",
967 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
969 /* Don't update the time on read-only shares */
970 /* We need this as set_filetime (which can be called on
971 close and other paths) can end up calling this function
972 without the NEED_WRITE protection. Found by :
973 Leo Weppelman <leo@wau.mis.ah.nl>
976 if (!CAN_WRITE(conn)) {
977 return 0;
980 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
981 return 0;
984 if((errno != EPERM) && (errno != EACCES)) {
985 return -1;
988 if(!lp_dos_filetimes(SNUM(conn))) {
989 return -1;
992 /* We have permission (given by the Samba admin) to
993 break POSIX semantics and allow a user to change
994 the time on a file they don't own but can write to
995 (as DOS does).
998 /* Check if we have write access. */
999 if (can_write_to_file(conn, smb_fname)) {
1000 /* We are allowed to become root and change the filetime. */
1001 become_root();
1002 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1003 unbecome_root();
1006 return ret;
1009 /******************************************************************
1010 Force a "sticky" write time on a pathname. This will always be
1011 returned on all future write time queries and set on close.
1012 ******************************************************************/
1014 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1016 if (null_timespec(mtime)) {
1017 return true;
1020 if (!set_sticky_write_time(fileid, mtime)) {
1021 return false;
1024 return true;
1027 /******************************************************************
1028 Force a "sticky" write time on an fsp. This will always be
1029 returned on all future write time queries and set on close.
1030 ******************************************************************/
1032 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1034 if (null_timespec(mtime)) {
1035 return true;
1038 fsp->write_time_forced = true;
1039 TALLOC_FREE(fsp->update_write_time_event);
1041 return set_sticky_write_time_path(fsp->file_id, mtime);
1044 /******************************************************************
1045 Set a create time EA.
1046 ******************************************************************/
1048 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1049 const struct smb_filename *psmb_fname,
1050 struct timespec create_time)
1052 struct smb_filename *smb_fname;
1053 uint32_t dosmode;
1054 int ret;
1056 if (!lp_store_dos_attributes(SNUM(conn))) {
1057 return NT_STATUS_OK;
1060 smb_fname = synthetic_smb_fname(talloc_tos(), psmb_fname->base_name,
1061 NULL, &psmb_fname->st);
1063 if (smb_fname == NULL) {
1064 return NT_STATUS_NO_MEMORY;
1067 dosmode = dos_mode(conn, smb_fname);
1069 smb_fname->st.st_ex_btime = create_time;
1071 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1072 if (ret == -1) {
1073 map_nt_error_from_unix(errno);
1076 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1077 smb_fname_str_dbg(smb_fname)));
1079 return NT_STATUS_OK;
1082 /******************************************************************
1083 Return a create time.
1084 ******************************************************************/
1086 struct timespec get_create_timespec(connection_struct *conn,
1087 struct files_struct *fsp,
1088 const struct smb_filename *smb_fname)
1090 return smb_fname->st.st_ex_btime;
1093 /******************************************************************
1094 Return a change time (may look at EA in future).
1095 ******************************************************************/
1097 struct timespec get_change_timespec(connection_struct *conn,
1098 struct files_struct *fsp,
1099 const struct smb_filename *smb_fname)
1101 return smb_fname->st.st_ex_mtime;
1104 /****************************************************************************
1105 Get a real open file handle we can do meta-data operations on. As it's
1106 going to be used under root access only on meta-data we should look for
1107 any existing open file handle first, and use that in preference (also to
1108 avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1109 ****************************************************************************/
1111 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1112 struct smb_filename *smb_fname,
1113 files_struct **ret_fsp,
1114 bool *need_close)
1116 NTSTATUS status;
1117 files_struct *fsp;
1118 struct file_id file_id;
1120 *need_close = false;
1122 if (!VALID_STAT(smb_fname->st)) {
1123 return NT_STATUS_INVALID_PARAMETER;
1126 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1128 for(fsp = file_find_di_first(conn->sconn, file_id);
1129 fsp;
1130 fsp = file_find_di_next(fsp)) {
1131 if (fsp->fh->fd != -1) {
1132 *ret_fsp = fsp;
1133 return NT_STATUS_OK;
1137 /* Opens an INTERNAL_OPEN_ONLY write handle. */
1138 status = SMB_VFS_CREATE_FILE(
1139 conn, /* conn */
1140 NULL, /* req */
1141 0, /* root_dir_fid */
1142 smb_fname, /* fname */
1143 FILE_WRITE_DATA, /* access_mask */
1144 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1145 FILE_SHARE_DELETE),
1146 FILE_OPEN, /* create_disposition*/
1147 0, /* create_options */
1148 0, /* file_attributes */
1149 INTERNAL_OPEN_ONLY, /* oplock_request */
1150 NULL, /* lease */
1151 0, /* allocation_size */
1152 0, /* private_flags */
1153 NULL, /* sd */
1154 NULL, /* ea_list */
1155 ret_fsp, /* result */
1156 NULL, /* pinfo */
1157 NULL, NULL); /* create context */
1159 if (NT_STATUS_IS_OK(status)) {
1160 *need_close = true;
1162 return status;