s3:libsmb: move cli_session_setup_get_account into cli_session_creds_init()
[Samba.git] / source3 / smbd / dosmode.c
blobdab47c09c9962ba19e32dd2f3c069d9c2012fad4
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 const struct smb_filename *smb_fname,
31 files_struct **ret_fsp,
32 bool *need_close);
34 static void dos_mode_debug_print(const char *func, uint32_t mode)
36 fstring modestr;
38 if (DEBUGLEVEL < DBGLVL_INFO) {
39 return;
42 modestr[0] = '\0';
44 if (mode & FILE_ATTRIBUTE_HIDDEN) {
45 fstrcat(modestr, "h");
47 if (mode & FILE_ATTRIBUTE_READONLY) {
48 fstrcat(modestr, "r");
50 if (mode & FILE_ATTRIBUTE_SYSTEM) {
51 fstrcat(modestr, "s");
53 if (mode & FILE_ATTRIBUTE_DIRECTORY) {
54 fstrcat(modestr, "d");
56 if (mode & FILE_ATTRIBUTE_ARCHIVE) {
57 fstrcat(modestr, "a");
59 if (mode & FILE_ATTRIBUTE_SPARSE) {
60 fstrcat(modestr, "[sparse]");
62 if (mode & FILE_ATTRIBUTE_OFFLINE) {
63 fstrcat(modestr, "[offline]");
65 if (mode & FILE_ATTRIBUTE_COMPRESSED) {
66 fstrcat(modestr, "[compressed]");
69 DBG_INFO("%s returning (0x%x): \"%s\"\n", func, (unsigned)mode,
70 modestr);
73 static uint32_t filter_mode_by_protocol(uint32_t mode)
75 if (get_Protocol() <= PROTOCOL_LANMAN2) {
76 DEBUG(10,("filter_mode_by_protocol: "
77 "filtering result 0x%x to 0x%x\n",
78 (unsigned int)mode,
79 (unsigned int)(mode & 0x3f) ));
80 mode &= 0x3f;
82 return mode;
85 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
87 #ifdef S_ISLNK
88 #if LINKS_READ_ONLY
89 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
90 return FILE_ATTRIBUTE_READONLY;
91 #endif
92 #endif
93 return 0;
96 /****************************************************************************
97 Change a dos mode to a unix mode.
98 Base permission for files:
99 if creating file and inheriting (i.e. parent_dir != NULL)
100 apply read/write bits from parent directory.
101 else
102 everybody gets read bit set
103 dos readonly is represented in unix by removing everyone's write bit
104 dos archive is represented in unix by the user's execute bit
105 dos system is represented in unix by the group's execute bit
106 dos hidden is represented in unix by the other's execute bit
107 if !inheriting {
108 Then apply create mask,
109 then add force bits.
111 Base permission for directories:
112 dos directory is represented in unix by unix's dir bit and the exec bit
113 if !inheriting {
114 Then apply create mask,
115 then add force bits.
117 ****************************************************************************/
119 mode_t unix_mode(connection_struct *conn, int dosmode,
120 const struct smb_filename *smb_fname,
121 const char *inherit_from_dir)
123 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
124 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
125 * inheriting. */
127 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
128 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
131 if ((inherit_from_dir != NULL) && lp_inherit_permissions(SNUM(conn))) {
132 struct smb_filename *smb_fname_parent;
134 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
135 smb_fname_str_dbg(smb_fname),
136 inherit_from_dir));
138 smb_fname_parent = synthetic_smb_fname(talloc_tos(),
139 inherit_from_dir,
140 NULL,
141 NULL,
142 smb_fname->flags);
143 if (smb_fname_parent == NULL) {
144 DEBUG(1,("unix_mode(%s) failed, [dir %s]: No memory\n",
145 smb_fname_str_dbg(smb_fname),
146 inherit_from_dir));
147 return(0);
150 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
151 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
152 smb_fname_str_dbg(smb_fname),
153 inherit_from_dir, strerror(errno)));
154 TALLOC_FREE(smb_fname_parent);
155 return(0); /* *** shouldn't happen! *** */
158 /* Save for later - but explicitly remove setuid bit for safety. */
159 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
160 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
161 smb_fname_str_dbg(smb_fname), (int)dir_mode));
162 /* Clear "result" */
163 result = 0;
164 TALLOC_FREE(smb_fname_parent);
167 if (IS_DOS_DIR(dosmode)) {
168 /* We never make directories read only for the owner as under DOS a user
169 can always create a file in a read-only directory. */
170 result |= (S_IFDIR | S_IWUSR);
172 if (dir_mode) {
173 /* Inherit mode of parent directory. */
174 result |= dir_mode;
175 } else {
176 /* Provisionally add all 'x' bits */
177 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
179 /* Apply directory mask */
180 result &= lp_directory_mask(SNUM(conn));
181 /* Add in force bits */
182 result |= lp_force_directory_mode(SNUM(conn));
184 } else {
185 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
186 result |= S_IXUSR;
188 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
189 result |= S_IXGRP;
191 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
192 result |= S_IXOTH;
194 if (dir_mode) {
195 /* Inherit 666 component of parent directory mode */
196 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
197 } else {
198 /* Apply mode mask */
199 result &= lp_create_mask(SNUM(conn));
200 /* Add in force bits */
201 result |= lp_force_create_mode(SNUM(conn));
205 DBG_INFO("unix_mode(%s) returning 0%o\n",
206 smb_fname_str_dbg(smb_fname), (int)result);
208 return(result);
211 /****************************************************************************
212 Change a unix mode to a dos mode.
213 ****************************************************************************/
215 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
216 const struct smb_filename *smb_fname)
218 int result = 0;
219 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
221 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
222 /* if we can find out if a file is immutable we should report it r/o */
223 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
224 result |= FILE_ATTRIBUTE_READONLY;
226 #endif
227 if (ro_opts == MAP_READONLY_YES) {
228 /* Original Samba method - map inverse of user "w" bit. */
229 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
230 result |= FILE_ATTRIBUTE_READONLY;
232 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
233 /* Check actual permissions for read-only. */
234 if (!can_write_to_file(conn, smb_fname)) {
235 result |= FILE_ATTRIBUTE_READONLY;
237 } /* Else never set the readonly bit. */
239 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
240 result |= FILE_ATTRIBUTE_ARCHIVE;
242 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
243 result |= FILE_ATTRIBUTE_SYSTEM;
245 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
246 result |= FILE_ATTRIBUTE_HIDDEN;
248 if (S_ISDIR(smb_fname->st.st_ex_mode))
249 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
251 result |= set_link_read_only_flag(&smb_fname->st);
253 dos_mode_debug_print(__func__, result);
255 return result;
258 /****************************************************************************
259 Get DOS attributes from an EA.
260 This can also pull the create time into the stat struct inside smb_fname.
261 ****************************************************************************/
263 NTSTATUS get_ea_dos_attribute(connection_struct *conn,
264 struct smb_filename *smb_fname,
265 uint32_t *pattr)
267 struct xattr_DOSATTRIB dosattrib;
268 enum ndr_err_code ndr_err;
269 DATA_BLOB blob;
270 ssize_t sizeret;
271 fstring attrstr;
272 uint32_t dosattr;
274 if (!lp_store_dos_attributes(SNUM(conn))) {
275 return NT_STATUS_NOT_IMPLEMENTED;
278 /* Don't reset pattr to zero as we may already have filename-based attributes we
279 need to preserve. */
281 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
282 SAMBA_XATTR_DOS_ATTRIB, attrstr,
283 sizeof(attrstr));
284 if (sizeret == -1) {
285 DBG_INFO("Cannot get attribute "
286 "from EA on file %s: Error = %s\n",
287 smb_fname_str_dbg(smb_fname), strerror(errno));
288 return map_nt_error_from_unix(errno);
291 blob.data = (uint8_t *)attrstr;
292 blob.length = sizeret;
294 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
295 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
297 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
298 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
299 "from EA on file %s: Error = %s\n",
300 smb_fname_str_dbg(smb_fname),
301 ndr_errstr(ndr_err)));
302 return ndr_map_error2ntstatus(ndr_err);
305 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
306 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
308 switch (dosattrib.version) {
309 case 0xFFFF:
310 dosattr = dosattrib.info.compatinfoFFFF.attrib;
311 break;
312 case 1:
313 dosattr = dosattrib.info.info1.attrib;
314 if (!null_nttime(dosattrib.info.info1.create_time)) {
315 struct timespec create_time =
316 nt_time_to_unix_timespec(
317 dosattrib.info.info1.create_time);
319 update_stat_ex_create_time(&smb_fname->st,
320 create_time);
322 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
323 "set btime %s\n",
324 smb_fname_str_dbg(smb_fname),
325 time_to_asc(convert_timespec_to_time_t(
326 create_time)) ));
328 break;
329 case 2:
330 dosattr = dosattrib.info.oldinfo2.attrib;
331 /* Don't know what flags to check for this case. */
332 break;
333 case 3:
334 dosattr = dosattrib.info.info3.attrib;
335 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
336 !null_nttime(dosattrib.info.info3.create_time)) {
337 struct timespec create_time =
338 nt_time_to_unix_timespec(
339 dosattrib.info.info3.create_time);
341 update_stat_ex_create_time(&smb_fname->st,
342 create_time);
344 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
345 "set btime %s\n",
346 smb_fname_str_dbg(smb_fname),
347 time_to_asc(convert_timespec_to_time_t(
348 create_time)) ));
350 break;
351 default:
352 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
353 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
354 attrstr));
355 /* Should this be INTERNAL_ERROR? */
356 return NT_STATUS_INVALID_PARAMETER;
359 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
360 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
362 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
363 *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
365 dos_mode_debug_print(__func__, *pattr);
367 return NT_STATUS_OK;
370 /****************************************************************************
371 Set DOS attributes in an EA.
372 Also sets the create time.
373 ****************************************************************************/
375 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
376 const struct smb_filename *smb_fname,
377 uint32_t dosmode)
379 struct xattr_DOSATTRIB dosattrib;
380 enum ndr_err_code ndr_err;
381 DATA_BLOB blob;
383 if (!lp_store_dos_attributes(SNUM(conn))) {
384 return NT_STATUS_NOT_IMPLEMENTED;
388 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
389 * vfs_default via DMAPI if that is enabled.
391 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
393 ZERO_STRUCT(dosattrib);
394 ZERO_STRUCT(blob);
396 dosattrib.version = 3;
397 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
398 XATTR_DOSINFO_CREATE_TIME;
399 dosattrib.info.info3.attrib = dosmode;
400 dosattrib.info.info3.create_time = unix_timespec_to_nt_time(
401 smb_fname->st.st_ex_btime);
403 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
404 (unsigned int)dosmode,
405 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
406 smb_fname_str_dbg(smb_fname) ));
408 ndr_err = ndr_push_struct_blob(
409 &blob, talloc_tos(), &dosattrib,
410 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
412 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
413 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
414 ndr_errstr(ndr_err)));
415 return ndr_map_error2ntstatus(ndr_err);
418 if (blob.data == NULL || blob.length == 0) {
419 /* Should this be INTERNAL_ERROR? */
420 return NT_STATUS_INVALID_PARAMETER;
423 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
424 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
425 0) == -1) {
426 NTSTATUS status = NT_STATUS_OK;
427 bool need_close = false;
428 files_struct *fsp = NULL;
430 if((errno != EPERM) && (errno != EACCES)) {
431 DBG_INFO("Cannot set "
432 "attribute EA on file %s: Error = %s\n",
433 smb_fname_str_dbg(smb_fname), strerror(errno));
434 return map_nt_error_from_unix(errno);
437 /* We want DOS semantics, ie allow non owner with write permission to change the
438 bits on a file. Just like file_ntimes below.
441 /* Check if we have write access. */
442 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
443 return NT_STATUS_ACCESS_DENIED;
445 if (!can_write_to_file(conn, smb_fname)) {
446 return NT_STATUS_ACCESS_DENIED;
450 * We need to get an open file handle to do the
451 * metadata operation under root.
454 status = get_file_handle_for_metadata(conn,
455 smb_fname,
456 &fsp,
457 &need_close);
458 if (!NT_STATUS_IS_OK(status)) {
459 return status;
462 become_root();
463 if (SMB_VFS_FSETXATTR(fsp,
464 SAMBA_XATTR_DOS_ATTRIB, blob.data,
465 blob.length, 0) == 0) {
466 status = NT_STATUS_OK;
468 unbecome_root();
469 if (need_close) {
470 close_file(NULL, fsp, NORMAL_CLOSE);
472 return status;
474 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
475 (unsigned int)dosmode,
476 smb_fname_str_dbg(smb_fname)));
477 return NT_STATUS_OK;
480 /****************************************************************************
481 Change a unix mode to a dos mode for an ms dfs link.
482 ****************************************************************************/
484 uint32_t dos_mode_msdfs(connection_struct *conn,
485 const struct smb_filename *smb_fname)
487 uint32_t result = 0;
489 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
491 if (!VALID_STAT(smb_fname->st)) {
492 return 0;
495 /* First do any modifications that depend on the path name. */
496 /* hide files with a name starting with a . */
497 if (lp_hide_dot_files(SNUM(conn))) {
498 const char *p = strrchr_m(smb_fname->base_name, '/');
499 if (p) {
500 p++;
501 } else {
502 p = smb_fname->base_name;
505 /* Only . and .. are not hidden. */
506 if (p[0] == '.' && !((p[1] == '\0') ||
507 (p[1] == '.' && p[2] == '\0'))) {
508 result |= FILE_ATTRIBUTE_HIDDEN;
512 result |= dos_mode_from_sbuf(conn, smb_fname);
514 /* Optimization : Only call is_hidden_path if it's not already
515 hidden. */
516 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
517 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
518 result |= FILE_ATTRIBUTE_HIDDEN;
521 if (result == 0) {
522 result = FILE_ATTRIBUTE_NORMAL;
525 result = filter_mode_by_protocol(result);
528 * Add in that it is a reparse point
530 result |= FILE_ATTRIBUTE_REPARSE_POINT;
532 dos_mode_debug_print(__func__, result);
534 return(result);
538 * check whether a file or directory is flagged as compressed.
540 static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
541 struct smb_filename *smb_fname,
542 bool *is_compressed)
544 NTSTATUS status;
545 uint16_t compression_fmt;
546 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
547 if (tmp_ctx == NULL) {
548 status = NT_STATUS_NO_MEMORY;
549 goto err_out;
552 status = SMB_VFS_GET_COMPRESSION(conn, tmp_ctx, NULL, smb_fname,
553 &compression_fmt);
554 if (!NT_STATUS_IS_OK(status)) {
555 goto err_ctx_free;
558 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
559 *is_compressed = true;
560 } else {
561 *is_compressed = false;
563 status = NT_STATUS_OK;
565 err_ctx_free:
566 talloc_free(tmp_ctx);
567 err_out:
568 return status;
571 static uint32_t dos_mode_from_name(connection_struct *conn,
572 const struct smb_filename *smb_fname,
573 uint32_t dosmode)
575 const char *p = NULL;
576 uint32_t result = dosmode;
578 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
579 lp_hide_dot_files(SNUM(conn)))
581 p = strrchr_m(smb_fname->base_name, '/');
582 if (p) {
583 p++;
584 } else {
585 p = smb_fname->base_name;
588 /* Only . and .. are not hidden. */
589 if ((p[0] == '.') &&
590 !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0')))
592 result |= FILE_ATTRIBUTE_HIDDEN;
596 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
597 IS_HIDDEN_PATH(conn, smb_fname->base_name))
599 result |= FILE_ATTRIBUTE_HIDDEN;
602 return result;
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_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
613 uint32_t result = 0;
614 NTSTATUS status = NT_STATUS_OK;
616 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
618 if (!VALID_STAT(smb_fname->st)) {
619 return 0;
622 /* Get the DOS attributes via the VFS if we can */
623 status = SMB_VFS_GET_DOS_ATTRIBUTES(conn, smb_fname, &result);
624 if (!NT_STATUS_IS_OK(status)) {
625 result |= dos_mode_from_sbuf(conn, smb_fname);
628 if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
629 bool compressed = false;
630 status = dos_mode_check_compressed(conn, smb_fname,
631 &compressed);
632 if (NT_STATUS_IS_OK(status) && compressed) {
633 result |= FILE_ATTRIBUTE_COMPRESSED;
637 result |= dos_mode_from_name(conn, smb_fname, result);
639 if (result == 0) {
640 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
641 result = FILE_ATTRIBUTE_DIRECTORY;
642 } else {
643 result = FILE_ATTRIBUTE_NORMAL;
647 result = filter_mode_by_protocol(result);
649 dos_mode_debug_print(__func__, result);
651 return result;
654 /*******************************************************************
655 chmod a file - but preserve some bits.
656 If "store dos attributes" is also set it will store the create time
657 from the stat struct in smb_fname (in NTTIME format) in the EA
658 attribute also.
659 ********************************************************************/
661 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
662 uint32_t dosmode, const char *parent_dir, bool newfile)
664 int mask=0;
665 mode_t tmp;
666 mode_t unixmode;
667 int ret = -1, lret = -1;
668 files_struct *fsp = NULL;
669 bool need_close = false;
670 NTSTATUS status;
672 if (!CAN_WRITE(conn)) {
673 errno = EROFS;
674 return -1;
677 dosmode &= SAMBA_ATTRIBUTES_MASK;
679 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
680 dosmode, smb_fname_str_dbg(smb_fname)));
682 unixmode = smb_fname->st.st_ex_mode;
684 get_acl_group_bits(conn, smb_fname->base_name,
685 &smb_fname->st.st_ex_mode);
687 if (S_ISDIR(smb_fname->st.st_ex_mode))
688 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
689 else
690 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
692 /* Store the DOS attributes in an EA by preference. */
693 status = SMB_VFS_SET_DOS_ATTRIBUTES(conn, smb_fname, dosmode);
694 if (NT_STATUS_IS_OK(status)) {
695 if (!newfile) {
696 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
697 FILE_NOTIFY_CHANGE_ATTRIBUTES,
698 smb_fname->base_name);
700 smb_fname->st.st_ex_mode = unixmode;
701 return 0;
702 } else {
704 * Only fall back to using UNIX modes if
705 * we get NOT_IMPLEMENTED.
707 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
708 errno = map_errno_from_nt_status(status);
709 return -1;
713 /* Fall back to UNIX modes. */
714 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
716 /* preserve the file type bits */
717 mask |= S_IFMT;
719 /* preserve the s bits */
720 mask |= (S_ISUID | S_ISGID);
722 /* preserve the t bit */
723 #ifdef S_ISVTX
724 mask |= S_ISVTX;
725 #endif
727 /* possibly preserve the x bits */
728 if (!MAP_ARCHIVE(conn))
729 mask |= S_IXUSR;
730 if (!MAP_SYSTEM(conn))
731 mask |= S_IXGRP;
732 if (!MAP_HIDDEN(conn))
733 mask |= S_IXOTH;
735 unixmode |= (smb_fname->st.st_ex_mode & mask);
737 /* if we previously had any r bits set then leave them alone */
738 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
739 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
740 unixmode |= tmp;
743 /* if we previously had any w bits set then leave them alone
744 whilst adding in the new w bits, if the new mode is not rdonly */
745 if (!IS_DOS_READONLY(dosmode)) {
746 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
750 * From the chmod 2 man page:
752 * "If the calling process is not privileged, and the group of the file
753 * does not match the effective group ID of the process or one of its
754 * supplementary group IDs, the S_ISGID bit will be turned off, but
755 * this will not cause an error to be returned."
757 * Simply refuse to do the chmod in this case.
760 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
761 geteuid() != sec_initial_uid() &&
762 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
763 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
764 "set for directory %s\n",
765 smb_fname_str_dbg(smb_fname)));
766 errno = EPERM;
767 return -1;
770 ret = SMB_VFS_CHMOD(conn, smb_fname, unixmode);
771 if (ret == 0) {
772 if(!newfile || (lret != -1)) {
773 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
774 FILE_NOTIFY_CHANGE_ATTRIBUTES,
775 smb_fname->base_name);
777 smb_fname->st.st_ex_mode = unixmode;
778 return 0;
781 if((errno != EPERM) && (errno != EACCES))
782 return -1;
784 if(!lp_dos_filemode(SNUM(conn)))
785 return -1;
787 /* We want DOS semantics, ie allow non owner with write permission to change the
788 bits on a file. Just like file_ntimes below.
791 if (!can_write_to_file(conn, smb_fname)) {
792 errno = EACCES;
793 return -1;
797 * We need to get an open file handle to do the
798 * metadata operation under root.
801 status = get_file_handle_for_metadata(conn,
802 smb_fname,
803 &fsp,
804 &need_close);
805 if (!NT_STATUS_IS_OK(status)) {
806 errno = map_errno_from_nt_status(status);
807 return -1;
810 become_root();
811 ret = SMB_VFS_FCHMOD(fsp, unixmode);
812 unbecome_root();
813 if (need_close) {
814 close_file(NULL, fsp, NORMAL_CLOSE);
816 if (!newfile) {
817 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
818 FILE_NOTIFY_CHANGE_ATTRIBUTES,
819 smb_fname->base_name);
821 if (ret == 0) {
822 smb_fname->st.st_ex_mode = unixmode;
825 return( ret );
829 NTSTATUS file_set_sparse(connection_struct *conn,
830 files_struct *fsp,
831 bool sparse)
833 uint32_t old_dosmode;
834 uint32_t new_dosmode;
835 NTSTATUS status;
837 if (!CAN_WRITE(conn)) {
838 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
839 "on readonly share[%s]\n",
840 smb_fname_str_dbg(fsp->fsp_name),
841 sparse,
842 lp_servicename(talloc_tos(), SNUM(conn))));
843 return NT_STATUS_MEDIA_WRITE_PROTECTED;
847 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
848 * following access flags are granted.
850 if ((fsp->access_mask & (FILE_WRITE_DATA
851 | FILE_WRITE_ATTRIBUTES
852 | SEC_FILE_APPEND_DATA)) == 0) {
853 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
854 "access_mask[0x%08X] - access denied\n",
855 smb_fname_str_dbg(fsp->fsp_name),
856 sparse,
857 fsp->access_mask));
858 return NT_STATUS_ACCESS_DENIED;
861 if (fsp->is_directory) {
862 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
863 (sparse ? "set" : "clear"),
864 smb_fname_str_dbg(fsp->fsp_name)));
865 return NT_STATUS_INVALID_PARAMETER;
868 if (IS_IPC(conn) || IS_PRINT(conn)) {
869 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
870 (sparse ? "set" : "clear")));
871 return NT_STATUS_INVALID_PARAMETER;
874 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
875 sparse, smb_fname_str_dbg(fsp->fsp_name)));
877 if (!lp_store_dos_attributes(SNUM(conn))) {
878 return NT_STATUS_INVALID_DEVICE_REQUEST;
881 status = vfs_stat_fsp(fsp);
882 if (!NT_STATUS_IS_OK(status)) {
883 return status;
886 old_dosmode = dos_mode(conn, fsp->fsp_name);
888 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
889 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
890 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
891 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
892 } else {
893 return NT_STATUS_OK;
896 /* Store the DOS attributes in an EA. */
897 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
898 if (!NT_STATUS_IS_OK(status)) {
899 return status;
902 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
903 FILE_NOTIFY_CHANGE_ATTRIBUTES,
904 fsp->fsp_name->base_name);
906 fsp->is_sparse = sparse;
908 return NT_STATUS_OK;
911 /*******************************************************************
912 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
913 than POSIX.
914 *******************************************************************/
916 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
917 struct smb_file_time *ft)
919 int ret = -1;
921 errno = 0;
923 DEBUG(6, ("file_ntime: actime: %s",
924 time_to_asc(convert_timespec_to_time_t(ft->atime))));
925 DEBUG(6, ("file_ntime: modtime: %s",
926 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
927 DEBUG(6, ("file_ntime: ctime: %s",
928 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
929 DEBUG(6, ("file_ntime: createtime: %s",
930 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
932 /* Don't update the time on read-only shares */
933 /* We need this as set_filetime (which can be called on
934 close and other paths) can end up calling this function
935 without the NEED_WRITE protection. Found by :
936 Leo Weppelman <leo@wau.mis.ah.nl>
939 if (!CAN_WRITE(conn)) {
940 return 0;
943 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
944 return 0;
947 if((errno != EPERM) && (errno != EACCES)) {
948 return -1;
951 if(!lp_dos_filetimes(SNUM(conn))) {
952 return -1;
955 /* We have permission (given by the Samba admin) to
956 break POSIX semantics and allow a user to change
957 the time on a file they don't own but can write to
958 (as DOS does).
961 /* Check if we have write access. */
962 if (can_write_to_file(conn, smb_fname)) {
963 /* We are allowed to become root and change the filetime. */
964 become_root();
965 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
966 unbecome_root();
969 return ret;
972 /******************************************************************
973 Force a "sticky" write time on a pathname. This will always be
974 returned on all future write time queries and set on close.
975 ******************************************************************/
977 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
979 if (null_timespec(mtime)) {
980 return true;
983 if (!set_sticky_write_time(fileid, mtime)) {
984 return false;
987 return true;
990 /******************************************************************
991 Force a "sticky" write time on an fsp. This will always be
992 returned on all future write time queries and set on close.
993 ******************************************************************/
995 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
997 if (null_timespec(mtime)) {
998 return true;
1001 fsp->write_time_forced = true;
1002 TALLOC_FREE(fsp->update_write_time_event);
1004 return set_sticky_write_time_path(fsp->file_id, mtime);
1007 /******************************************************************
1008 Set a create time EA.
1009 ******************************************************************/
1011 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1012 const struct smb_filename *psmb_fname,
1013 struct timespec create_time)
1015 struct smb_filename *smb_fname;
1016 uint32_t dosmode;
1017 int ret;
1019 if (!lp_store_dos_attributes(SNUM(conn))) {
1020 return NT_STATUS_OK;
1023 smb_fname = synthetic_smb_fname(talloc_tos(),
1024 psmb_fname->base_name,
1025 NULL,
1026 &psmb_fname->st,
1027 psmb_fname->flags);
1029 if (smb_fname == NULL) {
1030 return NT_STATUS_NO_MEMORY;
1033 dosmode = dos_mode(conn, smb_fname);
1035 smb_fname->st.st_ex_btime = create_time;
1037 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1038 if (ret == -1) {
1039 return map_nt_error_from_unix(errno);
1042 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1043 smb_fname_str_dbg(smb_fname)));
1045 return NT_STATUS_OK;
1048 /******************************************************************
1049 Return a create time.
1050 ******************************************************************/
1052 struct timespec get_create_timespec(connection_struct *conn,
1053 struct files_struct *fsp,
1054 const struct smb_filename *smb_fname)
1056 return smb_fname->st.st_ex_btime;
1059 /******************************************************************
1060 Return a change time (may look at EA in future).
1061 ******************************************************************/
1063 struct timespec get_change_timespec(connection_struct *conn,
1064 struct files_struct *fsp,
1065 const struct smb_filename *smb_fname)
1067 return smb_fname->st.st_ex_mtime;
1070 /****************************************************************************
1071 Get a real open file handle we can do meta-data operations on. As it's
1072 going to be used under root access only on meta-data we should look for
1073 any existing open file handle first, and use that in preference (also to
1074 avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1075 ****************************************************************************/
1077 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1078 const struct smb_filename *smb_fname,
1079 files_struct **ret_fsp,
1080 bool *need_close)
1082 NTSTATUS status;
1083 files_struct *fsp;
1084 struct file_id file_id;
1085 struct smb_filename *smb_fname_cp = NULL;
1087 *need_close = false;
1089 if (!VALID_STAT(smb_fname->st)) {
1090 return NT_STATUS_INVALID_PARAMETER;
1093 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1095 for(fsp = file_find_di_first(conn->sconn, file_id);
1096 fsp;
1097 fsp = file_find_di_next(fsp)) {
1098 if (fsp->fh->fd != -1) {
1099 *ret_fsp = fsp;
1100 return NT_STATUS_OK;
1104 smb_fname_cp = cp_smb_filename(talloc_tos(),
1105 smb_fname);
1106 if (smb_fname_cp == NULL) {
1107 return NT_STATUS_NO_MEMORY;
1110 /* Opens an INTERNAL_OPEN_ONLY write handle. */
1111 status = SMB_VFS_CREATE_FILE(
1112 conn, /* conn */
1113 NULL, /* req */
1114 0, /* root_dir_fid */
1115 smb_fname_cp, /* fname */
1116 FILE_WRITE_DATA, /* access_mask */
1117 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1118 FILE_SHARE_DELETE),
1119 FILE_OPEN, /* create_disposition*/
1120 0, /* create_options */
1121 0, /* file_attributes */
1122 INTERNAL_OPEN_ONLY, /* oplock_request */
1123 NULL, /* lease */
1124 0, /* allocation_size */
1125 0, /* private_flags */
1126 NULL, /* sd */
1127 NULL, /* ea_list */
1128 ret_fsp, /* result */
1129 NULL, /* pinfo */
1130 NULL, NULL); /* create context */
1132 TALLOC_FREE(smb_fname_cp);
1134 if (NT_STATUS_IS_OK(status)) {
1135 *need_close = true;
1137 return status;