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