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