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