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 "globals.h"
23 #include "system/filesys.h"
24 #include "librpc/gen_ndr/ndr_xattr.h"
25 #include "librpc/gen_ndr/ioctl.h"
26 #include "../libcli/security/security.h"
27 #include "smbd/smbd.h"
28 #include "lib/param/loadparm.h"
29 #include "lib/util/tevent_ntstatus.h"
30
31 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
32 const struct smb_filename *smb_fname,
33 files_struct **ret_fsp,
34 bool *need_close);
35
dos_mode_debug_print(const char * func,uint32_t mode)36 static void dos_mode_debug_print(const char *func, uint32_t mode)
37 {
38 fstring modestr;
39
40 if (DEBUGLEVEL < DBGLVL_INFO) {
41 return;
42 }
43
44 modestr[0] = '\0';
45
46 if (mode & FILE_ATTRIBUTE_HIDDEN) {
47 fstrcat(modestr, "h");
48 }
49 if (mode & FILE_ATTRIBUTE_READONLY) {
50 fstrcat(modestr, "r");
51 }
52 if (mode & FILE_ATTRIBUTE_SYSTEM) {
53 fstrcat(modestr, "s");
54 }
55 if (mode & FILE_ATTRIBUTE_DIRECTORY) {
56 fstrcat(modestr, "d");
57 }
58 if (mode & FILE_ATTRIBUTE_ARCHIVE) {
59 fstrcat(modestr, "a");
60 }
61 if (mode & FILE_ATTRIBUTE_SPARSE) {
62 fstrcat(modestr, "[sparse]");
63 }
64 if (mode & FILE_ATTRIBUTE_OFFLINE) {
65 fstrcat(modestr, "[offline]");
66 }
67 if (mode & FILE_ATTRIBUTE_COMPRESSED) {
68 fstrcat(modestr, "[compressed]");
69 }
70
71 DBG_INFO("%s returning (0x%x): \"%s\"\n", func, (unsigned)mode,
72 modestr);
73 }
74
filter_mode_by_protocol(uint32_t mode)75 static uint32_t filter_mode_by_protocol(uint32_t mode)
76 {
77 if (get_Protocol() <= PROTOCOL_LANMAN2) {
78 DEBUG(10,("filter_mode_by_protocol: "
79 "filtering result 0x%x to 0x%x\n",
80 (unsigned int)mode,
81 (unsigned int)(mode & 0x3f) ));
82 mode &= 0x3f;
83 }
84 return mode;
85 }
86
set_link_read_only_flag(const SMB_STRUCT_STAT * const sbuf)87 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
88 {
89 #ifdef S_ISLNK
90 #if LINKS_READ_ONLY
91 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
92 return FILE_ATTRIBUTE_READONLY;
93 #endif
94 #endif
95 return 0;
96 }
97
98 /****************************************************************************
99 Change a dos mode to a unix mode.
100 Base permission for files:
101 if creating file and inheriting (i.e. parent_dir != NULL)
102 apply read/write bits from parent directory.
103 else
104 everybody gets read bit set
105 dos readonly is represented in unix by removing everyone's write bit
106 dos archive is represented in unix by the user's execute bit
107 dos system is represented in unix by the group's execute bit
108 dos hidden is represented in unix by the other's execute bit
109 if !inheriting {
110 Then apply create mask,
111 then add force bits.
112 }
113 Base permission for directories:
114 dos directory is represented in unix by unix's dir bit and the exec bit
115 if !inheriting {
116 Then apply create mask,
117 then add force bits.
118 }
119 ****************************************************************************/
120
unix_mode(connection_struct * conn,int dosmode,const struct smb_filename * smb_fname,const char * inherit_from_dir)121 mode_t unix_mode(connection_struct *conn, int dosmode,
122 const struct smb_filename *smb_fname,
123 const char *inherit_from_dir)
124 {
125 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
126 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
127 * inheriting. */
128
129 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
130 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
131 }
132
133 if ((inherit_from_dir != NULL) && lp_inherit_permissions(SNUM(conn))) {
134 struct smb_filename *smb_fname_parent;
135
136 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
137 smb_fname_str_dbg(smb_fname),
138 inherit_from_dir));
139
140 smb_fname_parent = synthetic_smb_fname(talloc_tos(),
141 inherit_from_dir,
142 NULL,
143 NULL,
144 smb_fname->flags);
145 if (smb_fname_parent == NULL) {
146 DEBUG(1,("unix_mode(%s) failed, [dir %s]: No memory\n",
147 smb_fname_str_dbg(smb_fname),
148 inherit_from_dir));
149 return(0);
150 }
151
152 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
153 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
154 smb_fname_str_dbg(smb_fname),
155 inherit_from_dir, strerror(errno)));
156 TALLOC_FREE(smb_fname_parent);
157 return(0); /* *** shouldn't happen! *** */
158 }
159
160 /* Save for later - but explicitly remove setuid bit for safety. */
161 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
162 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
163 smb_fname_str_dbg(smb_fname), (int)dir_mode));
164 /* Clear "result" */
165 result = 0;
166 TALLOC_FREE(smb_fname_parent);
167 }
168
169 if (IS_DOS_DIR(dosmode)) {
170 /* We never make directories read only for the owner as under DOS a user
171 can always create a file in a read-only directory. */
172 result |= (S_IFDIR | S_IWUSR);
173
174 if (dir_mode) {
175 /* Inherit mode of parent directory. */
176 result |= dir_mode;
177 } else {
178 /* Provisionally add all 'x' bits */
179 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
180
181 /* Apply directory mask */
182 result &= lp_directory_mask(SNUM(conn));
183 /* Add in force bits */
184 result |= lp_force_directory_mode(SNUM(conn));
185 }
186 } else {
187 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
188 result |= S_IXUSR;
189
190 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
191 result |= S_IXGRP;
192
193 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
194 result |= S_IXOTH;
195
196 if (dir_mode) {
197 /* Inherit 666 component of parent directory mode */
198 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
199 } else {
200 /* Apply mode mask */
201 result &= lp_create_mask(SNUM(conn));
202 /* Add in force bits */
203 result |= lp_force_create_mode(SNUM(conn));
204 }
205 }
206
207 DBG_INFO("unix_mode(%s) returning 0%o\n",
208 smb_fname_str_dbg(smb_fname), (int)result);
209
210 return(result);
211 }
212
213 /****************************************************************************
214 Change a unix mode to a dos mode.
215 ****************************************************************************/
216
dos_mode_from_sbuf(connection_struct * conn,const struct smb_filename * smb_fname)217 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
218 const struct smb_filename *smb_fname)
219 {
220 int result = 0;
221 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
222
223 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
224 /* if we can find out if a file is immutable we should report it r/o */
225 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
226 result |= FILE_ATTRIBUTE_READONLY;
227 }
228 #endif
229 if (ro_opts == MAP_READONLY_YES) {
230 /* Original Samba method - map inverse of user "w" bit. */
231 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
232 result |= FILE_ATTRIBUTE_READONLY;
233 }
234 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
235 /* Check actual permissions for read-only. */
236 if (!can_write_to_file(conn, smb_fname)) {
237 result |= FILE_ATTRIBUTE_READONLY;
238 }
239 } /* Else never set the readonly bit. */
240
241 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
242 result |= FILE_ATTRIBUTE_ARCHIVE;
243
244 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
245 result |= FILE_ATTRIBUTE_SYSTEM;
246
247 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
248 result |= FILE_ATTRIBUTE_HIDDEN;
249
250 if (S_ISDIR(smb_fname->st.st_ex_mode))
251 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
252
253 result |= set_link_read_only_flag(&smb_fname->st);
254
255 dos_mode_debug_print(__func__, result);
256
257 return result;
258 }
259
260 /****************************************************************************
261 Get DOS attributes from an EA.
262 This can also pull the create time into the stat struct inside smb_fname.
263 ****************************************************************************/
264
parse_dos_attribute_blob(struct smb_filename * smb_fname,DATA_BLOB blob,uint32_t * pattr)265 NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
266 DATA_BLOB blob,
267 uint32_t *pattr)
268 {
269 struct xattr_DOSATTRIB dosattrib;
270 enum ndr_err_code ndr_err;
271 uint32_t dosattr;
272
273 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
274 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
275
276 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
277 DBG_WARNING("bad ndr decode "
278 "from EA on file %s: Error = %s\n",
279 smb_fname_str_dbg(smb_fname),
280 ndr_errstr(ndr_err));
281 return ndr_map_error2ntstatus(ndr_err);
282 }
283
284 DBG_DEBUG("%s attr = %s\n",
285 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
286
287 switch (dosattrib.version) {
288 case 0xFFFF:
289 dosattr = dosattrib.info.compatinfoFFFF.attrib;
290 break;
291 case 1:
292 dosattr = dosattrib.info.info1.attrib;
293 if (!null_nttime(dosattrib.info.info1.create_time)) {
294 struct timespec create_time =
295 nt_time_to_unix_timespec(
296 dosattrib.info.info1.create_time);
297
298 update_stat_ex_create_time(&smb_fname->st,
299 create_time);
300
301 DBG_DEBUG("file %s case 1 set btime %s\n",
302 smb_fname_str_dbg(smb_fname),
303 time_to_asc(convert_timespec_to_time_t(
304 create_time)));
305 }
306 break;
307 case 2:
308 dosattr = dosattrib.info.oldinfo2.attrib;
309 /* Don't know what flags to check for this case. */
310 break;
311 case 3:
312 dosattr = dosattrib.info.info3.attrib;
313 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
314 !null_nttime(dosattrib.info.info3.create_time)) {
315 struct timespec create_time =
316 nt_time_to_full_timespec(
317 dosattrib.info.info3.create_time);
318
319 update_stat_ex_create_time(&smb_fname->st,
320 create_time);
321
322 DBG_DEBUG("file %s case 3 set btime %s\n",
323 smb_fname_str_dbg(smb_fname),
324 time_to_asc(convert_timespec_to_time_t(
325 create_time)));
326 }
327 break;
328 case 4:
329 {
330 struct xattr_DosInfo4 *info = &dosattrib.info.info4;
331
332 dosattr = info->attrib;
333
334 if ((info->valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
335 !null_nttime(info->create_time))
336 {
337 struct timespec creat_time;
338
339 creat_time = nt_time_to_full_timespec(info->create_time);
340 update_stat_ex_create_time(&smb_fname->st, creat_time);
341
342 DBG_DEBUG("file [%s] creation time [%s]\n",
343 smb_fname_str_dbg(smb_fname),
344 nt_time_string(talloc_tos(), info->create_time));
345 }
346
347 if (info->valid_flags & XATTR_DOSINFO_ITIME) {
348 struct timespec itime;
349 uint64_t file_id;
350
351 itime = nt_time_to_unix_timespec(info->itime);
352 if (smb_fname->st.st_ex_iflags &
353 ST_EX_IFLAG_CALCULATED_ITIME)
354 {
355 update_stat_ex_itime(&smb_fname->st, itime);
356 }
357
358 file_id = make_file_id_from_itime(&smb_fname->st);
359 if (smb_fname->st.st_ex_iflags &
360 ST_EX_IFLAG_CALCULATED_FILE_ID)
361 {
362 update_stat_ex_file_id(&smb_fname->st, file_id);
363 }
364
365 DBG_DEBUG("file [%s] itime [%s] fileid [%"PRIx64"]\n",
366 smb_fname_str_dbg(smb_fname),
367 nt_time_string(talloc_tos(), info->itime),
368 file_id);
369 }
370 break;
371 }
372 default:
373 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
374 smb_fname_str_dbg(smb_fname), blob.data);
375 /* Should this be INTERNAL_ERROR? */
376 return NT_STATUS_INVALID_PARAMETER;
377 }
378
379 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
380 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
381 }
382
383 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
384 *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
385
386 dos_mode_debug_print(__func__, *pattr);
387
388 return NT_STATUS_OK;
389 }
390
get_ea_dos_attribute(connection_struct * conn,struct smb_filename * smb_fname,uint32_t * pattr)391 NTSTATUS get_ea_dos_attribute(connection_struct *conn,
392 struct smb_filename *smb_fname,
393 uint32_t *pattr)
394 {
395 DATA_BLOB blob;
396 ssize_t sizeret;
397 fstring attrstr;
398 NTSTATUS status;
399
400 if (!lp_store_dos_attributes(SNUM(conn))) {
401 return NT_STATUS_NOT_IMPLEMENTED;
402 }
403
404 /* Don't reset pattr to zero as we may already have filename-based attributes we
405 need to preserve. */
406
407 sizeret = SMB_VFS_GETXATTR(conn, smb_fname,
408 SAMBA_XATTR_DOS_ATTRIB, attrstr,
409 sizeof(attrstr));
410 if (sizeret == -1 && errno == EACCES) {
411 int saved_errno = 0;
412
413 /*
414 * According to MS-FSA 2.1.5.1.2.1 "Algorithm to Check Access to
415 * an Existing File" FILE_LIST_DIRECTORY on a directory implies
416 * FILE_READ_ATTRIBUTES for directory entries. Being able to
417 * stat() a file implies FILE_LIST_DIRECTORY for the directory
418 * containing the file.
419 */
420
421 if (!VALID_STAT(smb_fname->st)) {
422 /*
423 * Safety net: dos_mode() already checks this, but as we
424 * become root based on this, add an additional layer of
425 * defense.
426 */
427 DBG_ERR("Rejecting root override, invalid stat [%s]\n",
428 smb_fname_str_dbg(smb_fname));
429 return NT_STATUS_ACCESS_DENIED;
430 }
431
432 become_root();
433 sizeret = SMB_VFS_GETXATTR(conn, smb_fname,
434 SAMBA_XATTR_DOS_ATTRIB,
435 attrstr,
436 sizeof(attrstr));
437 if (sizeret == -1) {
438 saved_errno = errno;
439 }
440 unbecome_root();
441
442 if (saved_errno != 0) {
443 errno = saved_errno;
444 }
445 }
446 if (sizeret == -1) {
447 DBG_INFO("Cannot get attribute "
448 "from EA on file %s: Error = %s\n",
449 smb_fname_str_dbg(smb_fname), strerror(errno));
450 return map_nt_error_from_unix(errno);
451 }
452
453 blob.data = (uint8_t *)attrstr;
454 blob.length = sizeret;
455
456 status = parse_dos_attribute_blob(smb_fname, blob, pattr);
457 if (!NT_STATUS_IS_OK(status)) {
458 return status;
459 }
460
461 return NT_STATUS_OK;
462 }
463
464 /****************************************************************************
465 Set DOS attributes in an EA.
466 Also sets the create time.
467 ****************************************************************************/
468
set_ea_dos_attribute(connection_struct * conn,const struct smb_filename * smb_fname,uint32_t dosmode)469 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
470 const struct smb_filename *smb_fname,
471 uint32_t dosmode)
472 {
473 struct xattr_DOSATTRIB dosattrib;
474 enum ndr_err_code ndr_err;
475 DATA_BLOB blob;
476 int ret;
477
478 if (!lp_store_dos_attributes(SNUM(conn))) {
479 return NT_STATUS_NOT_IMPLEMENTED;
480 }
481
482 /*
483 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
484 * vfs_default via DMAPI if that is enabled.
485 */
486 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
487
488 ZERO_STRUCT(dosattrib);
489 ZERO_STRUCT(blob);
490
491 dosattrib.version = 4;
492 dosattrib.info.info4.valid_flags = XATTR_DOSINFO_ATTRIB |
493 XATTR_DOSINFO_CREATE_TIME;
494 dosattrib.info.info4.attrib = dosmode;
495 dosattrib.info.info4.create_time = full_timespec_to_nt_time(
496 &smb_fname->st.st_ex_btime);
497
498 if (!(smb_fname->st.st_ex_iflags & ST_EX_IFLAG_CALCULATED_ITIME)) {
499 dosattrib.info.info4.valid_flags |= XATTR_DOSINFO_ITIME;
500 dosattrib.info.info4.itime = full_timespec_to_nt_time(
501 &smb_fname->st.st_ex_itime);
502 }
503
504 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
505 (unsigned int)dosmode,
506 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
507 smb_fname_str_dbg(smb_fname) ));
508
509 ndr_err = ndr_push_struct_blob(
510 &blob, talloc_tos(), &dosattrib,
511 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
512
513 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
514 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
515 ndr_errstr(ndr_err)));
516 return ndr_map_error2ntstatus(ndr_err);
517 }
518
519 if (blob.data == NULL || blob.length == 0) {
520 /* Should this be INTERNAL_ERROR? */
521 return NT_STATUS_INVALID_PARAMETER;
522 }
523
524 ret = SMB_VFS_SETXATTR(conn, smb_fname,
525 SAMBA_XATTR_DOS_ATTRIB,
526 blob.data, blob.length, 0);
527 if (ret != 0) {
528 NTSTATUS status = NT_STATUS_OK;
529 bool need_close = false;
530 files_struct *fsp = NULL;
531 bool set_dosmode_ok = false;
532
533 if ((errno != EPERM) && (errno != EACCES)) {
534 DBG_INFO("Cannot set "
535 "attribute EA on file %s: Error = %s\n",
536 smb_fname_str_dbg(smb_fname), strerror(errno));
537 return map_nt_error_from_unix(errno);
538 }
539
540 /* We want DOS semantics, ie allow non owner with write permission to change the
541 bits on a file. Just like file_ntimes below.
542 */
543
544 /* Check if we have write access. */
545 if (!CAN_WRITE(conn)) {
546 return NT_STATUS_ACCESS_DENIED;
547 }
548
549 status = smbd_check_access_rights(conn, smb_fname, false,
550 FILE_WRITE_ATTRIBUTES);
551 if (NT_STATUS_IS_OK(status)) {
552 set_dosmode_ok = true;
553 }
554
555 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
556 set_dosmode_ok = can_write_to_file(conn, smb_fname);
557 }
558
559 if (!set_dosmode_ok) {
560 return NT_STATUS_ACCESS_DENIED;
561 }
562
563 /*
564 * We need to get an open file handle to do the
565 * metadata operation under root.
566 */
567
568 status = get_file_handle_for_metadata(conn,
569 smb_fname,
570 &fsp,
571 &need_close);
572 if (!NT_STATUS_IS_OK(status)) {
573 return status;
574 }
575
576 become_root();
577 ret = SMB_VFS_FSETXATTR(fsp,
578 SAMBA_XATTR_DOS_ATTRIB,
579 blob.data, blob.length, 0);
580 if (ret == 0) {
581 status = NT_STATUS_OK;
582 }
583 unbecome_root();
584 if (need_close) {
585 close_file(NULL, fsp, NORMAL_CLOSE);
586 }
587 return status;
588 }
589 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
590 (unsigned int)dosmode,
591 smb_fname_str_dbg(smb_fname)));
592 return NT_STATUS_OK;
593 }
594
595 /****************************************************************************
596 Change a unix mode to a dos mode for an ms dfs link.
597 ****************************************************************************/
598
dos_mode_msdfs(connection_struct * conn,const struct smb_filename * smb_fname)599 uint32_t dos_mode_msdfs(connection_struct *conn,
600 const struct smb_filename *smb_fname)
601 {
602 uint32_t result = 0;
603
604 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
605
606 if (!VALID_STAT(smb_fname->st)) {
607 return 0;
608 }
609
610 /* First do any modifications that depend on the path name. */
611 /* hide files with a name starting with a . */
612 if (lp_hide_dot_files(SNUM(conn))) {
613 const char *p = strrchr_m(smb_fname->base_name, '/');
614 if (p) {
615 p++;
616 } else {
617 p = smb_fname->base_name;
618 }
619
620 /* Only . and .. are not hidden. */
621 if (p[0] == '.' && !((p[1] == '\0') ||
622 (p[1] == '.' && p[2] == '\0'))) {
623 result |= FILE_ATTRIBUTE_HIDDEN;
624 }
625 }
626
627 result |= dos_mode_from_sbuf(conn, smb_fname);
628
629 /* Optimization : Only call is_hidden_path if it's not already
630 hidden. */
631 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
632 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
633 result |= FILE_ATTRIBUTE_HIDDEN;
634 }
635
636 if (result == 0) {
637 result = FILE_ATTRIBUTE_NORMAL;
638 }
639
640 result = filter_mode_by_protocol(result);
641
642 /*
643 * Add in that it is a reparse point
644 */
645 result |= FILE_ATTRIBUTE_REPARSE_POINT;
646
647 dos_mode_debug_print(__func__, result);
648
649 return(result);
650 }
651
652 /*
653 * check whether a file or directory is flagged as compressed.
654 */
dos_mode_check_compressed(connection_struct * conn,struct smb_filename * smb_fname,bool * is_compressed)655 static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
656 struct smb_filename *smb_fname,
657 bool *is_compressed)
658 {
659 NTSTATUS status;
660 uint16_t compression_fmt;
661 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
662 if (tmp_ctx == NULL) {
663 status = NT_STATUS_NO_MEMORY;
664 goto err_out;
665 }
666
667 status = SMB_VFS_GET_COMPRESSION(conn, tmp_ctx, NULL, smb_fname,
668 &compression_fmt);
669 if (!NT_STATUS_IS_OK(status)) {
670 goto err_ctx_free;
671 }
672
673 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
674 *is_compressed = true;
675 } else {
676 *is_compressed = false;
677 }
678 status = NT_STATUS_OK;
679
680 err_ctx_free:
681 talloc_free(tmp_ctx);
682 err_out:
683 return status;
684 }
685
dos_mode_from_name(connection_struct * conn,const struct smb_filename * smb_fname,uint32_t dosmode)686 static uint32_t dos_mode_from_name(connection_struct *conn,
687 const struct smb_filename *smb_fname,
688 uint32_t dosmode)
689 {
690 const char *p = NULL;
691 uint32_t result = dosmode;
692
693 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
694 lp_hide_dot_files(SNUM(conn)))
695 {
696 p = strrchr_m(smb_fname->base_name, '/');
697 if (p) {
698 p++;
699 } else {
700 p = smb_fname->base_name;
701 }
702
703 /* Only . and .. are not hidden. */
704 if ((p[0] == '.') &&
705 !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0')))
706 {
707 result |= FILE_ATTRIBUTE_HIDDEN;
708 }
709 }
710
711 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
712 IS_HIDDEN_PATH(conn, smb_fname->base_name))
713 {
714 result |= FILE_ATTRIBUTE_HIDDEN;
715 }
716
717 return result;
718 }
719
dos_mode_post(uint32_t dosmode,connection_struct * conn,struct smb_filename * smb_fname,const char * func)720 static uint32_t dos_mode_post(uint32_t dosmode,
721 connection_struct *conn,
722 struct smb_filename *smb_fname,
723 const char *func)
724 {
725 NTSTATUS status;
726
727 /*
728 * According to MS-FSA a stream name does not have
729 * separate DOS attribute metadata, so we must return
730 * the DOS attribute from the base filename. With one caveat,
731 * a non-default stream name can never be a directory.
732 *
733 * As this is common to all streams data stores, we handle
734 * it here instead of inside all stream VFS modules.
735 *
736 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
737 */
738
739 if (is_named_stream(smb_fname)) {
740 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
741 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
742 }
743
744 if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
745 bool compressed = false;
746
747 status = dos_mode_check_compressed(conn, smb_fname,
748 &compressed);
749 if (NT_STATUS_IS_OK(status) && compressed) {
750 dosmode |= FILE_ATTRIBUTE_COMPRESSED;
751 }
752 }
753
754 dosmode |= dos_mode_from_name(conn, smb_fname, dosmode);
755
756 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
757 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
758 } else if (dosmode == 0) {
759 dosmode = FILE_ATTRIBUTE_NORMAL;
760 }
761
762 dosmode = filter_mode_by_protocol(dosmode);
763
764 dos_mode_debug_print(func, dosmode);
765 return dosmode;
766 }
767
768 /****************************************************************************
769 Change a unix mode to a dos mode.
770 May also read the create timespec into the stat struct in smb_fname
771 if "store dos attributes" is true.
772 ****************************************************************************/
773
dos_mode(connection_struct * conn,struct smb_filename * smb_fname)774 uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
775 {
776 uint32_t result = 0;
777 NTSTATUS status = NT_STATUS_OK;
778
779 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
780
781 if (!VALID_STAT(smb_fname->st)) {
782 return 0;
783 }
784
785 /* Get the DOS attributes via the VFS if we can */
786 status = SMB_VFS_GET_DOS_ATTRIBUTES(conn, smb_fname, &result);
787 if (!NT_STATUS_IS_OK(status)) {
788 /*
789 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
790 */
791 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
792 result |= dos_mode_from_sbuf(conn, smb_fname);
793 }
794 }
795
796 result = dos_mode_post(result, conn, smb_fname, __func__);
797 return result;
798 }
799
800 struct dos_mode_at_state {
801 files_struct *dir_fsp;
802 struct smb_filename *smb_fname;
803 uint32_t dosmode;
804 };
805
806 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
807
dos_mode_at_send(TALLOC_CTX * mem_ctx,struct tevent_context * ev,files_struct * dir_fsp,struct smb_filename * smb_fname)808 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
809 struct tevent_context *ev,
810 files_struct *dir_fsp,
811 struct smb_filename *smb_fname)
812 {
813 struct tevent_req *req = NULL;
814 struct dos_mode_at_state *state = NULL;
815 struct tevent_req *subreq = NULL;
816
817 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
818
819 req = tevent_req_create(mem_ctx, &state,
820 struct dos_mode_at_state);
821 if (req == NULL) {
822 return NULL;
823 }
824
825 *state = (struct dos_mode_at_state) {
826 .dir_fsp = dir_fsp,
827 .smb_fname = smb_fname,
828 };
829
830 if (!VALID_STAT(smb_fname->st)) {
831 tevent_req_done(req);
832 return tevent_req_post(req, ev);
833 }
834
835 subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
836 ev,
837 dir_fsp,
838 smb_fname);
839 if (tevent_req_nomem(subreq, req)) {
840 return tevent_req_post(req, ev);
841 }
842 tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
843
844 return req;
845 }
846
dos_mode_at_vfs_get_dosmode_done(struct tevent_req * subreq)847 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
848 {
849 struct tevent_req *req =
850 tevent_req_callback_data(subreq,
851 struct tevent_req);
852 struct dos_mode_at_state *state =
853 tevent_req_data(req,
854 struct dos_mode_at_state);
855 char *path = NULL;
856 struct smb_filename *smb_path = NULL;
857 struct vfs_aio_state aio_state;
858 NTSTATUS status;
859 bool ok;
860
861 /*
862 * Make sure we run as the user again
863 */
864 ok = change_to_user_and_service_by_fsp(state->dir_fsp);
865 SMB_ASSERT(ok);
866
867 status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
868 &aio_state,
869 &state->dosmode);
870 TALLOC_FREE(subreq);
871 if (!NT_STATUS_IS_OK(status)) {
872 /*
873 * Both the sync dos_mode() as well as the async
874 * dos_mode_at_[send|recv] have no real error return, the only
875 * unhandled error is when the stat info in smb_fname is not
876 * valid (cf the checks in dos_mode() and dos_mode_at_send().
877 *
878 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
879 * dos_mode_post() which also does the mapping of a last ressort
880 * from S_IFMT(st_mode).
881 *
882 * Only if we get NT_STATUS_NOT_IMPLEMENTED from a stacked VFS
883 * module we must fallback to sync processing.
884 */
885 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
886 /*
887 * state->dosmode should still be 0, but reset
888 * it to be sure.
889 */
890 state->dosmode = 0;
891 status = NT_STATUS_OK;
892 }
893 }
894 if (NT_STATUS_IS_OK(status)) {
895 state->dosmode = dos_mode_post(state->dosmode,
896 state->dir_fsp->conn,
897 state->smb_fname,
898 __func__);
899 tevent_req_done(req);
900 return;
901 }
902
903 /*
904 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
905 */
906
907 path = talloc_asprintf(state,
908 "%s/%s",
909 state->dir_fsp->fsp_name->base_name,
910 state->smb_fname->base_name);
911 if (tevent_req_nomem(path, req)) {
912 return;
913 }
914
915 smb_path = synthetic_smb_fname(state,
916 path,
917 NULL,
918 &state->smb_fname->st,
919 0);
920 if (tevent_req_nomem(smb_path, req)) {
921 return;
922 }
923
924 state->dosmode = dos_mode(state->dir_fsp->conn, smb_path);
925 tevent_req_done(req);
926 return;
927 }
928
dos_mode_at_recv(struct tevent_req * req,uint32_t * dosmode)929 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
930 {
931 struct dos_mode_at_state *state =
932 tevent_req_data(req,
933 struct dos_mode_at_state);
934 NTSTATUS status;
935
936 if (tevent_req_is_nterror(req, &status)) {
937 tevent_req_received(req);
938 return status;
939 }
940
941 *dosmode = state->dosmode;
942 tevent_req_received(req);
943 return NT_STATUS_OK;
944 }
945
946 /*******************************************************************
947 chmod a file - but preserve some bits.
948 If "store dos attributes" is also set it will store the create time
949 from the stat struct in smb_fname (in NTTIME format) in the EA
950 attribute also.
951 ********************************************************************/
952
file_set_dosmode(connection_struct * conn,struct smb_filename * smb_fname,uint32_t dosmode,const char * parent_dir,bool newfile)953 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
954 uint32_t dosmode, const char *parent_dir, bool newfile)
955 {
956 int mask=0;
957 mode_t tmp;
958 mode_t unixmode;
959 int ret = -1, lret = -1;
960 files_struct *fsp = NULL;
961 bool need_close = false;
962 NTSTATUS status;
963
964 if (!CAN_WRITE(conn)) {
965 errno = EROFS;
966 return -1;
967 }
968
969 dosmode &= SAMBA_ATTRIBUTES_MASK;
970
971 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
972 dosmode, smb_fname_str_dbg(smb_fname)));
973
974 unixmode = smb_fname->st.st_ex_mode;
975
976 get_acl_group_bits(conn, smb_fname,
977 &smb_fname->st.st_ex_mode);
978
979 if (S_ISDIR(smb_fname->st.st_ex_mode))
980 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
981 else
982 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
983
984 /* Store the DOS attributes in an EA by preference. */
985 status = SMB_VFS_SET_DOS_ATTRIBUTES(conn, smb_fname, dosmode);
986 if (NT_STATUS_IS_OK(status)) {
987 if (!newfile) {
988 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
989 FILE_NOTIFY_CHANGE_ATTRIBUTES,
990 smb_fname->base_name);
991 }
992 smb_fname->st.st_ex_mode = unixmode;
993 return 0;
994 } else {
995 /*
996 * Only fall back to using UNIX modes if
997 * we get NOT_IMPLEMENTED.
998 */
999 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
1000 errno = map_errno_from_nt_status(status);
1001 return -1;
1002 }
1003 }
1004
1005 /* Fall back to UNIX modes. */
1006 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
1007
1008 /* preserve the file type bits */
1009 mask |= S_IFMT;
1010
1011 /* preserve the s bits */
1012 mask |= (S_ISUID | S_ISGID);
1013
1014 /* preserve the t bit */
1015 #ifdef S_ISVTX
1016 mask |= S_ISVTX;
1017 #endif
1018
1019 /* possibly preserve the x bits */
1020 if (!MAP_ARCHIVE(conn))
1021 mask |= S_IXUSR;
1022 if (!MAP_SYSTEM(conn))
1023 mask |= S_IXGRP;
1024 if (!MAP_HIDDEN(conn))
1025 mask |= S_IXOTH;
1026
1027 unixmode |= (smb_fname->st.st_ex_mode & mask);
1028
1029 /* if we previously had any r bits set then leave them alone */
1030 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
1031 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
1032 unixmode |= tmp;
1033 }
1034
1035 /* if we previously had any w bits set then leave them alone
1036 whilst adding in the new w bits, if the new mode is not rdonly */
1037 if (!IS_DOS_READONLY(dosmode)) {
1038 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
1039 }
1040
1041 /*
1042 * From the chmod 2 man page:
1043 *
1044 * "If the calling process is not privileged, and the group of the file
1045 * does not match the effective group ID of the process or one of its
1046 * supplementary group IDs, the S_ISGID bit will be turned off, but
1047 * this will not cause an error to be returned."
1048 *
1049 * Simply refuse to do the chmod in this case.
1050 */
1051
1052 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
1053 geteuid() != sec_initial_uid() &&
1054 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
1055 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1056 "set for directory %s\n",
1057 smb_fname_str_dbg(smb_fname)));
1058 errno = EPERM;
1059 return -1;
1060 }
1061
1062 ret = SMB_VFS_CHMOD(conn, smb_fname, unixmode);
1063 if (ret == 0) {
1064 if(!newfile || (lret != -1)) {
1065 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1066 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1067 smb_fname->base_name);
1068 }
1069 smb_fname->st.st_ex_mode = unixmode;
1070 return 0;
1071 }
1072
1073 if((errno != EPERM) && (errno != EACCES))
1074 return -1;
1075
1076 if(!lp_dos_filemode(SNUM(conn)))
1077 return -1;
1078
1079 /* We want DOS semantics, ie allow non owner with write permission to change the
1080 bits on a file. Just like file_ntimes below.
1081 */
1082
1083 if (!can_write_to_file(conn, smb_fname)) {
1084 errno = EACCES;
1085 return -1;
1086 }
1087
1088 /*
1089 * We need to get an open file handle to do the
1090 * metadata operation under root.
1091 */
1092
1093 status = get_file_handle_for_metadata(conn,
1094 smb_fname,
1095 &fsp,
1096 &need_close);
1097 if (!NT_STATUS_IS_OK(status)) {
1098 errno = map_errno_from_nt_status(status);
1099 return -1;
1100 }
1101
1102 become_root();
1103 ret = SMB_VFS_FCHMOD(fsp, unixmode);
1104 unbecome_root();
1105 if (need_close) {
1106 close_file(NULL, fsp, NORMAL_CLOSE);
1107 }
1108 if (!newfile) {
1109 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1110 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1111 smb_fname->base_name);
1112 }
1113 if (ret == 0) {
1114 smb_fname->st.st_ex_mode = unixmode;
1115 }
1116
1117 return( ret );
1118 }
1119
1120
file_set_sparse(connection_struct * conn,files_struct * fsp,bool sparse)1121 NTSTATUS file_set_sparse(connection_struct *conn,
1122 files_struct *fsp,
1123 bool sparse)
1124 {
1125 const struct loadparm_substitution *lp_sub =
1126 loadparm_s3_global_substitution();
1127 uint32_t old_dosmode;
1128 uint32_t new_dosmode;
1129 NTSTATUS status;
1130
1131 if (!CAN_WRITE(conn)) {
1132 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1133 "on readonly share[%s]\n",
1134 smb_fname_str_dbg(fsp->fsp_name),
1135 sparse,
1136 lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1137 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1138 }
1139
1140 /*
1141 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1142 * following access flags are granted.
1143 */
1144 if ((fsp->access_mask & (FILE_WRITE_DATA
1145 | FILE_WRITE_ATTRIBUTES
1146 | SEC_FILE_APPEND_DATA)) == 0) {
1147 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1148 "access_mask[0x%08X] - access denied\n",
1149 smb_fname_str_dbg(fsp->fsp_name),
1150 sparse,
1151 fsp->access_mask));
1152 return NT_STATUS_ACCESS_DENIED;
1153 }
1154
1155 if (fsp->is_directory) {
1156 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1157 (sparse ? "set" : "clear"),
1158 smb_fname_str_dbg(fsp->fsp_name)));
1159 return NT_STATUS_INVALID_PARAMETER;
1160 }
1161
1162 if (IS_IPC(conn) || IS_PRINT(conn)) {
1163 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1164 (sparse ? "set" : "clear")));
1165 return NT_STATUS_INVALID_PARAMETER;
1166 }
1167
1168 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1169 sparse, smb_fname_str_dbg(fsp->fsp_name)));
1170
1171 if (!lp_store_dos_attributes(SNUM(conn))) {
1172 return NT_STATUS_INVALID_DEVICE_REQUEST;
1173 }
1174
1175 status = vfs_stat_fsp(fsp);
1176 if (!NT_STATUS_IS_OK(status)) {
1177 return status;
1178 }
1179
1180 old_dosmode = dos_mode(conn, fsp->fsp_name);
1181
1182 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1183 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1184 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1185 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1186 } else {
1187 return NT_STATUS_OK;
1188 }
1189
1190 /* Store the DOS attributes in an EA. */
1191 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1192 if (!NT_STATUS_IS_OK(status)) {
1193 return status;
1194 }
1195
1196 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1197 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1198 fsp->fsp_name->base_name);
1199
1200 fsp->is_sparse = sparse;
1201
1202 return NT_STATUS_OK;
1203 }
1204
1205 /*******************************************************************
1206 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1207 than POSIX.
1208 *******************************************************************/
1209
file_ntimes(connection_struct * conn,const struct smb_filename * smb_fname,struct smb_file_time * ft)1210 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
1211 struct smb_file_time *ft)
1212 {
1213 int ret = -1;
1214
1215 errno = 0;
1216
1217 DEBUG(6, ("file_ntime: actime: %s",
1218 time_to_asc(convert_timespec_to_time_t(ft->atime))));
1219 DEBUG(6, ("file_ntime: modtime: %s",
1220 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
1221 DEBUG(6, ("file_ntime: ctime: %s",
1222 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
1223 DEBUG(6, ("file_ntime: createtime: %s",
1224 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
1225
1226 /* Don't update the time on read-only shares */
1227 /* We need this as set_filetime (which can be called on
1228 close and other paths) can end up calling this function
1229 without the NEED_WRITE protection. Found by :
1230 Leo Weppelman <leo@wau.mis.ah.nl>
1231 */
1232
1233 if (!CAN_WRITE(conn)) {
1234 return 0;
1235 }
1236
1237 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
1238 return 0;
1239 }
1240
1241 if((errno != EPERM) && (errno != EACCES)) {
1242 return -1;
1243 }
1244
1245 if(!lp_dos_filetimes(SNUM(conn))) {
1246 return -1;
1247 }
1248
1249 /* We have permission (given by the Samba admin) to
1250 break POSIX semantics and allow a user to change
1251 the time on a file they don't own but can write to
1252 (as DOS does).
1253 */
1254
1255 /* Check if we have write access. */
1256 if (can_write_to_file(conn, smb_fname)) {
1257 /* We are allowed to become root and change the filetime. */
1258 become_root();
1259 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1260 unbecome_root();
1261 }
1262
1263 return ret;
1264 }
1265
1266 /******************************************************************
1267 Force a "sticky" write time on a pathname. This will always be
1268 returned on all future write time queries and set on close.
1269 ******************************************************************/
1270
set_sticky_write_time_path(struct file_id fileid,struct timespec mtime)1271 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1272 {
1273 if (is_omit_timespec(&mtime)) {
1274 return true;
1275 }
1276
1277 if (!set_sticky_write_time(fileid, mtime)) {
1278 return false;
1279 }
1280
1281 return true;
1282 }
1283
1284 /******************************************************************
1285 Force a "sticky" write time on an fsp. This will always be
1286 returned on all future write time queries and set on close.
1287 ******************************************************************/
1288
set_sticky_write_time_fsp(struct files_struct * fsp,struct timespec mtime)1289 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1290 {
1291 if (is_omit_timespec(&mtime)) {
1292 return true;
1293 }
1294
1295 fsp->write_time_forced = true;
1296 TALLOC_FREE(fsp->update_write_time_event);
1297
1298 return set_sticky_write_time_path(fsp->file_id, mtime);
1299 }
1300
1301 /******************************************************************
1302 Set a create time EA.
1303 ******************************************************************/
1304
set_create_timespec_ea(connection_struct * conn,const struct smb_filename * psmb_fname,struct timespec create_time)1305 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1306 const struct smb_filename *psmb_fname,
1307 struct timespec create_time)
1308 {
1309 struct smb_filename *smb_fname;
1310 uint32_t dosmode;
1311 int ret;
1312
1313 if (!lp_store_dos_attributes(SNUM(conn))) {
1314 return NT_STATUS_OK;
1315 }
1316
1317 smb_fname = synthetic_smb_fname(talloc_tos(),
1318 psmb_fname->base_name,
1319 NULL,
1320 &psmb_fname->st,
1321 psmb_fname->flags);
1322
1323 if (smb_fname == NULL) {
1324 return NT_STATUS_NO_MEMORY;
1325 }
1326
1327 dosmode = dos_mode(conn, smb_fname);
1328
1329 smb_fname->st.st_ex_btime = create_time;
1330
1331 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1332 if (ret == -1) {
1333 return map_nt_error_from_unix(errno);
1334 }
1335
1336 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1337 smb_fname_str_dbg(smb_fname)));
1338
1339 return NT_STATUS_OK;
1340 }
1341
1342 /******************************************************************
1343 Return a create time.
1344 ******************************************************************/
1345
get_create_timespec(connection_struct * conn,struct files_struct * fsp,const struct smb_filename * smb_fname)1346 struct timespec get_create_timespec(connection_struct *conn,
1347 struct files_struct *fsp,
1348 const struct smb_filename *smb_fname)
1349 {
1350 return smb_fname->st.st_ex_btime;
1351 }
1352
1353 /******************************************************************
1354 Return a change time (may look at EA in future).
1355 ******************************************************************/
1356
get_change_timespec(connection_struct * conn,struct files_struct * fsp,const struct smb_filename * smb_fname)1357 struct timespec get_change_timespec(connection_struct *conn,
1358 struct files_struct *fsp,
1359 const struct smb_filename *smb_fname)
1360 {
1361 return smb_fname->st.st_ex_mtime;
1362 }
1363
1364 /****************************************************************************
1365 Get a real open file handle we can do meta-data operations on. As it's
1366 going to be used under root access only on meta-data we should look for
1367 any existing open file handle first, and use that in preference (also to
1368 avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1369 ****************************************************************************/
1370
get_file_handle_for_metadata(connection_struct * conn,const struct smb_filename * smb_fname,files_struct ** ret_fsp,bool * need_close)1371 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1372 const struct smb_filename *smb_fname,
1373 files_struct **ret_fsp,
1374 bool *need_close)
1375 {
1376 NTSTATUS status;
1377 files_struct *fsp;
1378 struct file_id file_id;
1379 struct smb_filename *smb_fname_cp = NULL;
1380
1381 *need_close = false;
1382
1383 if (!VALID_STAT(smb_fname->st)) {
1384 return NT_STATUS_INVALID_PARAMETER;
1385 }
1386
1387 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1388
1389 for(fsp = file_find_di_first(conn->sconn, file_id);
1390 fsp;
1391 fsp = file_find_di_next(fsp)) {
1392 if (fsp->fh->fd != -1) {
1393 *ret_fsp = fsp;
1394 return NT_STATUS_OK;
1395 }
1396 }
1397
1398 smb_fname_cp = cp_smb_filename(talloc_tos(),
1399 smb_fname);
1400 if (smb_fname_cp == NULL) {
1401 return NT_STATUS_NO_MEMORY;
1402 }
1403
1404 /* Opens an INTERNAL_OPEN_ONLY write handle. */
1405 status = SMB_VFS_CREATE_FILE(
1406 conn, /* conn */
1407 NULL, /* req */
1408 0, /* root_dir_fid */
1409 smb_fname_cp, /* fname */
1410 FILE_WRITE_ATTRIBUTES, /* access_mask */
1411 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1412 FILE_SHARE_DELETE),
1413 FILE_OPEN, /* create_disposition*/
1414 0, /* create_options */
1415 0, /* file_attributes */
1416 INTERNAL_OPEN_ONLY, /* oplock_request */
1417 NULL, /* lease */
1418 0, /* allocation_size */
1419 0, /* private_flags */
1420 NULL, /* sd */
1421 NULL, /* ea_list */
1422 ret_fsp, /* result */
1423 NULL, /* pinfo */
1424 NULL, NULL); /* create context */
1425
1426 TALLOC_FREE(smb_fname_cp);
1427
1428 if (NT_STATUS_IS_OK(status)) {
1429 *need_close = true;
1430 }
1431 return status;
1432 }
1433