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