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