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