source: trunk/server/source3/smbd/dosmode.c@ 745

Last change on this file since 745 was 745, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated trunk to 3.6.0

File size: 32.6 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 dos mode handling functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) James Peach 2006
6
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.
11
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.
16
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/>.
19*/
20
21#include "includes.h"
22#include "system/filesys.h"
23#include "librpc/gen_ndr/ndr_xattr.h"
24#include "../libcli/security/security.h"
25#include "smbd/smbd.h"
26
27static uint32_t filter_mode_by_protocol(uint32_t mode)
28{
29 if (get_Protocol() <= PROTOCOL_LANMAN2) {
30 DEBUG(10,("filter_mode_by_protocol: "
31 "filtering result 0x%x to 0x%x\n",
32 (unsigned int)mode,
33 (unsigned int)(mode & 0x3f) ));
34 mode &= 0x3f;
35 }
36 return mode;
37}
38
39static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
40{
41#ifdef S_ISLNK
42#if LINKS_READ_ONLY
43 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
44 return FILE_ATTRIBUTE_READONLY;
45#endif
46#endif
47 return 0;
48}
49
50/****************************************************************************
51 Change a dos mode to a unix mode.
52 Base permission for files:
53 if creating file and inheriting (i.e. parent_dir != NULL)
54 apply read/write bits from parent directory.
55 else
56 everybody gets read bit set
57 dos readonly is represented in unix by removing everyone's write bit
58 dos archive is represented in unix by the user's execute bit
59 dos system is represented in unix by the group's execute bit
60 dos hidden is represented in unix by the other's execute bit
61 if !inheriting {
62 Then apply create mask,
63 then add force bits.
64 }
65 Base permission for directories:
66 dos directory is represented in unix by unix's dir bit and the exec bit
67 if !inheriting {
68 Then apply create mask,
69 then add force bits.
70 }
71****************************************************************************/
72
73mode_t unix_mode(connection_struct *conn, int dosmode,
74 const struct smb_filename *smb_fname,
75 const char *inherit_from_dir)
76{
77 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
78 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
79 * inheriting. */
80
81 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
82 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
83 }
84
85 if ((inherit_from_dir != NULL) && lp_inherit_perms(SNUM(conn))) {
86 struct smb_filename *smb_fname_parent = NULL;
87 NTSTATUS status;
88
89 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
90 smb_fname_str_dbg(smb_fname),
91 inherit_from_dir));
92
93 status = create_synthetic_smb_fname(talloc_tos(),
94 inherit_from_dir, NULL,
95 NULL, &smb_fname_parent);
96 if (!NT_STATUS_IS_OK(status)) {
97 DEBUG(1,("unix_mode(%s) failed, [dir %s]: %s\n",
98 smb_fname_str_dbg(smb_fname),
99 inherit_from_dir, nt_errstr(status)));
100 return(0);
101 }
102
103 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
104 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
105 smb_fname_str_dbg(smb_fname),
106 inherit_from_dir, strerror(errno)));
107 TALLOC_FREE(smb_fname_parent);
108 return(0); /* *** shouldn't happen! *** */
109 }
110
111 /* Save for later - but explicitly remove setuid bit for safety. */
112 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
113 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
114 smb_fname_str_dbg(smb_fname), (int)dir_mode));
115 /* Clear "result" */
116 result = 0;
117 TALLOC_FREE(smb_fname_parent);
118 }
119
120 if (IS_DOS_DIR(dosmode)) {
121 /* We never make directories read only for the owner as under DOS a user
122 can always create a file in a read-only directory. */
123 result |= (S_IFDIR | S_IWUSR);
124
125 if (dir_mode) {
126 /* Inherit mode of parent directory. */
127 result |= dir_mode;
128 } else {
129 /* Provisionally add all 'x' bits */
130 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
131
132 /* Apply directory mask */
133 result &= lp_dir_mask(SNUM(conn));
134 /* Add in force bits */
135 result |= lp_force_dir_mode(SNUM(conn));
136 }
137 } else {
138 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
139 result |= S_IXUSR;
140
141 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
142 result |= S_IXGRP;
143
144 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
145 result |= S_IXOTH;
146
147 if (dir_mode) {
148 /* Inherit 666 component of parent directory mode */
149 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
150 } else {
151 /* Apply mode mask */
152 result &= lp_create_mask(SNUM(conn));
153 /* Add in force bits */
154 result |= lp_force_create_mode(SNUM(conn));
155 }
156 }
157
158 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
159 (int)result));
160 return(result);
161}
162
163/****************************************************************************
164 Change a unix mode to a dos mode.
165****************************************************************************/
166
167static uint32 dos_mode_from_sbuf(connection_struct *conn,
168 const struct smb_filename *smb_fname)
169{
170 int result = 0;
171 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
172
173 if (ro_opts == MAP_READONLY_YES) {
174 /* Original Samba method - map inverse of user "w" bit. */
175#ifndef __OS2__
176 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
177#else
178 if (os2_isattribute(smb_fname->base_name, FILE_ATTRIBUTE_READONLY) == 0) {
179#endif
180 result |= FILE_ATTRIBUTE_READONLY;
181 }
182 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
183 /* Check actual permissions for read-only. */
184 if (!can_write_to_file(conn, smb_fname)) {
185 result |= FILE_ATTRIBUTE_READONLY;
186 }
187 } /* Else never set the readonly bit. */
188
189#ifndef __OS2__
190 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
191#else
192 if (os2_isattribute(smb_fname->base_name, FILE_ATTRIBUTE_ARCHIVE) == 0)
193#endif
194 result |= FILE_ATTRIBUTE_ARCHIVE;
195
196#ifndef __OS2__
197 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
198#else
199 if (os2_isattribute(smb_fname->base_name, FILE_ATTRIBUTE_SYSTEM) == 0)
200#endif
201 result |= FILE_ATTRIBUTE_SYSTEM;
202
203#ifndef __OS2__
204 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
205#else
206 if (os2_isattribute(smb_fname->base_name, FILE_ATTRIBUTE_HIDDEN) == 0)
207#endif
208 result |= FILE_ATTRIBUTE_HIDDEN;
209
210 if (S_ISDIR(smb_fname->st.st_ex_mode))
211 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
212
213 result |= set_link_read_only_flag(&smb_fname->st);
214
215 DEBUG(8,("dos_mode_from_sbuf returning "));
216
217 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
218 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
219 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
220 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
221 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
222
223 DEBUG(8,("\n"));
224 return result;
225}
226
227/****************************************************************************
228 Get DOS attributes from an EA.
229 This can also pull the create time into the stat struct inside smb_fname.
230****************************************************************************/
231
232static bool get_ea_dos_attribute(connection_struct *conn,
233 struct smb_filename *smb_fname,
234 uint32 *pattr)
235{
236 struct xattr_DOSATTRIB dosattrib;
237 enum ndr_err_code ndr_err;
238 DATA_BLOB blob;
239 ssize_t sizeret;
240 fstring attrstr;
241 uint32_t dosattr;
242
243 if (!lp_store_dos_attributes(SNUM(conn))) {
244 return False;
245 }
246
247 /* Don't reset pattr to zero as we may already have filename-based attributes we
248 need to preserve. */
249
250 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
251 SAMBA_XATTR_DOS_ATTRIB, attrstr,
252 sizeof(attrstr));
253 if (sizeret == -1) {
254 if (errno == ENOSYS
255#if defined(ENOTSUP)
256 || errno == ENOTSUP) {
257#else
258 ) {
259#endif
260#ifdef __OS2__ //on os2 no error in case of ./.. and errno 2
261 if ((strncmp(smb_fname->base_name, "./..", 4) !=0) || (errno !=2))
262#endif
263 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
264 "from EA on file %s: Error = %s\n",
265 smb_fname_str_dbg(smb_fname),
266 strerror(errno)));
267 set_store_dos_attributes(SNUM(conn), False);
268 }
269 return False;
270 }
271
272 blob.data = (uint8_t *)attrstr;
273 blob.length = sizeret;
274
275 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
276 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
277
278 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
279 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
280 "from EA on file %s: Error = %s\n",
281 smb_fname_str_dbg(smb_fname),
282 ndr_errstr(ndr_err)));
283 return false;
284 }
285
286 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
287 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
288
289 switch (dosattrib.version) {
290 case 0xFFFF:
291 dosattr = dosattrib.info.compatinfoFFFF.attrib;
292 break;
293 case 1:
294 dosattr = dosattrib.info.info1.attrib;
295 if (!null_nttime(dosattrib.info.info1.create_time)) {
296 struct timespec create_time =
297 nt_time_to_unix_timespec(
298 &dosattrib.info.info1.create_time);
299
300 update_stat_ex_create_time(&smb_fname->st,
301 create_time);
302
303 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
304 "set btime %s\n",
305 smb_fname_str_dbg(smb_fname),
306 time_to_asc(convert_timespec_to_time_t(
307 create_time)) ));
308 }
309 break;
310 case 2:
311 dosattr = dosattrib.info.oldinfo2.attrib;
312 /* Don't know what flags to check for this case. */
313 break;
314 case 3:
315 dosattr = dosattrib.info.info3.attrib;
316 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
317 !null_nttime(dosattrib.info.info3.create_time)) {
318 struct timespec create_time =
319 nt_time_to_unix_timespec(
320 &dosattrib.info.info3.create_time);
321
322 update_stat_ex_create_time(&smb_fname->st,
323 create_time);
324
325 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
326 "set btime %s\n",
327 smb_fname_str_dbg(smb_fname),
328 time_to_asc(convert_timespec_to_time_t(
329 create_time)) ));
330 }
331 break;
332 default:
333 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
334 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
335 attrstr));
336 return false;
337 }
338
339 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
340 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
341 }
342 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
343 *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
344
345 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
346
347 if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
348 if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
349 if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
350 if (dosattr & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
351 if (dosattr & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
352
353 DEBUG(8,("\n"));
354
355 return True;
356}
357
358/****************************************************************************
359 Set DOS attributes in an EA.
360 Also sets the create time.
361****************************************************************************/
362
363static bool set_ea_dos_attribute(connection_struct *conn,
364 struct smb_filename *smb_fname,
365 uint32 dosmode)
366{
367 struct xattr_DOSATTRIB dosattrib;
368 enum ndr_err_code ndr_err;
369 DATA_BLOB blob;
370
371 if (!lp_store_dos_attributes(SNUM(conn))) {
372 return False;
373 }
374
375 ZERO_STRUCT(dosattrib);
376 ZERO_STRUCT(blob);
377
378 dosattrib.version = 3;
379 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
380 XATTR_DOSINFO_CREATE_TIME;
381 dosattrib.info.info3.attrib = dosmode;
382 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
383 smb_fname->st.st_ex_btime);
384
385 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
386 (unsigned int)dosmode,
387 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
388 smb_fname_str_dbg(smb_fname) ));
389
390 ndr_err = ndr_push_struct_blob(
391 &blob, talloc_tos(), &dosattrib,
392 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
393
394 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
395 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
396 ndr_errstr(ndr_err)));
397 return false;
398 }
399
400 if (blob.data == NULL || blob.length == 0) {
401 return false;
402 }
403
404 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
405 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
406 0) == -1) {
407 bool ret = false;
408 files_struct *fsp = NULL;
409
410 if((errno != EPERM) && (errno != EACCES)) {
411 if (errno == ENOSYS
412#if defined(ENOTSUP)
413 || errno == ENOTSUP) {
414#else
415 ) {
416#endif
417 DEBUG(1,("set_ea_dos_attributes: Cannot set "
418 "attribute EA on file %s: Error = %s\n",
419 smb_fname_str_dbg(smb_fname),
420 strerror(errno) ));
421 set_store_dos_attributes(SNUM(conn), False);
422 }
423 return false;
424 }
425
426 /* We want DOS semantics, ie allow non owner with write permission to change the
427 bits on a file. Just like file_ntimes below.
428 */
429
430 /* Check if we have write access. */
431 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
432 return false;
433
434 /*
435 * We need to open the file with write access whilst
436 * still in our current user context. This ensures we
437 * are not violating security in doing the setxattr.
438 */
439
440 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
441 &fsp)))
442 return false;
443 become_root();
444 if (SMB_VFS_FSETXATTR(fsp,
445 SAMBA_XATTR_DOS_ATTRIB, blob.data,
446 blob.length, 0) == 0) {
447 ret = true;
448 }
449 unbecome_root();
450 close_file(NULL, fsp, NORMAL_CLOSE);
451 return ret;
452 }
453 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
454 (unsigned int)dosmode,
455 smb_fname_str_dbg(smb_fname)));
456 return true;
457}
458
459/****************************************************************************
460 Change a unix mode to a dos mode for an ms dfs link.
461****************************************************************************/
462
463uint32 dos_mode_msdfs(connection_struct *conn,
464 const struct smb_filename *smb_fname)
465{
466 uint32 result = 0;
467
468 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
469
470 if (!VALID_STAT(smb_fname->st)) {
471 return 0;
472 }
473
474 /* First do any modifications that depend on the path name. */
475 /* hide files with a name starting with a . */
476 if (lp_hide_dot_files(SNUM(conn))) {
477 const char *p = strrchr_m(smb_fname->base_name, '/');
478 if (p) {
479 p++;
480 } else {
481 p = smb_fname->base_name;
482 }
483
484 /* Only . and .. are not hidden. */
485 if (p[0] == '.' && !((p[1] == '\0') ||
486 (p[1] == '.' && p[2] == '\0'))) {
487 result |= FILE_ATTRIBUTE_HIDDEN;
488 }
489 }
490
491 result |= dos_mode_from_sbuf(conn, smb_fname);
492
493 /* Optimization : Only call is_hidden_path if it's not already
494 hidden. */
495 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
496 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
497 result |= FILE_ATTRIBUTE_HIDDEN;
498 }
499
500 if (result == 0) {
501 result = FILE_ATTRIBUTE_NORMAL;
502 }
503
504 result = filter_mode_by_protocol(result);
505
506 DEBUG(8,("dos_mode_msdfs returning "));
507
508 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
509 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
510 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
511 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
512 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
513 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
514
515 DEBUG(8,("\n"));
516
517 return(result);
518}
519
520#ifdef HAVE_STAT_DOS_FLAGS
521/****************************************************************************
522 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
523****************************************************************************/
524
525int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
526{
527 uint32_t dos_stat_flags = 0;
528
529 if (dosmode & FILE_ATTRIBUTE_ARCHIVE)
530 dos_stat_flags |= UF_DOS_ARCHIVE;
531 if (dosmode & FILE_ATTRIBUTE_HIDDEN)
532 dos_stat_flags |= UF_DOS_HIDDEN;
533 if (dosmode & FILE_ATTRIBUTE_READONLY)
534 dos_stat_flags |= UF_DOS_RO;
535 if (dosmode & FILE_ATTRIBUTE_SYSTEM)
536 dos_stat_flags |= UF_DOS_SYSTEM;
537 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
538 dos_stat_flags |= UF_DOS_NOINDEX;
539
540 return dos_stat_flags;
541}
542
543/****************************************************************************
544 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
545****************************************************************************/
546
547static bool get_stat_dos_flags(connection_struct *conn,
548 const struct smb_filename *smb_fname,
549 uint32_t *dosmode)
550{
551 SMB_ASSERT(VALID_STAT(smb_fname->st));
552 SMB_ASSERT(dosmode);
553
554 if (!lp_store_dos_attributes(SNUM(conn))) {
555 return false;
556 }
557
558 DEBUG(5, ("Getting stat dos attributes for %s.\n",
559 smb_fname_str_dbg(smb_fname)));
560
561 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
562 *dosmode |= FILE_ATTRIBUTE_ARCHIVE;
563 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
564 *dosmode |= FILE_ATTRIBUTE_HIDDEN;
565 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
566 *dosmode |= FILE_ATTRIBUTE_READONLY;
567 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
568 *dosmode |= FILE_ATTRIBUTE_SYSTEM;
569 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
570 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
571 if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
572 *dosmode |= FILE_ATTRIBUTE_SPARSE;
573 if (S_ISDIR(smb_fname->st.st_ex_mode))
574 *dosmode |= FILE_ATTRIBUTE_DIRECTORY;
575
576 *dosmode |= set_link_read_only_flag(&smb_fname->st);
577
578 return true;
579}
580
581/****************************************************************************
582 Sets DOS attributes, stored in st_ex_flags of the inode.
583****************************************************************************/
584
585static bool set_stat_dos_flags(connection_struct *conn,
586 const struct smb_filename *smb_fname,
587 uint32_t dosmode,
588 bool *attributes_changed)
589{
590 uint32_t new_flags = 0;
591 int error = 0;
592
593 SMB_ASSERT(VALID_STAT(smb_fname->st));
594 SMB_ASSERT(attributes_changed);
595
596 *attributes_changed = false;
597
598 if (!lp_store_dos_attributes(SNUM(conn))) {
599 return false;
600 }
601
602 DEBUG(5, ("Setting stat dos attributes for %s.\n",
603 smb_fname_str_dbg(smb_fname)));
604
605 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
606 dos_attributes_to_stat_dos_flags(dosmode);
607
608 /* Return early if no flags changed. */
609 if (new_flags == smb_fname->st.st_ex_flags)
610 return true;
611
612 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
613 smb_fname->st.st_ex_flags));
614
615 /* Set new flags with chflags. */
616 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
617 if (error) {
618 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
619 "file %s! errno=%d\n", new_flags,
620 smb_fname_str_dbg(smb_fname), errno));
621 return false;
622 }
623
624 *attributes_changed = true;
625 return true;
626}
627#endif /* HAVE_STAT_DOS_FLAGS */
628
629/****************************************************************************
630 Change a unix mode to a dos mode.
631 May also read the create timespec into the stat struct in smb_fname
632 if "store dos attributes" is true.
633****************************************************************************/
634
635uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
636{
637 uint32 result = 0;
638 bool offline, used_stat_dos_flags = false;
639
640 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
641
642 if (!VALID_STAT(smb_fname->st)) {
643 return 0;
644 }
645
646 /* First do any modifications that depend on the path name. */
647 /* hide files with a name starting with a . */
648 if (lp_hide_dot_files(SNUM(conn))) {
649 const char *p = strrchr_m(smb_fname->base_name,'/');
650 if (p) {
651 p++;
652 } else {
653 p = smb_fname->base_name;
654 }
655
656 /* Only . and .. are not hidden. */
657 if (p[0] == '.' && !((p[1] == '\0') ||
658 (p[1] == '.' && p[2] == '\0'))) {
659 result |= FILE_ATTRIBUTE_HIDDEN;
660 }
661 }
662
663#ifdef HAVE_STAT_DOS_FLAGS
664 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
665#endif
666 if (!used_stat_dos_flags) {
667 /* Get the DOS attributes from an EA by preference. */
668 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
669 result |= dos_mode_from_sbuf(conn, smb_fname);
670 }
671 }
672
673 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
674 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
675 result |= FILE_ATTRIBUTE_OFFLINE;
676 }
677
678 /* Optimization : Only call is_hidden_path if it's not already
679 hidden. */
680 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
681 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
682 result |= FILE_ATTRIBUTE_HIDDEN;
683 }
684
685 if (result == 0) {
686 result = FILE_ATTRIBUTE_NORMAL;
687 }
688
689 result = filter_mode_by_protocol(result);
690
691 DEBUG(8,("dos_mode returning "));
692
693 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
694 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
695 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
696 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
697 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
698 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
699
700 DEBUG(8,("\n"));
701
702 return(result);
703}
704
705/*******************************************************************
706 chmod a file - but preserve some bits.
707 If "store dos attributes" is also set it will store the create time
708 from the stat struct in smb_fname (in NTTIME format) in the EA
709 attribute also.
710********************************************************************/
711
712int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
713 uint32 dosmode, const char *parent_dir, bool newfile)
714{
715 int mask=0;
716 mode_t tmp;
717 mode_t unixmode;
718 int ret = -1, lret = -1;
719 uint32_t old_mode;
720 struct timespec new_create_timespec;
721
722 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
723 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
724
725 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
726 dosmode, smb_fname_str_dbg(smb_fname)));
727
728 unixmode = smb_fname->st.st_ex_mode;
729
730 get_acl_group_bits(conn, smb_fname->base_name,
731 &smb_fname->st.st_ex_mode);
732
733 if (S_ISDIR(smb_fname->st.st_ex_mode))
734 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
735 else
736 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
737
738 new_create_timespec = smb_fname->st.st_ex_btime;
739
740 old_mode = dos_mode(conn, smb_fname);
741
742 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
743 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
744 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
745 if (lret == -1) {
746 DEBUG(0, ("set_dos_mode: client has asked to "
747 "set FILE_ATTRIBUTE_OFFLINE to "
748 "%s/%s but there was an error while "
749 "setting it or it is not "
750 "supported.\n", parent_dir,
751 smb_fname_str_dbg(smb_fname)));
752 }
753 }
754 }
755
756 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
757 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
758
759 smb_fname->st.st_ex_btime = new_create_timespec;
760
761#ifdef HAVE_STAT_DOS_FLAGS
762 {
763 bool attributes_changed;
764
765 if (set_stat_dos_flags(conn, smb_fname, dosmode,
766 &attributes_changed))
767 {
768 if (!newfile && attributes_changed) {
769 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
770 FILE_NOTIFY_CHANGE_ATTRIBUTES,
771 smb_fname->base_name);
772 }
773 smb_fname->st.st_ex_mode = unixmode;
774 return 0;
775 }
776 }
777#endif
778 /* Store the DOS attributes in an EA by preference. */
779 if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
780 if (!newfile) {
781 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
782 FILE_NOTIFY_CHANGE_ATTRIBUTES,
783 smb_fname->base_name);
784 }
785 smb_fname->st.st_ex_mode = unixmode;
786 return 0;
787 }
788
789 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
790
791 /* preserve the s bits */
792 mask |= (S_ISUID | S_ISGID);
793
794 /* preserve the t bit */
795#ifdef S_ISVTX
796 mask |= S_ISVTX;
797#endif
798
799 /* possibly preserve the x bits */
800 if (!MAP_ARCHIVE(conn))
801 mask |= S_IXUSR;
802 if (!MAP_SYSTEM(conn))
803 mask |= S_IXGRP;
804 if (!MAP_HIDDEN(conn))
805 mask |= S_IXOTH;
806
807 unixmode |= (smb_fname->st.st_ex_mode & mask);
808
809 /* if we previously had any r bits set then leave them alone */
810 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
811 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
812 unixmode |= tmp;
813 }
814
815 /* if we previously had any w bits set then leave them alone
816 whilst adding in the new w bits, if the new mode is not rdonly */
817 if (!IS_DOS_READONLY(dosmode)) {
818 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
819 }
820
821 /*
822 * From the chmod 2 man page:
823 *
824 * "If the calling process is not privileged, and the group of the file
825 * does not match the effective group ID of the process or one of its
826 * supplementary group IDs, the S_ISGID bit will be turned off, but
827 * this will not cause an error to be returned."
828 *
829 * Simply refuse to do the chmod in this case.
830 */
831
832 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
833 geteuid() != sec_initial_uid() &&
834 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
835 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
836 "set for directory %s\n",
837 smb_fname_str_dbg(smb_fname)));
838 errno = EPERM;
839 return -1;
840 }
841
842 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
843 if (ret == 0) {
844 if(!newfile || (lret != -1)) {
845 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
846 FILE_NOTIFY_CHANGE_ATTRIBUTES,
847 smb_fname->base_name);
848 }
849 smb_fname->st.st_ex_mode = unixmode;
850 return 0;
851 }
852
853 if((errno != EPERM) && (errno != EACCES))
854 return -1;
855
856 if(!lp_dos_filemode(SNUM(conn)))
857 return -1;
858
859 /* We want DOS semantics, ie allow non owner with write permission to change the
860 bits on a file. Just like file_ntimes below.
861 */
862
863 /* Check if we have write access. */
864 if (CAN_WRITE(conn)) {
865 /*
866 * We need to open the file with write access whilst
867 * still in our current user context. This ensures we
868 * are not violating security in doing the fchmod.
869 */
870 files_struct *fsp;
871 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
872 &fsp)))
873 return -1;
874 become_root();
875 ret = SMB_VFS_FCHMOD(fsp, unixmode);
876 unbecome_root();
877 close_file(NULL, fsp, NORMAL_CLOSE);
878 if (!newfile) {
879 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
880 FILE_NOTIFY_CHANGE_ATTRIBUTES,
881 smb_fname->base_name);
882 }
883 if (ret == 0) {
884 smb_fname->st.st_ex_mode = unixmode;
885 }
886 }
887
888 return( ret );
889}
890
891
892NTSTATUS file_set_sparse(connection_struct *conn,
893 files_struct *fsp,
894 bool sparse)
895{
896 uint32_t old_dosmode;
897 uint32_t new_dosmode;
898 NTSTATUS status;
899
900 if (!CAN_WRITE(conn)) {
901 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
902 "on readonly share[%s]\n",
903 smb_fname_str_dbg(fsp->fsp_name),
904 sparse,
905 lp_servicename(SNUM(conn))));
906 return NT_STATUS_MEDIA_WRITE_PROTECTED;
907 }
908
909 if (!(fsp->access_mask & FILE_WRITE_DATA) &&
910 !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
911 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
912 "access_mask[0x%08X] - access denied\n",
913 smb_fname_str_dbg(fsp->fsp_name),
914 sparse,
915 fsp->access_mask));
916 return NT_STATUS_ACCESS_DENIED;
917 }
918
919 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
920 sparse, smb_fname_str_dbg(fsp->fsp_name)));
921
922 if (!lp_store_dos_attributes(SNUM(conn))) {
923 return NT_STATUS_INVALID_DEVICE_REQUEST;
924 }
925
926 status = vfs_stat_fsp(fsp);
927 if (!NT_STATUS_IS_OK(status)) {
928 return status;
929 }
930
931 old_dosmode = dos_mode(conn, fsp->fsp_name);
932
933 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
934 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
935 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
936 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
937 } else {
938 return NT_STATUS_OK;
939 }
940
941 /* Store the DOS attributes in an EA. */
942 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
943 new_dosmode)) {
944 if (errno == 0) {
945 errno = EIO;
946 }
947 return map_nt_error_from_unix(errno);
948 }
949
950 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
951 FILE_NOTIFY_CHANGE_ATTRIBUTES,
952 fsp->fsp_name->base_name);
953
954 fsp->is_sparse = sparse;
955
956 return NT_STATUS_OK;
957}
958
959/*******************************************************************
960 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
961 than POSIX.
962*******************************************************************/
963
964int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
965 struct smb_file_time *ft)
966{
967 int ret = -1;
968
969 errno = 0;
970
971 DEBUG(6, ("file_ntime: actime: %s",
972 time_to_asc(convert_timespec_to_time_t(ft->atime))));
973 DEBUG(6, ("file_ntime: modtime: %s",
974 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
975 DEBUG(6, ("file_ntime: ctime: %s",
976 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
977 DEBUG(6, ("file_ntime: createtime: %s",
978 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
979
980 /* Don't update the time on read-only shares */
981 /* We need this as set_filetime (which can be called on
982 close and other paths) can end up calling this function
983 without the NEED_WRITE protection. Found by :
984 Leo Weppelman <leo@wau.mis.ah.nl>
985 */
986
987 if (!CAN_WRITE(conn)) {
988 return 0;
989 }
990
991 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
992 return 0;
993 }
994
995 if((errno != EPERM) && (errno != EACCES)) {
996 return -1;
997 }
998
999 if(!lp_dos_filetimes(SNUM(conn))) {
1000 return -1;
1001 }
1002
1003 /* We have permission (given by the Samba admin) to
1004 break POSIX semantics and allow a user to change
1005 the time on a file they don't own but can write to
1006 (as DOS does).
1007 */
1008
1009 /* Check if we have write access. */
1010 if (can_write_to_file(conn, smb_fname)) {
1011 /* We are allowed to become root and change the filetime. */
1012 become_root();
1013 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1014 unbecome_root();
1015 }
1016
1017 return ret;
1018}
1019
1020/******************************************************************
1021 Force a "sticky" write time on a pathname. This will always be
1022 returned on all future write time queries and set on close.
1023******************************************************************/
1024
1025bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1026{
1027 if (null_timespec(mtime)) {
1028 return true;
1029 }
1030
1031 if (!set_sticky_write_time(fileid, mtime)) {
1032 return false;
1033 }
1034
1035 return true;
1036}
1037
1038/******************************************************************
1039 Force a "sticky" write time on an fsp. This will always be
1040 returned on all future write time queries and set on close.
1041******************************************************************/
1042
1043bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1044{
1045 if (null_timespec(mtime)) {
1046 return true;
1047 }
1048
1049 fsp->write_time_forced = true;
1050 TALLOC_FREE(fsp->update_write_time_event);
1051
1052 return set_sticky_write_time_path(fsp->file_id, mtime);
1053}
1054
1055/******************************************************************
1056 Set a create time EA.
1057******************************************************************/
1058
1059NTSTATUS set_create_timespec_ea(connection_struct *conn,
1060 const struct smb_filename *psmb_fname,
1061 struct timespec create_time)
1062{
1063 NTSTATUS status;
1064 struct smb_filename *smb_fname = NULL;
1065 uint32_t dosmode;
1066 int ret;
1067
1068 if (!lp_store_dos_attributes(SNUM(conn))) {
1069 return NT_STATUS_OK;
1070 }
1071
1072 status = create_synthetic_smb_fname(talloc_tos(),
1073 psmb_fname->base_name,
1074 NULL, &psmb_fname->st,
1075 &smb_fname);
1076
1077 if (!NT_STATUS_IS_OK(status)) {
1078 return status;
1079 }
1080
1081 dosmode = dos_mode(conn, smb_fname);
1082
1083 smb_fname->st.st_ex_btime = create_time;
1084
1085 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1086 if (ret == -1) {
1087 map_nt_error_from_unix(errno);
1088 }
1089
1090 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1091 smb_fname_str_dbg(smb_fname)));
1092
1093 return NT_STATUS_OK;
1094}
1095
1096/******************************************************************
1097 Return a create time.
1098******************************************************************/
1099
1100struct timespec get_create_timespec(connection_struct *conn,
1101 struct files_struct *fsp,
1102 const struct smb_filename *smb_fname)
1103{
1104 return smb_fname->st.st_ex_btime;
1105}
1106
1107/******************************************************************
1108 Return a change time (may look at EA in future).
1109******************************************************************/
1110
1111struct timespec get_change_timespec(connection_struct *conn,
1112 struct files_struct *fsp,
1113 const struct smb_filename *smb_fname)
1114{
1115 return smb_fname->st.st_ex_mtime;
1116}
Note: See TracBrowser for help on using the repository browser.