source: vendor/3.6.0/source3/smbd/dosmode.c

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

Samba Server: update vendor to 3.6.0

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