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

Last change on this file was 989, checked in by Silvan Scherrer, 9 years ago

Samba Server: update vendor to version 4.4.7

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