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