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