Revert "s3: remove unused Variable"
[Samba/fernandojvsilva.git] / source3 / smbd / dosmode.c
blobaaef09bc85cca8b5b78e7c400ff958d0b7d23dd2
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_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
38 #if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
39 if (sbuf->st_ex_size > sbuf->st_ex_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
40 return FILE_ATTRIBUTE_SPARSE;
42 #endif
43 return 0;
46 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
48 #ifdef S_ISLNK
49 #if LINKS_READ_ONLY
50 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
51 return aRONLY;
52 #endif
53 #endif
54 return 0;
57 /****************************************************************************
58 Change a dos mode to a unix mode.
59 Base permission for files:
60 if creating file and inheriting (i.e. parent_dir != NULL)
61 apply read/write bits from parent directory.
62 else
63 everybody gets read bit set
64 dos readonly is represented in unix by removing everyone's write bit
65 dos archive is represented in unix by the user's execute bit
66 dos system is represented in unix by the group's execute bit
67 dos hidden is represented in unix by the other's execute bit
68 if !inheriting {
69 Then apply create mask,
70 then add force bits.
72 Base permission for directories:
73 dos directory is represented in unix by unix's dir bit and the exec bit
74 if !inheriting {
75 Then apply create mask,
76 then add force bits.
78 ****************************************************************************/
80 mode_t unix_mode(connection_struct *conn, int dosmode,
81 const struct smb_filename *smb_fname,
82 const char *inherit_from_dir)
84 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
85 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
86 * inheriting. */
88 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
89 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
92 if ((inherit_from_dir != NULL) && lp_inherit_perms(SNUM(conn))) {
93 struct smb_filename *smb_fname_parent = NULL;
94 NTSTATUS status;
96 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
97 smb_fname_str_dbg(smb_fname),
98 inherit_from_dir));
100 status = create_synthetic_smb_fname(talloc_tos(),
101 inherit_from_dir, NULL,
102 NULL, &smb_fname_parent);
103 if (!NT_STATUS_IS_OK(status)) {
104 DEBUG(1,("unix_mode(%s) failed, [dir %s]: %s\n",
105 smb_fname_str_dbg(smb_fname),
106 inherit_from_dir, nt_errstr(status)));
107 return(0);
110 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
111 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
112 smb_fname_str_dbg(smb_fname),
113 inherit_from_dir, strerror(errno)));
114 TALLOC_FREE(smb_fname_parent);
115 return(0); /* *** shouldn't happen! *** */
118 /* Save for later - but explicitly remove setuid bit for safety. */
119 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
120 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
121 smb_fname_str_dbg(smb_fname), (int)dir_mode));
122 /* Clear "result" */
123 result = 0;
124 TALLOC_FREE(smb_fname_parent);
127 if (IS_DOS_DIR(dosmode)) {
128 /* We never make directories read only for the owner as under DOS a user
129 can always create a file in a read-only directory. */
130 result |= (S_IFDIR | S_IWUSR);
132 if (dir_mode) {
133 /* Inherit mode of parent directory. */
134 result |= dir_mode;
135 } else {
136 /* Provisionally add all 'x' bits */
137 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
139 /* Apply directory mask */
140 result &= lp_dir_mask(SNUM(conn));
141 /* Add in force bits */
142 result |= lp_force_dir_mode(SNUM(conn));
144 } else {
145 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
146 result |= S_IXUSR;
148 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
149 result |= S_IXGRP;
151 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
152 result |= S_IXOTH;
154 if (dir_mode) {
155 /* Inherit 666 component of parent directory mode */
156 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
157 } else {
158 /* Apply mode mask */
159 result &= lp_create_mask(SNUM(conn));
160 /* Add in force bits */
161 result |= lp_force_create_mode(SNUM(conn));
165 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
166 (int)result));
167 return(result);
170 /****************************************************************************
171 Change a unix mode to a dos mode.
172 ****************************************************************************/
174 static uint32 dos_mode_from_sbuf(connection_struct *conn,
175 const struct smb_filename *smb_fname)
177 int result = 0;
178 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
180 if (ro_opts == MAP_READONLY_YES) {
181 /* Original Samba method - map inverse of user "w" bit. */
182 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
183 result |= aRONLY;
185 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
186 /* Check actual permissions for read-only. */
187 if (!can_write_to_file(conn, smb_fname)) {
188 result |= aRONLY;
190 } /* Else never set the readonly bit. */
192 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
193 result |= aARCH;
195 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
196 result |= aSYSTEM;
198 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
199 result |= aHIDDEN;
201 if (S_ISDIR(smb_fname->st.st_ex_mode))
202 result = aDIR | (result & aRONLY);
204 result |= set_sparse_flag(&smb_fname->st);
205 result |= set_link_read_only_flag(&smb_fname->st);
207 DEBUG(8,("dos_mode_from_sbuf returning "));
209 if (result & aHIDDEN) DEBUG(8, ("h"));
210 if (result & aRONLY ) DEBUG(8, ("r"));
211 if (result & aSYSTEM) DEBUG(8, ("s"));
212 if (result & aDIR ) DEBUG(8, ("d"));
213 if (result & aARCH ) DEBUG(8, ("a"));
215 DEBUG(8,("\n"));
216 return result;
219 /****************************************************************************
220 Get DOS attributes from an EA.
221 This can also pull the create time into the stat struct inside smb_fname.
222 ****************************************************************************/
224 static bool get_ea_dos_attribute(connection_struct *conn,
225 struct smb_filename *smb_fname,
226 uint32 *pattr)
228 struct xattr_DOSATTRIB dosattrib;
229 enum ndr_err_code ndr_err;
230 DATA_BLOB blob;
231 ssize_t sizeret;
232 fstring attrstr;
233 uint32_t dosattr;
235 if (!lp_store_dos_attributes(SNUM(conn))) {
236 return False;
239 /* Don't reset pattr to zero as we may already have filename-based attributes we
240 need to preserve. */
242 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
243 SAMBA_XATTR_DOS_ATTRIB, attrstr,
244 sizeof(attrstr));
245 if (sizeret == -1) {
246 if (errno == ENOSYS
247 #if defined(ENOTSUP)
248 || errno == ENOTSUP) {
249 #else
251 #endif
252 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute "
253 "from EA on file %s: Error = %s\n",
254 smb_fname_str_dbg(smb_fname),
255 strerror(errno)));
256 set_store_dos_attributes(SNUM(conn), False);
258 return False;
261 blob.data = (uint8_t *)attrstr;
262 blob.length = sizeret;
264 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), NULL, &dosattrib,
265 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
267 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
268 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
270 switch (dosattrib.version) {
271 case 0xFFFF:
272 dosattr = dosattrib.info.compatinfoFFFF.attrib;
273 break;
274 case 1:
275 dosattr = dosattrib.info.info1.attrib;
276 if (!null_nttime(dosattrib.info.info1.create_time)) {
277 struct timespec create_time =
278 nt_time_to_unix_timespec(
279 &dosattrib.info.info1.create_time);
281 update_stat_ex_create_time(&smb_fname->st,
282 create_time);
284 DEBUG(10,("get_ea_dos_attributes: file %s case 1 "
285 "set btime %s\n",
286 smb_fname_str_dbg(smb_fname),
287 time_to_asc(convert_timespec_to_time_t(
288 create_time)) ));
290 break;
291 case 2:
292 dosattr = dosattrib.info.oldinfo2.attrib;
293 /* Don't know what flags to check for this case. */
294 break;
295 case 3:
296 dosattr = dosattrib.info.info3.attrib;
297 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
298 !null_nttime(dosattrib.info.info3.create_time)) {
299 struct timespec create_time =
300 nt_time_to_unix_timespec(
301 &dosattrib.info.info3.create_time);
303 update_stat_ex_create_time(&smb_fname->st,
304 create_time);
306 DEBUG(10,("get_ea_dos_attributes: file %s case 3 "
307 "set btime %s\n",
308 smb_fname_str_dbg(smb_fname),
309 time_to_asc(convert_timespec_to_time_t(
310 create_time)) ));
312 break;
313 default:
314 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on "
315 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
316 attrstr));
317 return false;
320 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
321 dosattr |= aDIR;
323 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
325 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
327 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
328 if (dosattr & aRONLY ) DEBUG(8, ("r"));
329 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
330 if (dosattr & aDIR ) DEBUG(8, ("d"));
331 if (dosattr & aARCH ) DEBUG(8, ("a"));
333 DEBUG(8,("\n"));
335 return True;
338 /****************************************************************************
339 Set DOS attributes in an EA.
340 Also sets the create time.
341 ****************************************************************************/
343 static bool set_ea_dos_attribute(connection_struct *conn,
344 struct smb_filename *smb_fname,
345 uint32 dosmode)
347 struct xattr_DOSATTRIB dosattrib;
348 enum ndr_err_code ndr_err;
349 DATA_BLOB blob;
350 files_struct *fsp = NULL;
351 bool ret = false;
353 if (!lp_store_dos_attributes(SNUM(conn))) {
354 return False;
357 ZERO_STRUCT(dosattrib);
358 ZERO_STRUCT(blob);
360 dosattrib.version = 3;
361 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
362 XATTR_DOSINFO_CREATE_TIME;
363 dosattrib.info.info3.attrib = dosmode;
364 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
365 smb_fname->st.st_ex_btime);
367 ndr_err = ndr_push_struct_blob(
368 &blob, talloc_tos(), NULL, &dosattrib,
369 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
371 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
372 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
373 ndr_errstr(ndr_err)));
374 return false;
377 if (blob.data == NULL || blob.length == 0) {
378 return false;
381 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
382 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
383 0) == -1) {
384 if((errno != EPERM) && (errno != EACCES)) {
385 if (errno == ENOSYS
386 #if defined(ENOTSUP)
387 || errno == ENOTSUP) {
388 #else
390 #endif
391 DEBUG(1,("set_ea_dos_attributes: Cannot set "
392 "attribute EA on file %s: Error = %s\n",
393 smb_fname_str_dbg(smb_fname),
394 strerror(errno) ));
395 set_store_dos_attributes(SNUM(conn), False);
397 return false;
400 /* We want DOS semantics, ie allow non owner with write permission to change the
401 bits on a file. Just like file_ntimes below.
404 /* Check if we have write access. */
405 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
406 return false;
409 * We need to open the file with write access whilst
410 * still in our current user context. This ensures we
411 * are not violating security in doing the setxattr.
414 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
415 &fsp)))
416 return ret;
417 become_root();
418 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
419 SAMBA_XATTR_DOS_ATTRIB, blob.data,
420 blob.length, 0) == 0) {
421 ret = true;
423 unbecome_root();
424 close_file_fchmod(NULL, fsp);
425 return ret;
427 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
428 (unsigned int)dosmode,
429 smb_fname_str_dbg(smb_fname)));
430 return true;
433 /****************************************************************************
434 Change a unix mode to a dos mode for an ms dfs link.
435 ****************************************************************************/
437 uint32 dos_mode_msdfs(connection_struct *conn,
438 const struct smb_filename *smb_fname)
440 uint32 result = 0;
442 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
444 if (!VALID_STAT(smb_fname->st)) {
445 return 0;
448 /* First do any modifications that depend on the path name. */
449 /* hide files with a name starting with a . */
450 if (lp_hide_dot_files(SNUM(conn))) {
451 const char *p = strrchr_m(smb_fname->base_name, '/');
452 if (p) {
453 p++;
454 } else {
455 p = smb_fname->base_name;
458 /* Only . and .. are not hidden. */
459 if (p[0] == '.' && !((p[1] == '\0') ||
460 (p[1] == '.' && p[2] == '\0'))) {
461 result |= aHIDDEN;
465 result |= dos_mode_from_sbuf(conn, smb_fname);
467 /* Optimization : Only call is_hidden_path if it's not already
468 hidden. */
469 if (!(result & aHIDDEN) &&
470 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
471 result |= aHIDDEN;
474 if (result == 0) {
475 result = FILE_ATTRIBUTE_NORMAL;
478 result = filter_mode_by_protocol(result);
480 DEBUG(8,("dos_mode_msdfs returning "));
482 if (result & aHIDDEN) DEBUG(8, ("h"));
483 if (result & aRONLY ) DEBUG(8, ("r"));
484 if (result & aSYSTEM) DEBUG(8, ("s"));
485 if (result & aDIR ) DEBUG(8, ("d"));
486 if (result & aARCH ) DEBUG(8, ("a"));
487 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
489 DEBUG(8,("\n"));
491 return(result);
494 #ifdef HAVE_STAT_DOS_FLAGS
495 /****************************************************************************
496 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
497 ****************************************************************************/
499 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
501 uint32_t dos_stat_flags = 0;
503 if (dosmode & aARCH)
504 dos_stat_flags |= UF_DOS_ARCHIVE;
505 if (dosmode & aHIDDEN)
506 dos_stat_flags |= UF_DOS_HIDDEN;
507 if (dosmode & aRONLY)
508 dos_stat_flags |= UF_DOS_RO;
509 if (dosmode & aSYSTEM)
510 dos_stat_flags |= UF_DOS_SYSTEM;
511 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
512 dos_stat_flags |= UF_DOS_NOINDEX;
514 return dos_stat_flags;
517 /****************************************************************************
518 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
519 ****************************************************************************/
521 static bool get_stat_dos_flags(connection_struct *conn,
522 const struct smb_filename *smb_fname,
523 uint32_t *dosmode)
525 SMB_ASSERT(VALID_STAT(smb_fname->st));
526 SMB_ASSERT(dosmode);
528 if (!lp_store_dos_attributes(SNUM(conn))) {
529 return false;
532 DEBUG(5, ("Getting stat dos attributes for %s.\n",
533 smb_fname_str_dbg(smb_fname)));
535 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
536 *dosmode |= aARCH;
537 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
538 *dosmode |= aHIDDEN;
539 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
540 *dosmode |= aRONLY;
541 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
542 *dosmode |= aSYSTEM;
543 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
544 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
545 if (S_ISDIR(smb_fname->st.st_ex_mode))
546 *dosmode |= aDIR;
548 *dosmode |= set_sparse_flag(&smb_fname->st);
549 *dosmode |= set_link_read_only_flag(&smb_fname->st);
551 return true;
554 /****************************************************************************
555 Sets DOS attributes, stored in st_ex_flags of the inode.
556 ****************************************************************************/
558 static bool set_stat_dos_flags(connection_struct *conn,
559 const struct smb_filename *smb_fname,
560 uint32_t dosmode,
561 bool *attributes_changed)
563 uint32_t new_flags = 0;
564 int error = 0;
566 SMB_ASSERT(VALID_STAT(smb_fname->st));
567 SMB_ASSERT(attributes_changed);
569 *attributes_changed = false;
571 if (!lp_store_dos_attributes(SNUM(conn))) {
572 return false;
575 DEBUG(5, ("Setting stat dos attributes for %s.\n",
576 smb_fname_str_dbg(smb_fname)));
578 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
579 dos_attributes_to_stat_dos_flags(dosmode);
581 /* Return early if no flags changed. */
582 if (new_flags == smb_fname->st.st_ex_flags)
583 return true;
585 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
586 smb_fname->st.st_ex_flags));
588 /* Set new flags with chflags. */
589 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
590 if (error) {
591 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
592 "file %s! errno=%d\n", new_flags,
593 smb_fname_str_dbg(smb_fname), errno));
594 return false;
597 *attributes_changed = true;
598 return true;
600 #endif /* HAVE_STAT_DOS_FLAGS */
602 /****************************************************************************
603 Change a unix mode to a dos mode.
604 May also read the create timespec into the stat struct in smb_fname
605 if "store dos attributes" is true.
606 ****************************************************************************/
608 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
610 uint32 result = 0;
611 bool offline, used_stat_dos_flags = false;
613 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
615 if (!VALID_STAT(smb_fname->st)) {
616 return 0;
619 /* First do any modifications that depend on the path name. */
620 /* hide files with a name starting with a . */
621 if (lp_hide_dot_files(SNUM(conn))) {
622 const char *p = strrchr_m(smb_fname->base_name,'/');
623 if (p) {
624 p++;
625 } else {
626 p = smb_fname->base_name;
629 /* Only . and .. are not hidden. */
630 if (p[0] == '.' && !((p[1] == '\0') ||
631 (p[1] == '.' && p[2] == '\0'))) {
632 result |= aHIDDEN;
636 #ifdef HAVE_STAT_DOS_FLAGS
637 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
638 #endif
639 if (!used_stat_dos_flags) {
640 /* Get the DOS attributes from an EA by preference. */
641 if (get_ea_dos_attribute(conn, smb_fname, &result)) {
642 result |= set_sparse_flag(&smb_fname->st);
643 } else {
644 result |= dos_mode_from_sbuf(conn, smb_fname);
648 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname->base_name, &smb_fname->st);
649 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
650 result |= FILE_ATTRIBUTE_OFFLINE;
653 /* Optimization : Only call is_hidden_path if it's not already
654 hidden. */
655 if (!(result & aHIDDEN) &&
656 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
657 result |= aHIDDEN;
660 if (result == 0) {
661 result = FILE_ATTRIBUTE_NORMAL;
664 result = filter_mode_by_protocol(result);
666 DEBUG(8,("dos_mode returning "));
668 if (result & aHIDDEN) DEBUG(8, ("h"));
669 if (result & aRONLY ) DEBUG(8, ("r"));
670 if (result & aSYSTEM) DEBUG(8, ("s"));
671 if (result & aDIR ) DEBUG(8, ("d"));
672 if (result & aARCH ) DEBUG(8, ("a"));
673 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
675 DEBUG(8,("\n"));
677 return(result);
680 /*******************************************************************
681 chmod a file - but preserve some bits.
682 If "store dos attributes" is also set it will store the create time
683 from the stat struct in smb_fname (in NTTIME format) in the EA
684 attribute also.
685 ********************************************************************/
687 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
688 uint32 dosmode, const char *parent_dir, bool newfile)
690 int mask=0;
691 mode_t tmp;
692 mode_t unixmode;
693 int ret = -1, lret = -1;
694 uint32_t old_mode;
695 struct timespec new_create_timespec;
697 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
698 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
700 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
701 dosmode, smb_fname_str_dbg(smb_fname)));
703 unixmode = smb_fname->st.st_ex_mode;
705 get_acl_group_bits(conn, smb_fname->base_name,
706 &smb_fname->st.st_ex_mode);
708 if (S_ISDIR(smb_fname->st.st_ex_mode))
709 dosmode |= aDIR;
710 else
711 dosmode &= ~aDIR;
713 new_create_timespec = smb_fname->st.st_ex_btime;
715 old_mode = dos_mode(conn, smb_fname);
717 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
718 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
719 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname->base_name);
720 if (lret == -1) {
721 DEBUG(0, ("set_dos_mode: client has asked to "
722 "set FILE_ATTRIBUTE_OFFLINE to "
723 "%s/%s but there was an error while "
724 "setting it or it is not "
725 "supported.\n", parent_dir,
726 smb_fname_str_dbg(smb_fname)));
731 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
732 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
734 if (old_mode == dosmode &&
735 (timespec_compare(&new_create_timespec,
736 &smb_fname->st.st_ex_btime) == 0)) {
737 smb_fname->st.st_ex_mode = unixmode;
738 return(0);
741 smb_fname->st.st_ex_btime = new_create_timespec;
743 #ifdef HAVE_STAT_DOS_FLAGS
745 bool attributes_changed;
747 if (set_stat_dos_flags(conn, smb_fname, dosmode,
748 &attributes_changed))
750 if (!newfile && attributes_changed) {
751 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
752 FILE_NOTIFY_CHANGE_ATTRIBUTES,
753 smb_fname->base_name);
755 smb_fname->st.st_ex_mode = unixmode;
756 return 0;
759 #endif
760 /* Store the DOS attributes in an EA by preference. */
761 if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
762 if (!newfile) {
763 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
764 FILE_NOTIFY_CHANGE_ATTRIBUTES,
765 smb_fname->base_name);
767 smb_fname->st.st_ex_mode = unixmode;
768 return 0;
771 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
773 /* preserve the s bits */
774 mask |= (S_ISUID | S_ISGID);
776 /* preserve the t bit */
777 #ifdef S_ISVTX
778 mask |= S_ISVTX;
779 #endif
781 /* possibly preserve the x bits */
782 if (!MAP_ARCHIVE(conn))
783 mask |= S_IXUSR;
784 if (!MAP_SYSTEM(conn))
785 mask |= S_IXGRP;
786 if (!MAP_HIDDEN(conn))
787 mask |= S_IXOTH;
789 unixmode |= (smb_fname->st.st_ex_mode & mask);
791 /* if we previously had any r bits set then leave them alone */
792 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
793 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
794 unixmode |= tmp;
797 /* if we previously had any w bits set then leave them alone
798 whilst adding in the new w bits, if the new mode is not rdonly */
799 if (!IS_DOS_READONLY(dosmode)) {
800 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
803 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
804 if (ret == 0) {
805 if(!newfile || (lret != -1)) {
806 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
807 FILE_NOTIFY_CHANGE_ATTRIBUTES,
808 smb_fname->base_name);
810 smb_fname->st.st_ex_mode = unixmode;
811 return 0;
814 if((errno != EPERM) && (errno != EACCES))
815 return -1;
817 if(!lp_dos_filemode(SNUM(conn)))
818 return -1;
820 /* We want DOS semantics, ie allow non owner with write permission to change the
821 bits on a file. Just like file_ntimes below.
824 /* Check if we have write access. */
825 if (CAN_WRITE(conn)) {
827 * We need to open the file with write access whilst
828 * still in our current user context. This ensures we
829 * are not violating security in doing the fchmod.
830 * This file open does *not* break any oplocks we are
831 * holding. We need to review this.... may need to
832 * break batch oplocks open by others. JRA.
834 files_struct *fsp;
835 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
836 &fsp)))
837 return -1;
838 become_root();
839 ret = SMB_VFS_FCHMOD(fsp, unixmode);
840 unbecome_root();
841 close_file_fchmod(NULL, fsp);
842 if (!newfile) {
843 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
844 FILE_NOTIFY_CHANGE_ATTRIBUTES,
845 smb_fname->base_name);
847 if (ret == 0) {
848 smb_fname->st.st_ex_mode = unixmode;
852 return( ret );
855 /*******************************************************************
856 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
857 than POSIX.
858 *******************************************************************/
860 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
861 struct smb_file_time *ft)
863 int ret = -1;
865 errno = 0;
867 DEBUG(6, ("file_ntime: actime: %s",
868 time_to_asc(convert_timespec_to_time_t(ft->atime))));
869 DEBUG(6, ("file_ntime: modtime: %s",
870 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
871 DEBUG(6, ("file_ntime: ctime: %s",
872 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
873 DEBUG(6, ("file_ntime: createtime: %s",
874 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
876 /* Don't update the time on read-only shares */
877 /* We need this as set_filetime (which can be called on
878 close and other paths) can end up calling this function
879 without the NEED_WRITE protection. Found by :
880 Leo Weppelman <leo@wau.mis.ah.nl>
883 if (!CAN_WRITE(conn)) {
884 return 0;
887 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
888 return 0;
891 if((errno != EPERM) && (errno != EACCES)) {
892 return -1;
895 if(!lp_dos_filetimes(SNUM(conn))) {
896 return -1;
899 /* We have permission (given by the Samba admin) to
900 break POSIX semantics and allow a user to change
901 the time on a file they don't own but can write to
902 (as DOS does).
905 /* Check if we have write access. */
906 if (can_write_to_file(conn, smb_fname)) {
907 /* We are allowed to become root and change the filetime. */
908 become_root();
909 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
910 unbecome_root();
913 return ret;
916 /******************************************************************
917 Force a "sticky" write time on a pathname. This will always be
918 returned on all future write time queries and set on close.
919 ******************************************************************/
921 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
923 if (null_timespec(mtime)) {
924 return true;
927 if (!set_sticky_write_time(fileid, mtime)) {
928 return false;
931 return true;
934 /******************************************************************
935 Force a "sticky" write time on an fsp. This will always be
936 returned on all future write time queries and set on close.
937 ******************************************************************/
939 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
941 if (null_timespec(mtime)) {
942 return true;
945 fsp->write_time_forced = true;
946 TALLOC_FREE(fsp->update_write_time_event);
948 return set_sticky_write_time_path(fsp->file_id, mtime);
951 /******************************************************************
952 Set a create time EA.
953 ******************************************************************/
955 NTSTATUS set_create_timespec_ea(connection_struct *conn,
956 const struct smb_filename *psmb_fname,
957 struct timespec create_time)
959 NTSTATUS status;
960 struct smb_filename *smb_fname = NULL;
961 uint32_t dosmode;
962 int ret;
964 if (!lp_store_dos_attributes(SNUM(conn))) {
965 return NT_STATUS_OK;
968 status = create_synthetic_smb_fname(talloc_tos(),
969 psmb_fname->base_name,
970 NULL, &psmb_fname->st,
971 &smb_fname);
973 if (!NT_STATUS_IS_OK(status)) {
974 return status;
977 dosmode = dos_mode(conn, smb_fname);
979 smb_fname->st.st_ex_btime = create_time;
981 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
982 if (ret == -1) {
983 map_nt_error_from_unix(errno);
986 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
987 smb_fname_str_dbg(smb_fname)));
989 return NT_STATUS_OK;
992 /******************************************************************
993 Return a create time.
994 ******************************************************************/
996 struct timespec get_create_timespec(connection_struct *conn,
997 struct files_struct *fsp,
998 const struct smb_filename *smb_fname)
1000 return smb_fname->st.st_ex_btime;
1003 /******************************************************************
1004 Return a change time (may look at EA in future).
1005 ******************************************************************/
1007 struct timespec get_change_timespec(connection_struct *conn,
1008 struct files_struct *fsp,
1009 const struct smb_filename *smb_fname)
1011 return smb_fname->st.st_ex_mtime;