s3: smbd: Add dirfsp parameter to can_write_to_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, smb_fname, false,
539                                                   FILE_WRITE_ATTRIBUTES);
540                 if (NT_STATUS_IS_OK(status)) {
541                         set_dosmode_ok = true;
542                 }
543
544                 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
545                         set_dosmode_ok = can_write_to_file(conn,
546                                                 conn->cwd_fsp,
547                                                 smb_fname);
548                 }
549
550                 if (!set_dosmode_ok) {
551                         return NT_STATUS_ACCESS_DENIED;
552                 }
553
554                 /*
555                  * We need to get an open file handle to do the
556                  * metadata operation under root.
557                  */
558
559                 status = get_file_handle_for_metadata(conn,
560                                                 smb_fname,
561                                                 &fsp,
562                                                 &need_close);
563                 if (!NT_STATUS_IS_OK(status)) {
564                         return status;
565                 }
566
567                 become_root();
568                 ret = SMB_VFS_FSETXATTR(fsp,
569                                         SAMBA_XATTR_DOS_ATTRIB,
570                                         blob.data, blob.length, 0);
571                 if (ret == 0) {
572                         status = NT_STATUS_OK;
573                 }
574                 unbecome_root();
575                 if (need_close) {
576                         close_file(NULL, fsp, NORMAL_CLOSE);
577                 }
578                 return status;
579         }
580         DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
581                 (unsigned int)dosmode,
582                 smb_fname_str_dbg(smb_fname)));
583         return NT_STATUS_OK;
584 }
585
586 /****************************************************************************
587  Change a unix mode to a dos mode for an ms dfs link.
588 ****************************************************************************/
589
590 uint32_t dos_mode_msdfs(connection_struct *conn,
591                       const struct smb_filename *smb_fname)
592 {
593         uint32_t result = 0;
594
595         DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
596
597         if (!VALID_STAT(smb_fname->st)) {
598                 return 0;
599         }
600
601         /* First do any modifications that depend on the path name. */
602         /* hide files with a name starting with a . */
603         if (lp_hide_dot_files(SNUM(conn))) {
604                 const char *p = strrchr_m(smb_fname->base_name, '/');
605                 if (p) {
606                         p++;
607                 } else {
608                         p = smb_fname->base_name;
609                 }
610
611                 /* Only . and .. are not hidden. */
612                 if (p[0] == '.' && !((p[1] == '\0') ||
613                                 (p[1] == '.' && p[2] == '\0'))) {
614                         result |= FILE_ATTRIBUTE_HIDDEN;
615                 }
616         }
617
618         result |= dos_mode_from_sbuf(conn, smb_fname);
619
620         /* Optimization : Only call is_hidden_path if it's not already
621            hidden. */
622         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
623             IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
624                 result |= FILE_ATTRIBUTE_HIDDEN;
625         }
626
627         if (result == 0) {
628                 result = FILE_ATTRIBUTE_NORMAL;
629         }
630
631         result = filter_mode_by_protocol(result);
632
633         /*
634          * Add in that it is a reparse point
635          */
636         result |= FILE_ATTRIBUTE_REPARSE_POINT;
637
638         dos_mode_debug_print(__func__, result);
639
640         return(result);
641 }
642
643 /*
644  * check whether a file or directory is flagged as compressed.
645  */
646 static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
647                                           struct smb_filename *smb_fname,
648                                           bool *is_compressed)
649 {
650         NTSTATUS status;
651         uint16_t compression_fmt;
652         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
653         if (tmp_ctx == NULL) {
654                 status = NT_STATUS_NO_MEMORY;
655                 goto err_out;
656         }
657
658         status = SMB_VFS_GET_COMPRESSION(conn, tmp_ctx, NULL, smb_fname,
659                                          &compression_fmt);
660         if (!NT_STATUS_IS_OK(status)) {
661                 goto err_ctx_free;
662         }
663
664         if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
665                 *is_compressed = true;
666         } else {
667                 *is_compressed = false;
668         }
669         status = NT_STATUS_OK;
670
671 err_ctx_free:
672         talloc_free(tmp_ctx);
673 err_out:
674         return status;
675 }
676
677 static uint32_t dos_mode_from_name(connection_struct *conn,
678                                    const struct smb_filename *smb_fname,
679                                    uint32_t dosmode)
680 {
681         const char *p = NULL;
682         uint32_t result = dosmode;
683
684         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
685             lp_hide_dot_files(SNUM(conn)))
686         {
687                 p = strrchr_m(smb_fname->base_name, '/');
688                 if (p) {
689                         p++;
690                 } else {
691                         p = smb_fname->base_name;
692                 }
693
694                 /* Only . and .. are not hidden. */
695                 if ((p[0] == '.') &&
696                     !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0')))
697                 {
698                         result |= FILE_ATTRIBUTE_HIDDEN;
699                 }
700         }
701
702         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
703             IS_HIDDEN_PATH(conn, smb_fname->base_name))
704         {
705                 result |= FILE_ATTRIBUTE_HIDDEN;
706         }
707
708         return result;
709 }
710
711 static uint32_t dos_mode_post(uint32_t dosmode,
712                               connection_struct *conn,
713                               struct smb_filename *smb_fname,
714                               const char *func)
715 {
716         NTSTATUS status;
717
718         /*
719          * According to MS-FSA a stream name does not have
720          * separate DOS attribute metadata, so we must return
721          * the DOS attribute from the base filename. With one caveat,
722          * a non-default stream name can never be a directory.
723          *
724          * As this is common to all streams data stores, we handle
725          * it here instead of inside all stream VFS modules.
726          *
727          * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
728          */
729
730         if (is_named_stream(smb_fname)) {
731                 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
732                 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
733         }
734
735         if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
736                 bool compressed = false;
737
738                 status = dos_mode_check_compressed(conn, smb_fname,
739                                                    &compressed);
740                 if (NT_STATUS_IS_OK(status) && compressed) {
741                         dosmode |= FILE_ATTRIBUTE_COMPRESSED;
742                 }
743         }
744
745         dosmode |= dos_mode_from_name(conn, smb_fname, dosmode);
746
747         if (S_ISDIR(smb_fname->st.st_ex_mode)) {
748                 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
749         } else if (dosmode == 0) {
750                 dosmode = FILE_ATTRIBUTE_NORMAL;
751         }
752
753         dosmode = filter_mode_by_protocol(dosmode);
754
755         dos_mode_debug_print(func, dosmode);
756         return dosmode;
757 }
758
759 /****************************************************************************
760  Change a unix mode to a dos mode.
761  May also read the create timespec into the stat struct in smb_fname
762  if "store dos attributes" is true.
763 ****************************************************************************/
764
765 uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
766 {
767         uint32_t result = 0;
768         NTSTATUS status = NT_STATUS_OK;
769
770         DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
771
772         if (!VALID_STAT(smb_fname->st)) {
773                 return 0;
774         }
775
776         /* Get the DOS attributes via the VFS if we can */
777         status = SMB_VFS_GET_DOS_ATTRIBUTES(conn, smb_fname, &result);
778         if (!NT_STATUS_IS_OK(status)) {
779                 /*
780                  * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
781                  */
782                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
783                         result |= dos_mode_from_sbuf(conn, smb_fname);
784                 }
785         }
786
787         result = dos_mode_post(result, conn, smb_fname, __func__);
788         return result;
789 }
790
791 struct dos_mode_at_state {
792         files_struct *dir_fsp;
793         struct smb_filename *smb_fname;
794         uint32_t dosmode;
795 };
796
797 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
798
799 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
800                                     struct tevent_context *ev,
801                                     files_struct *dir_fsp,
802                                     struct smb_filename *smb_fname)
803 {
804         struct tevent_req *req = NULL;
805         struct dos_mode_at_state *state = NULL;
806         struct tevent_req *subreq = NULL;
807
808         DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
809
810         req = tevent_req_create(mem_ctx, &state,
811                                 struct dos_mode_at_state);
812         if (req == NULL) {
813                 return NULL;
814         }
815
816         *state = (struct dos_mode_at_state) {
817                 .dir_fsp = dir_fsp,
818                 .smb_fname = smb_fname,
819         };
820
821         if (!VALID_STAT(smb_fname->st)) {
822                 tevent_req_done(req);
823                 return tevent_req_post(req, ev);
824         }
825
826         subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
827                                                  ev,
828                                                  dir_fsp,
829                                                  smb_fname);
830         if (tevent_req_nomem(subreq, req)) {
831                 return tevent_req_post(req, ev);
832         }
833         tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
834
835         return req;
836 }
837
838 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
839 {
840         struct tevent_req *req =
841                 tevent_req_callback_data(subreq,
842                 struct tevent_req);
843         struct dos_mode_at_state *state =
844                 tevent_req_data(req,
845                 struct dos_mode_at_state);
846         char *path = NULL;
847         struct smb_filename *smb_path = NULL;
848         struct vfs_aio_state aio_state;
849         NTSTATUS status;
850         bool ok;
851
852         /*
853          * Make sure we run as the user again
854          */
855         ok = change_to_user_and_service_by_fsp(state->dir_fsp);
856         SMB_ASSERT(ok);
857
858         status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
859                                                  &aio_state,
860                                                  &state->dosmode);
861         TALLOC_FREE(subreq);
862         if (!NT_STATUS_IS_OK(status)) {
863                 /*
864                  * Both the sync dos_mode() as well as the async
865                  * dos_mode_at_[send|recv] have no real error return, the only
866                  * unhandled error is when the stat info in smb_fname is not
867                  * valid (cf the checks in dos_mode() and dos_mode_at_send().
868                  *
869                  * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
870                  * dos_mode_post() which also does the mapping of a last ressort
871                  * from S_IFMT(st_mode).
872                  *
873                  * Only if we get NT_STATUS_NOT_IMPLEMENTED from a stacked VFS
874                  * module we must fallback to sync processing.
875                  */
876                 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
877                         /*
878                          * state->dosmode should still be 0, but reset
879                          * it to be sure.
880                          */
881                         state->dosmode = 0;
882                         status = NT_STATUS_OK;
883                 }
884         }
885         if (NT_STATUS_IS_OK(status)) {
886                 state->dosmode = dos_mode_post(state->dosmode,
887                                                state->dir_fsp->conn,
888                                                state->smb_fname,
889                                                __func__);
890                 tevent_req_done(req);
891                 return;
892         }
893
894         /*
895          * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
896          */
897
898         path = talloc_asprintf(state,
899                                "%s/%s",
900                                state->dir_fsp->fsp_name->base_name,
901                                state->smb_fname->base_name);
902         if (tevent_req_nomem(path, req)) {
903                 return;
904         }
905
906         smb_path = synthetic_smb_fname(state,
907                                        path,
908                                        NULL,
909                                        &state->smb_fname->st,
910                                        0);
911         if (tevent_req_nomem(smb_path, req)) {
912                 return;
913         }
914
915         state->dosmode = dos_mode(state->dir_fsp->conn, smb_path);
916         tevent_req_done(req);
917         return;
918 }
919
920 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
921 {
922         struct dos_mode_at_state *state =
923                 tevent_req_data(req,
924                 struct dos_mode_at_state);
925         NTSTATUS status;
926
927         if (tevent_req_is_nterror(req, &status)) {
928                 tevent_req_received(req);
929                 return status;
930         }
931
932         *dosmode = state->dosmode;
933         tevent_req_received(req);
934         return NT_STATUS_OK;
935 }
936
937 /*******************************************************************
938  chmod a file - but preserve some bits.
939  If "store dos attributes" is also set it will store the create time
940  from the stat struct in smb_fname (in NTTIME format) in the EA
941  attribute also.
942 ********************************************************************/
943
944 int file_set_dosmode(connection_struct *conn,
945                      struct smb_filename *smb_fname,
946                      uint32_t dosmode,
947                      struct smb_filename *parent_dir,
948                      bool newfile)
949 {
950         int mask=0;
951         mode_t tmp;
952         mode_t unixmode;
953         int ret = -1, lret = -1;
954         files_struct *fsp = NULL;
955         bool need_close = false;
956         NTSTATUS status;
957
958         if (!CAN_WRITE(conn)) {
959                 errno = EROFS;
960                 return -1;
961         }
962
963         dosmode &= SAMBA_ATTRIBUTES_MASK;
964
965         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
966                   dosmode, smb_fname_str_dbg(smb_fname)));
967
968         unixmode = smb_fname->st.st_ex_mode;
969
970         get_acl_group_bits(conn, smb_fname,
971                         &smb_fname->st.st_ex_mode);
972
973         if (S_ISDIR(smb_fname->st.st_ex_mode))
974                 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
975         else
976                 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
977
978         /* Store the DOS attributes in an EA by preference. */
979         status = SMB_VFS_SET_DOS_ATTRIBUTES(conn, smb_fname, dosmode);
980         if (NT_STATUS_IS_OK(status)) {
981                 if (!newfile) {
982                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
983                                 FILE_NOTIFY_CHANGE_ATTRIBUTES,
984                                 smb_fname->base_name);
985                 }
986                 smb_fname->st.st_ex_mode = unixmode;
987                 return 0;
988         } else {
989                 /*
990                  * Only fall back to using UNIX modes if
991                  * we get NOT_IMPLEMENTED.
992                  */
993                 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
994                         errno = map_errno_from_nt_status(status);
995                         return -1;
996                 }
997         }
998
999         /* Fall back to UNIX modes. */
1000         unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
1001
1002         /* preserve the file type bits */
1003         mask |= S_IFMT;
1004
1005         /* preserve the s bits */
1006         mask |= (S_ISUID | S_ISGID);
1007
1008         /* preserve the t bit */
1009 #ifdef S_ISVTX
1010         mask |= S_ISVTX;
1011 #endif
1012
1013         /* possibly preserve the x bits */
1014         if (!MAP_ARCHIVE(conn))
1015                 mask |= S_IXUSR;
1016         if (!MAP_SYSTEM(conn))
1017                 mask |= S_IXGRP;
1018         if (!MAP_HIDDEN(conn))
1019                 mask |= S_IXOTH;
1020
1021         unixmode |= (smb_fname->st.st_ex_mode & mask);
1022
1023         /* if we previously had any r bits set then leave them alone */
1024         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
1025                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
1026                 unixmode |= tmp;
1027         }
1028
1029         /* if we previously had any w bits set then leave them alone 
1030                 whilst adding in the new w bits, if the new mode is not rdonly */
1031         if (!IS_DOS_READONLY(dosmode)) {
1032                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
1033         }
1034
1035         /*
1036          * From the chmod 2 man page:
1037          *
1038          * "If the calling process is not privileged, and the group of the file
1039          * does not match the effective group ID of the process or one of its
1040          * supplementary group IDs, the S_ISGID bit will be turned off, but
1041          * this will not cause an error to be returned."
1042          *
1043          * Simply refuse to do the chmod in this case.
1044          */
1045
1046         if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
1047                         geteuid() != sec_initial_uid() &&
1048                         !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
1049                 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1050                         "set for directory %s\n",
1051                         smb_fname_str_dbg(smb_fname)));
1052                 errno = EPERM;
1053                 return -1;
1054         }
1055
1056         ret = SMB_VFS_CHMOD(conn, smb_fname, unixmode);
1057         if (ret == 0) {
1058                 if(!newfile || (lret != -1)) {
1059                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1060                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
1061                                      smb_fname->base_name);
1062                 }
1063                 smb_fname->st.st_ex_mode = unixmode;
1064                 return 0;
1065         }
1066
1067         if((errno != EPERM) && (errno != EACCES))
1068                 return -1;
1069
1070         if(!lp_dos_filemode(SNUM(conn)))
1071                 return -1;
1072
1073         /* We want DOS semantics, ie allow non owner with write permission to change the
1074                 bits on a file. Just like file_ntimes below.
1075         */
1076
1077         if (!can_write_to_file(conn,
1078                         conn->cwd_fsp,
1079                         smb_fname))
1080         {
1081                 errno = EACCES;
1082                 return -1;
1083         }
1084
1085         /*
1086          * We need to get an open file handle to do the
1087          * metadata operation under root.
1088          */
1089
1090         status = get_file_handle_for_metadata(conn,
1091                                               smb_fname,
1092                                               &fsp,
1093                                               &need_close);
1094         if (!NT_STATUS_IS_OK(status)) {
1095                 errno = map_errno_from_nt_status(status);
1096                 return -1;
1097         }
1098
1099         become_root();
1100         ret = SMB_VFS_FCHMOD(fsp, unixmode);
1101         unbecome_root();
1102         if (need_close) {
1103                 close_file(NULL, fsp, NORMAL_CLOSE);
1104         }
1105         if (!newfile) {
1106                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1107                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
1108                              smb_fname->base_name);
1109         }
1110         if (ret == 0) {
1111                 smb_fname->st.st_ex_mode = unixmode;
1112         }
1113
1114         return( ret );
1115 }
1116
1117
1118 NTSTATUS file_set_sparse(connection_struct *conn,
1119                          files_struct *fsp,
1120                          bool sparse)
1121 {
1122         const struct loadparm_substitution *lp_sub =
1123                 loadparm_s3_global_substitution();
1124         uint32_t old_dosmode;
1125         uint32_t new_dosmode;
1126         NTSTATUS status;
1127
1128         if (!CAN_WRITE(conn)) {
1129                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1130                         "on readonly share[%s]\n",
1131                         smb_fname_str_dbg(fsp->fsp_name),
1132                         sparse,
1133                         lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1134                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1135         }
1136
1137         /*
1138          * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1139          * following access flags are granted.
1140          */
1141         if ((fsp->access_mask & (FILE_WRITE_DATA
1142                                 | FILE_WRITE_ATTRIBUTES
1143                                 | SEC_FILE_APPEND_DATA)) == 0) {
1144                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1145                         "access_mask[0x%08X] - access denied\n",
1146                         smb_fname_str_dbg(fsp->fsp_name),
1147                         sparse,
1148                         fsp->access_mask));
1149                 return NT_STATUS_ACCESS_DENIED;
1150         }
1151
1152         if (fsp->fsp_flags.is_directory) {
1153                 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1154                           (sparse ? "set" : "clear"),
1155                           smb_fname_str_dbg(fsp->fsp_name)));
1156                 return NT_STATUS_INVALID_PARAMETER;
1157         }
1158
1159         if (IS_IPC(conn) || IS_PRINT(conn)) {
1160                 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1161                           (sparse ? "set" : "clear")));
1162                 return NT_STATUS_INVALID_PARAMETER;
1163         }
1164
1165         DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1166                   sparse, smb_fname_str_dbg(fsp->fsp_name)));
1167
1168         if (!lp_store_dos_attributes(SNUM(conn))) {
1169                 return NT_STATUS_INVALID_DEVICE_REQUEST;
1170         }
1171
1172         status = vfs_stat_fsp(fsp);
1173         if (!NT_STATUS_IS_OK(status)) {
1174                 return status;
1175         }
1176
1177         old_dosmode = dos_mode(conn, fsp->fsp_name);
1178
1179         if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1180                 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1181         } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1182                 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1183         } else {
1184                 return NT_STATUS_OK;
1185         }
1186
1187         /* Store the DOS attributes in an EA. */
1188         status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1189         if (!NT_STATUS_IS_OK(status)) {
1190                 return status;
1191         }
1192
1193         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1194                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
1195                      fsp->fsp_name->base_name);
1196
1197         fsp->fsp_flags.is_sparse = sparse;
1198
1199         return NT_STATUS_OK;
1200 }
1201
1202 /*******************************************************************
1203  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1204  than POSIX.
1205 *******************************************************************/
1206
1207 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
1208                 struct smb_file_time *ft)
1209 {
1210         int ret = -1;
1211
1212         errno = 0;
1213
1214         DEBUG(6, ("file_ntime: actime: %s",
1215                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
1216         DEBUG(6, ("file_ntime: modtime: %s",
1217                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
1218         DEBUG(6, ("file_ntime: ctime: %s",
1219                   time_to_asc(convert_timespec_to_time_t(ft->ctime))));
1220         DEBUG(6, ("file_ntime: createtime: %s",
1221                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
1222
1223         /* Don't update the time on read-only shares */
1224         /* We need this as set_filetime (which can be called on
1225            close and other paths) can end up calling this function
1226            without the NEED_WRITE protection. Found by : 
1227            Leo Weppelman <leo@wau.mis.ah.nl>
1228         */
1229
1230         if (!CAN_WRITE(conn)) {
1231                 return 0;
1232         }
1233
1234         if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
1235                 return 0;
1236         }
1237
1238         if((errno != EPERM) && (errno != EACCES)) {
1239                 return -1;
1240         }
1241
1242         if(!lp_dos_filetimes(SNUM(conn))) {
1243                 return -1;
1244         }
1245
1246         /* We have permission (given by the Samba admin) to
1247            break POSIX semantics and allow a user to change
1248            the time on a file they don't own but can write to
1249            (as DOS does).
1250          */
1251
1252         /* Check if we have write access. */
1253         if (can_write_to_file(conn,
1254                         conn->cwd_fsp,
1255                         smb_fname))
1256         {
1257                 /* We are allowed to become root and change the filetime. */
1258                 become_root();
1259                 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1260                 unbecome_root();
1261         }
1262
1263         return ret;
1264 }
1265
1266 /******************************************************************
1267  Force a "sticky" write time on a pathname. This will always be
1268  returned on all future write time queries and set on close.
1269 ******************************************************************/
1270
1271 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1272 {
1273         if (is_omit_timespec(&mtime)) {
1274                 return true;
1275         }
1276
1277         if (!set_sticky_write_time(fileid, mtime)) {
1278                 return false;
1279         }
1280
1281         return true;
1282 }
1283
1284 /******************************************************************
1285  Force a "sticky" write time on an fsp. This will always be
1286  returned on all future write time queries and set on close.
1287 ******************************************************************/
1288
1289 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1290 {
1291         if (is_omit_timespec(&mtime)) {
1292                 return true;
1293         }
1294
1295         fsp->fsp_flags.write_time_forced = true;
1296         TALLOC_FREE(fsp->update_write_time_event);
1297
1298         return set_sticky_write_time_path(fsp->file_id, mtime);
1299 }
1300
1301 /******************************************************************
1302  Set a create time EA.
1303 ******************************************************************/
1304
1305 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1306                                 const struct smb_filename *psmb_fname,
1307                                 struct timespec create_time)
1308 {
1309         struct smb_filename *smb_fname;
1310         uint32_t dosmode;
1311         int ret;
1312
1313         if (!lp_store_dos_attributes(SNUM(conn))) {
1314                 return NT_STATUS_OK;
1315         }
1316
1317         smb_fname = synthetic_smb_fname(talloc_tos(),
1318                                         psmb_fname->base_name,
1319                                         NULL,
1320                                         &psmb_fname->st,
1321                                         psmb_fname->flags);
1322
1323         if (smb_fname == NULL) {
1324                 return NT_STATUS_NO_MEMORY;
1325         }
1326
1327         dosmode = dos_mode(conn, smb_fname);
1328
1329         smb_fname->st.st_ex_btime = create_time;
1330
1331         ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1332         if (ret == -1) {
1333                 return map_nt_error_from_unix(errno);
1334         }
1335
1336         DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1337                 smb_fname_str_dbg(smb_fname)));
1338
1339         return NT_STATUS_OK;
1340 }
1341
1342 /******************************************************************
1343  Return a create time.
1344 ******************************************************************/
1345
1346 struct timespec get_create_timespec(connection_struct *conn,
1347                                 struct files_struct *fsp,
1348                                 const struct smb_filename *smb_fname)
1349 {
1350         return smb_fname->st.st_ex_btime;
1351 }
1352
1353 /******************************************************************
1354  Return a change time (may look at EA in future).
1355 ******************************************************************/
1356
1357 struct timespec get_change_timespec(connection_struct *conn,
1358                                 struct files_struct *fsp,
1359                                 const struct smb_filename *smb_fname)
1360 {
1361         return smb_fname->st.st_ex_mtime;
1362 }
1363
1364 /****************************************************************************
1365  Get a real open file handle we can do meta-data operations on. As it's
1366  going to be used under root access only on meta-data we should look for
1367  any existing open file handle first, and use that in preference (also to
1368  avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1369 ****************************************************************************/
1370
1371 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1372                                 const struct smb_filename *smb_fname,
1373                                 files_struct **ret_fsp,
1374                                 bool *need_close)
1375 {
1376         NTSTATUS status;
1377         files_struct *fsp;
1378         struct file_id file_id;
1379         struct smb_filename *smb_fname_cp = NULL;
1380
1381         *need_close = false;
1382
1383         if (!VALID_STAT(smb_fname->st)) {
1384                 return NT_STATUS_INVALID_PARAMETER;
1385         }
1386
1387         file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1388
1389         for(fsp = file_find_di_first(conn->sconn, file_id);
1390                         fsp;
1391                         fsp = file_find_di_next(fsp)) {
1392                 if (fsp->fh->fd != -1) {
1393                         *ret_fsp = fsp;
1394                         return NT_STATUS_OK;
1395                 }
1396         }
1397
1398         smb_fname_cp = cp_smb_filename(talloc_tos(),
1399                                         smb_fname);
1400         if (smb_fname_cp == NULL) {
1401                 return NT_STATUS_NO_MEMORY;
1402         }
1403
1404         /* Opens an INTERNAL_OPEN_ONLY write handle. */
1405         status = SMB_VFS_CREATE_FILE(
1406                 conn,                                   /* conn */
1407                 NULL,                                   /* req */
1408                 0,                                      /* root_dir_fid */
1409                 smb_fname_cp,                           /* fname */
1410                 FILE_WRITE_ATTRIBUTES,                  /* access_mask */
1411                 (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
1412                         FILE_SHARE_DELETE),
1413                 FILE_OPEN,                              /* create_disposition*/
1414                 0,                                      /* create_options */
1415                 0,                                      /* file_attributes */
1416                 INTERNAL_OPEN_ONLY,                     /* oplock_request */
1417                 NULL,                                   /* lease */
1418                 0,                                      /* allocation_size */
1419                 0,                                      /* private_flags */
1420                 NULL,                                   /* sd */
1421                 NULL,                                   /* ea_list */
1422                 ret_fsp,                                /* result */
1423                 NULL,                                   /* pinfo */
1424                 NULL, NULL);                            /* create context */
1425
1426         TALLOC_FREE(smb_fname_cp);
1427
1428         if (NT_STATUS_IS_OK(status)) {
1429                 *need_close = true;
1430         }
1431         return status;
1432 }