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