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

Last change on this file since 888 was 862, checked in by Silvan Scherrer, 11 years ago

Samba Server: update trunk to 3.6.23

File size: 31.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 /*
507 * Add in that it is a reparse point
508 */
509 result |= FILE_ATTRIBUTE_REPARSE_POINT;
510
511 DEBUG(8,("dos_mode_msdfs returning "));
512
513 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
514 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
515 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
516 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
517 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
518 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
519
520 DEBUG(8,("\n"));
521
522 return(result);
523}
524
525#ifdef HAVE_STAT_DOS_FLAGS
526/****************************************************************************
527 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
528****************************************************************************/
529
530int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
531{
532 uint32_t dos_stat_flags = 0;
533
534 if (dosmode & FILE_ATTRIBUTE_ARCHIVE)
535 dos_stat_flags |= UF_DOS_ARCHIVE;
536 if (dosmode & FILE_ATTRIBUTE_HIDDEN)
537 dos_stat_flags |= UF_DOS_HIDDEN;
538 if (dosmode & FILE_ATTRIBUTE_READONLY)
539 dos_stat_flags |= UF_DOS_RO;
540 if (dosmode & FILE_ATTRIBUTE_SYSTEM)
541 dos_stat_flags |= UF_DOS_SYSTEM;
542 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
543 dos_stat_flags |= UF_DOS_NOINDEX;
544
545 return dos_stat_flags;
546}
547
548/****************************************************************************
549 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
550****************************************************************************/
551
552static bool get_stat_dos_flags(connection_struct *conn,
553 const struct smb_filename *smb_fname,
554 uint32_t *dosmode)
555{
556 SMB_ASSERT(VALID_STAT(smb_fname->st));
557 SMB_ASSERT(dosmode);
558
559 if (!lp_store_dos_attributes(SNUM(conn))) {
560 return false;
561 }
562
563 DEBUG(5, ("Getting stat dos attributes for %s.\n",
564 smb_fname_str_dbg(smb_fname)));
565
566 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
567 *dosmode |= FILE_ATTRIBUTE_ARCHIVE;
568 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
569 *dosmode |= FILE_ATTRIBUTE_HIDDEN;
570 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
571 *dosmode |= FILE_ATTRIBUTE_READONLY;
572 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
573 *dosmode |= FILE_ATTRIBUTE_SYSTEM;
574 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
575 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
576 if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
577 *dosmode |= FILE_ATTRIBUTE_SPARSE;
578 if (S_ISDIR(smb_fname->st.st_ex_mode))
579 *dosmode |= FILE_ATTRIBUTE_DIRECTORY;
580
581 *dosmode |= set_link_read_only_flag(&smb_fname->st);
582
583 return true;
584}
585
586/****************************************************************************
587 Sets DOS attributes, stored in st_ex_flags of the inode.
588****************************************************************************/
589
590static bool set_stat_dos_flags(connection_struct *conn,
591 const struct smb_filename *smb_fname,
592 uint32_t dosmode,
593 bool *attributes_changed)
594{
595 uint32_t new_flags = 0;
596 int error = 0;
597
598 SMB_ASSERT(VALID_STAT(smb_fname->st));
599 SMB_ASSERT(attributes_changed);
600
601 *attributes_changed = false;
602
603 if (!lp_store_dos_attributes(SNUM(conn))) {
604 return false;
605 }
606
607 DEBUG(5, ("Setting stat dos attributes for %s.\n",
608 smb_fname_str_dbg(smb_fname)));
609
610 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
611 dos_attributes_to_stat_dos_flags(dosmode);
612
613 /* Return early if no flags changed. */
614 if (new_flags == smb_fname->st.st_ex_flags)
615 return true;
616
617 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
618 smb_fname->st.st_ex_flags));
619
620 /* Set new flags with chflags. */
621 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
622 if (error) {
623 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
624 "file %s! errno=%d\n", new_flags,
625 smb_fname_str_dbg(smb_fname), errno));
626 return false;
627 }
628
629 *attributes_changed = true;
630 return true;
631}
632#endif /* HAVE_STAT_DOS_FLAGS */
633
634/****************************************************************************
635 Change a unix mode to a dos mode.
636 May also read the create timespec into the stat struct in smb_fname
637 if "store dos attributes" is true.
638****************************************************************************/
639
640uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
641{
642 uint32 result = 0;
643 bool offline, used_stat_dos_flags = false;
644
645 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
646
647 if (!VALID_STAT(smb_fname->st)) {
648 return 0;
649 }
650
651 /* First do any modifications that depend on the path name. */
652 /* hide files with a name starting with a . */
653 if (lp_hide_dot_files(SNUM(conn))) {
654 const char *p = strrchr_m(smb_fname->base_name,'/');
655 if (p) {
656 p++;
657 } else {
658 p = smb_fname->base_name;
659 }
660
661 /* Only . and .. are not hidden. */
662 if (p[0] == '.' && !((p[1] == '\0') ||
663 (p[1] == '.' && p[2] == '\0'))) {
664 result |= FILE_ATTRIBUTE_HIDDEN;
665 }
666 }
667
668#ifdef HAVE_STAT_DOS_FLAGS
669 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
670#endif
671 if (!used_stat_dos_flags) {
672 /* Get the DOS attributes from an EA by preference. */
673 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
674 result |= dos_mode_from_sbuf(conn, smb_fname);
675 }
676 }
677
678 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
679 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
680 result |= FILE_ATTRIBUTE_OFFLINE;
681 }
682
683 /* Optimization : Only call is_hidden_path if it's not already
684 hidden. */
685 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
686 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
687 result |= FILE_ATTRIBUTE_HIDDEN;
688 }
689
690 if (result == 0) {
691 result = FILE_ATTRIBUTE_NORMAL;
692 }
693
694 result = filter_mode_by_protocol(result);
695
696 DEBUG(8,("dos_mode returning "));
697
698 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
699 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
700 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
701 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
702 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
703 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
704
705 DEBUG(8,("\n"));
706
707 return(result);
708}
709
710/*******************************************************************
711 chmod a file - but preserve some bits.
712 If "store dos attributes" is also set it will store the create time
713 from the stat struct in smb_fname (in NTTIME format) in the EA
714 attribute also.
715********************************************************************/
716
717int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
718 uint32 dosmode, const char *parent_dir, bool newfile)
719{
720 int mask=0;
721 mode_t tmp;
722 mode_t unixmode;
723 int ret = -1, lret = -1;
724 uint32_t old_mode;
725 struct timespec new_create_timespec;
726
727 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
728 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
729
730 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
731 dosmode, smb_fname_str_dbg(smb_fname)));
732
733 unixmode = smb_fname->st.st_ex_mode;
734
735 get_acl_group_bits(conn, smb_fname->base_name,
736 &smb_fname->st.st_ex_mode);
737
738 if (S_ISDIR(smb_fname->st.st_ex_mode))
739 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
740 else
741 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
742
743 new_create_timespec = smb_fname->st.st_ex_btime;
744
745 old_mode = dos_mode(conn, smb_fname);
746
747 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
748 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
749 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
750 if (lret == -1) {
751 DEBUG(0, ("set_dos_mode: client has asked to "
752 "set FILE_ATTRIBUTE_OFFLINE to "
753 "%s/%s but there was an error while "
754 "setting it or it is not "
755 "supported.\n", parent_dir,
756 smb_fname_str_dbg(smb_fname)));
757 }
758 }
759 }
760
761 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
762 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
763
764 smb_fname->st.st_ex_btime = new_create_timespec;
765
766#ifdef HAVE_STAT_DOS_FLAGS
767 {
768 bool attributes_changed;
769
770 if (set_stat_dos_flags(conn, smb_fname, dosmode,
771 &attributes_changed))
772 {
773 if (!newfile && attributes_changed) {
774 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
775 FILE_NOTIFY_CHANGE_ATTRIBUTES,
776 smb_fname->base_name);
777 }
778 smb_fname->st.st_ex_mode = unixmode;
779 return 0;
780 }
781 }
782#endif
783 /* Store the DOS attributes in an EA by preference. */
784 if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
785 if (!newfile) {
786 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
787 FILE_NOTIFY_CHANGE_ATTRIBUTES,
788 smb_fname->base_name);
789 }
790 smb_fname->st.st_ex_mode = unixmode;
791 return 0;
792 }
793
794 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
795
796 /* preserve the s bits */
797 mask |= (S_ISUID | S_ISGID);
798
799 /* preserve the t bit */
800#ifdef S_ISVTX
801 mask |= S_ISVTX;
802#endif
803
804 /* possibly preserve the x bits */
805 if (!MAP_ARCHIVE(conn))
806 mask |= S_IXUSR;
807 if (!MAP_SYSTEM(conn))
808 mask |= S_IXGRP;
809 if (!MAP_HIDDEN(conn))
810 mask |= S_IXOTH;
811
812 unixmode |= (smb_fname->st.st_ex_mode & mask);
813
814 /* if we previously had any r bits set then leave them alone */
815 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
816 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
817 unixmode |= tmp;
818 }
819
820 /* if we previously had any w bits set then leave them alone
821 whilst adding in the new w bits, if the new mode is not rdonly */
822 if (!IS_DOS_READONLY(dosmode)) {
823 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
824 }
825
826 /*
827 * From the chmod 2 man page:
828 *
829 * "If the calling process is not privileged, and the group of the file
830 * does not match the effective group ID of the process or one of its
831 * supplementary group IDs, the S_ISGID bit will be turned off, but
832 * this will not cause an error to be returned."
833 *
834 * Simply refuse to do the chmod in this case.
835 */
836
837 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
838 geteuid() != sec_initial_uid() &&
839 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
840 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
841 "set for directory %s\n",
842 smb_fname_str_dbg(smb_fname)));
843 errno = EPERM;
844 return -1;
845 }
846
847 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
848 if (ret == 0) {
849 if(!newfile || (lret != -1)) {
850 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
851 FILE_NOTIFY_CHANGE_ATTRIBUTES,
852 smb_fname->base_name);
853 }
854 smb_fname->st.st_ex_mode = unixmode;
855 return 0;
856 }
857
858 if((errno != EPERM) && (errno != EACCES))
859 return -1;
860
861 if(!lp_dos_filemode(SNUM(conn)))
862 return -1;
863
864 /* We want DOS semantics, ie allow non owner with write permission to change the
865 bits on a file. Just like file_ntimes below.
866 */
867
868 /* Check if we have write access. */
869 if (CAN_WRITE(conn)) {
870 /*
871 * We need to open the file with write access whilst
872 * still in our current user context. This ensures we
873 * are not violating security in doing the fchmod.
874 */
875 files_struct *fsp;
876 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
877 &fsp)))
878 return -1;
879 become_root();
880 ret = SMB_VFS_FCHMOD(fsp, unixmode);
881 unbecome_root();
882 close_file(NULL, fsp, NORMAL_CLOSE);
883 if (!newfile) {
884 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
885 FILE_NOTIFY_CHANGE_ATTRIBUTES,
886 smb_fname->base_name);
887 }
888 if (ret == 0) {
889 smb_fname->st.st_ex_mode = unixmode;
890 }
891 }
892
893 return( ret );
894}
895
896
897NTSTATUS file_set_sparse(connection_struct *conn,
898 files_struct *fsp,
899 bool sparse)
900{
901 uint32_t old_dosmode;
902 uint32_t new_dosmode;
903 NTSTATUS status;
904
905 if (!CAN_WRITE(conn)) {
906 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
907 "on readonly share[%s]\n",
908 smb_fname_str_dbg(fsp->fsp_name),
909 sparse,
910 lp_servicename(SNUM(conn))));
911 return NT_STATUS_MEDIA_WRITE_PROTECTED;
912 }
913
914 if (!(fsp->access_mask & FILE_WRITE_DATA) &&
915 !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
916 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
917 "access_mask[0x%08X] - access denied\n",
918 smb_fname_str_dbg(fsp->fsp_name),
919 sparse,
920 fsp->access_mask));
921 return NT_STATUS_ACCESS_DENIED;
922 }
923
924 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
925 sparse, smb_fname_str_dbg(fsp->fsp_name)));
926
927 if (!lp_store_dos_attributes(SNUM(conn))) {
928 return NT_STATUS_INVALID_DEVICE_REQUEST;
929 }
930
931 status = vfs_stat_fsp(fsp);
932 if (!NT_STATUS_IS_OK(status)) {
933 return status;
934 }
935
936 old_dosmode = dos_mode(conn, fsp->fsp_name);
937
938 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
939 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
940 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
941 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
942 } else {
943 return NT_STATUS_OK;
944 }
945
946 /* Store the DOS attributes in an EA. */
947 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
948 new_dosmode)) {
949 if (errno == 0) {
950 errno = EIO;
951 }
952 return map_nt_error_from_unix(errno);
953 }
954
955 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
956 FILE_NOTIFY_CHANGE_ATTRIBUTES,
957 fsp->fsp_name->base_name);
958
959 fsp->is_sparse = sparse;
960
961 return NT_STATUS_OK;
962}
963
964/*******************************************************************
965 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
966 than POSIX.
967*******************************************************************/
968
969int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
970 struct smb_file_time *ft)
971{
972 int ret = -1;
973
974 errno = 0;
975
976 DEBUG(6, ("file_ntime: actime: %s",
977 time_to_asc(convert_timespec_to_time_t(ft->atime))));
978 DEBUG(6, ("file_ntime: modtime: %s",
979 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
980 DEBUG(6, ("file_ntime: ctime: %s",
981 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
982 DEBUG(6, ("file_ntime: createtime: %s",
983 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
984
985 /* Don't update the time on read-only shares */
986 /* We need this as set_filetime (which can be called on
987 close and other paths) can end up calling this function
988 without the NEED_WRITE protection. Found by :
989 Leo Weppelman <leo@wau.mis.ah.nl>
990 */
991
992 if (!CAN_WRITE(conn)) {
993 return 0;
994 }
995
996 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
997 return 0;
998 }
999
1000 if((errno != EPERM) && (errno != EACCES)) {
1001 return -1;
1002 }
1003
1004 if(!lp_dos_filetimes(SNUM(conn))) {
1005 return -1;
1006 }
1007
1008 /* We have permission (given by the Samba admin) to
1009 break POSIX semantics and allow a user to change
1010 the time on a file they don't own but can write to
1011 (as DOS does).
1012 */
1013
1014 /* Check if we have write access. */
1015 if (can_write_to_file(conn, smb_fname)) {
1016 /* We are allowed to become root and change the filetime. */
1017 become_root();
1018 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1019 unbecome_root();
1020 }
1021
1022 return ret;
1023}
1024
1025/******************************************************************
1026 Force a "sticky" write time on a pathname. This will always be
1027 returned on all future write time queries and set on close.
1028******************************************************************/
1029
1030bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1031{
1032 if (null_timespec(mtime)) {
1033 return true;
1034 }
1035
1036 if (!set_sticky_write_time(fileid, mtime)) {
1037 return false;
1038 }
1039
1040 return true;
1041}
1042
1043/******************************************************************
1044 Force a "sticky" write time on an fsp. This will always be
1045 returned on all future write time queries and set on close.
1046******************************************************************/
1047
1048bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1049{
1050 if (null_timespec(mtime)) {
1051 return true;
1052 }
1053
1054 fsp->write_time_forced = true;
1055 TALLOC_FREE(fsp->update_write_time_event);
1056
1057 return set_sticky_write_time_path(fsp->file_id, mtime);
1058}
1059
1060/******************************************************************
1061 Set a create time EA.
1062******************************************************************/
1063
1064NTSTATUS set_create_timespec_ea(connection_struct *conn,
1065 const struct smb_filename *psmb_fname,
1066 struct timespec create_time)
1067{
1068 NTSTATUS status;
1069 struct smb_filename *smb_fname = NULL;
1070 uint32_t dosmode;
1071 int ret;
1072
1073 if (!lp_store_dos_attributes(SNUM(conn))) {
1074 return NT_STATUS_OK;
1075 }
1076
1077 status = create_synthetic_smb_fname(talloc_tos(),
1078 psmb_fname->base_name,
1079 NULL, &psmb_fname->st,
1080 &smb_fname);
1081
1082 if (!NT_STATUS_IS_OK(status)) {
1083 return status;
1084 }
1085
1086 dosmode = dos_mode(conn, smb_fname);
1087
1088 smb_fname->st.st_ex_btime = create_time;
1089
1090 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1091 if (ret == -1) {
1092 map_nt_error_from_unix(errno);
1093 }
1094
1095 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1096 smb_fname_str_dbg(smb_fname)));
1097
1098 return NT_STATUS_OK;
1099}
1100
1101/******************************************************************
1102 Return a create time.
1103******************************************************************/
1104
1105struct timespec get_create_timespec(connection_struct *conn,
1106 struct files_struct *fsp,
1107 const struct smb_filename *smb_fname)
1108{
1109 return smb_fname->st.st_ex_btime;
1110}
1111
1112/******************************************************************
1113 Return a change time (may look at EA in future).
1114******************************************************************/
1115
1116struct timespec get_change_timespec(connection_struct *conn,
1117 struct files_struct *fsp,
1118 const struct smb_filename *smb_fname)
1119{
1120 return smb_fname->st.st_ex_mtime;
1121}
Note: See TracBrowser for help on using the repository browser.