lib/krb5_wrap: Fix missing error check in new salt code
[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_unix_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_unix_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 = unix_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 = unix_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, struct smb_filename *smb_fname,
954                      uint32_t dosmode, const char *parent_dir, bool newfile)
955 {
956         int mask=0;
957         mode_t tmp;
958         mode_t unixmode;
959         int ret = -1, lret = -1;
960         files_struct *fsp = NULL;
961         bool need_close = false;
962         NTSTATUS status;
963
964         if (!CAN_WRITE(conn)) {
965                 errno = EROFS;
966                 return -1;
967         }
968
969         dosmode &= SAMBA_ATTRIBUTES_MASK;
970
971         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
972                   dosmode, smb_fname_str_dbg(smb_fname)));
973
974         unixmode = smb_fname->st.st_ex_mode;
975
976         get_acl_group_bits(conn, smb_fname,
977                         &smb_fname->st.st_ex_mode);
978
979         if (S_ISDIR(smb_fname->st.st_ex_mode))
980                 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
981         else
982                 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
983
984         /* Store the DOS attributes in an EA by preference. */
985         status = SMB_VFS_SET_DOS_ATTRIBUTES(conn, smb_fname, dosmode);
986         if (NT_STATUS_IS_OK(status)) {
987                 if (!newfile) {
988                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
989                                 FILE_NOTIFY_CHANGE_ATTRIBUTES,
990                                 smb_fname->base_name);
991                 }
992                 smb_fname->st.st_ex_mode = unixmode;
993                 return 0;
994         } else {
995                 /*
996                  * Only fall back to using UNIX modes if
997                  * we get NOT_IMPLEMENTED.
998                  */
999                 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
1000                         errno = map_errno_from_nt_status(status);
1001                         return -1;
1002                 }
1003         }
1004
1005         /* Fall back to UNIX modes. */
1006         unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
1007
1008         /* preserve the file type bits */
1009         mask |= S_IFMT;
1010
1011         /* preserve the s bits */
1012         mask |= (S_ISUID | S_ISGID);
1013
1014         /* preserve the t bit */
1015 #ifdef S_ISVTX
1016         mask |= S_ISVTX;
1017 #endif
1018
1019         /* possibly preserve the x bits */
1020         if (!MAP_ARCHIVE(conn))
1021                 mask |= S_IXUSR;
1022         if (!MAP_SYSTEM(conn))
1023                 mask |= S_IXGRP;
1024         if (!MAP_HIDDEN(conn))
1025                 mask |= S_IXOTH;
1026
1027         unixmode |= (smb_fname->st.st_ex_mode & mask);
1028
1029         /* if we previously had any r bits set then leave them alone */
1030         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
1031                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
1032                 unixmode |= tmp;
1033         }
1034
1035         /* if we previously had any w bits set then leave them alone 
1036                 whilst adding in the new w bits, if the new mode is not rdonly */
1037         if (!IS_DOS_READONLY(dosmode)) {
1038                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
1039         }
1040
1041         /*
1042          * From the chmod 2 man page:
1043          *
1044          * "If the calling process is not privileged, and the group of the file
1045          * does not match the effective group ID of the process or one of its
1046          * supplementary group IDs, the S_ISGID bit will be turned off, but
1047          * this will not cause an error to be returned."
1048          *
1049          * Simply refuse to do the chmod in this case.
1050          */
1051
1052         if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
1053                         geteuid() != sec_initial_uid() &&
1054                         !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
1055                 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1056                         "set for directory %s\n",
1057                         smb_fname_str_dbg(smb_fname)));
1058                 errno = EPERM;
1059                 return -1;
1060         }
1061
1062         ret = SMB_VFS_CHMOD(conn, smb_fname, unixmode);
1063         if (ret == 0) {
1064                 if(!newfile || (lret != -1)) {
1065                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1066                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
1067                                      smb_fname->base_name);
1068                 }
1069                 smb_fname->st.st_ex_mode = unixmode;
1070                 return 0;
1071         }
1072
1073         if((errno != EPERM) && (errno != EACCES))
1074                 return -1;
1075
1076         if(!lp_dos_filemode(SNUM(conn)))
1077                 return -1;
1078
1079         /* We want DOS semantics, ie allow non owner with write permission to change the
1080                 bits on a file. Just like file_ntimes below.
1081         */
1082
1083         if (!can_write_to_file(conn, smb_fname)) {
1084                 errno = EACCES;
1085                 return -1;
1086         }
1087
1088         /*
1089          * We need to get an open file handle to do the
1090          * metadata operation under root.
1091          */
1092
1093         status = get_file_handle_for_metadata(conn,
1094                                               smb_fname,
1095                                               &fsp,
1096                                               &need_close);
1097         if (!NT_STATUS_IS_OK(status)) {
1098                 errno = map_errno_from_nt_status(status);
1099                 return -1;
1100         }
1101
1102         become_root();
1103         ret = SMB_VFS_FCHMOD(fsp, unixmode);
1104         unbecome_root();
1105         if (need_close) {
1106                 close_file(NULL, fsp, NORMAL_CLOSE);
1107         }
1108         if (!newfile) {
1109                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1110                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
1111                              smb_fname->base_name);
1112         }
1113         if (ret == 0) {
1114                 smb_fname->st.st_ex_mode = unixmode;
1115         }
1116
1117         return( ret );
1118 }
1119
1120
1121 NTSTATUS file_set_sparse(connection_struct *conn,
1122                          files_struct *fsp,
1123                          bool sparse)
1124 {
1125         uint32_t old_dosmode;
1126         uint32_t new_dosmode;
1127         NTSTATUS status;
1128
1129         if (!CAN_WRITE(conn)) {
1130                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1131                         "on readonly share[%s]\n",
1132                         smb_fname_str_dbg(fsp->fsp_name),
1133                         sparse,
1134                         lp_servicename(talloc_tos(), SNUM(conn))));
1135                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1136         }
1137
1138         /*
1139          * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1140          * following access flags are granted.
1141          */
1142         if ((fsp->access_mask & (FILE_WRITE_DATA
1143                                 | FILE_WRITE_ATTRIBUTES
1144                                 | SEC_FILE_APPEND_DATA)) == 0) {
1145                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1146                         "access_mask[0x%08X] - access denied\n",
1147                         smb_fname_str_dbg(fsp->fsp_name),
1148                         sparse,
1149                         fsp->access_mask));
1150                 return NT_STATUS_ACCESS_DENIED;
1151         }
1152
1153         if (fsp->is_directory) {
1154                 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1155                           (sparse ? "set" : "clear"),
1156                           smb_fname_str_dbg(fsp->fsp_name)));
1157                 return NT_STATUS_INVALID_PARAMETER;
1158         }
1159
1160         if (IS_IPC(conn) || IS_PRINT(conn)) {
1161                 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1162                           (sparse ? "set" : "clear")));
1163                 return NT_STATUS_INVALID_PARAMETER;
1164         }
1165
1166         DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1167                   sparse, smb_fname_str_dbg(fsp->fsp_name)));
1168
1169         if (!lp_store_dos_attributes(SNUM(conn))) {
1170                 return NT_STATUS_INVALID_DEVICE_REQUEST;
1171         }
1172
1173         status = vfs_stat_fsp(fsp);
1174         if (!NT_STATUS_IS_OK(status)) {
1175                 return status;
1176         }
1177
1178         old_dosmode = dos_mode(conn, fsp->fsp_name);
1179
1180         if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1181                 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1182         } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1183                 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1184         } else {
1185                 return NT_STATUS_OK;
1186         }
1187
1188         /* Store the DOS attributes in an EA. */
1189         status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1190         if (!NT_STATUS_IS_OK(status)) {
1191                 return status;
1192         }
1193
1194         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1195                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
1196                      fsp->fsp_name->base_name);
1197
1198         fsp->is_sparse = sparse;
1199
1200         return NT_STATUS_OK;
1201 }
1202
1203 /*******************************************************************
1204  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1205  than POSIX.
1206 *******************************************************************/
1207
1208 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
1209                 struct smb_file_time *ft)
1210 {
1211         int ret = -1;
1212
1213         errno = 0;
1214
1215         DEBUG(6, ("file_ntime: actime: %s",
1216                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
1217         DEBUG(6, ("file_ntime: modtime: %s",
1218                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
1219         DEBUG(6, ("file_ntime: ctime: %s",
1220                   time_to_asc(convert_timespec_to_time_t(ft->ctime))));
1221         DEBUG(6, ("file_ntime: createtime: %s",
1222                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
1223
1224         /* Don't update the time on read-only shares */
1225         /* We need this as set_filetime (which can be called on
1226            close and other paths) can end up calling this function
1227            without the NEED_WRITE protection. Found by : 
1228            Leo Weppelman <leo@wau.mis.ah.nl>
1229         */
1230
1231         if (!CAN_WRITE(conn)) {
1232                 return 0;
1233         }
1234
1235         if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
1236                 return 0;
1237         }
1238
1239         if((errno != EPERM) && (errno != EACCES)) {
1240                 return -1;
1241         }
1242
1243         if(!lp_dos_filetimes(SNUM(conn))) {
1244                 return -1;
1245         }
1246
1247         /* We have permission (given by the Samba admin) to
1248            break POSIX semantics and allow a user to change
1249            the time on a file they don't own but can write to
1250            (as DOS does).
1251          */
1252
1253         /* Check if we have write access. */
1254         if (can_write_to_file(conn, smb_fname)) {
1255                 /* We are allowed to become root and change the filetime. */
1256                 become_root();
1257                 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1258                 unbecome_root();
1259         }
1260
1261         return ret;
1262 }
1263
1264 /******************************************************************
1265  Force a "sticky" write time on a pathname. This will always be
1266  returned on all future write time queries and set on close.
1267 ******************************************************************/
1268
1269 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1270 {
1271         if (null_timespec(mtime)) {
1272                 return true;
1273         }
1274
1275         if (!set_sticky_write_time(fileid, mtime)) {
1276                 return false;
1277         }
1278
1279         return true;
1280 }
1281
1282 /******************************************************************
1283  Force a "sticky" write time on an fsp. This will always be
1284  returned on all future write time queries and set on close.
1285 ******************************************************************/
1286
1287 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1288 {
1289         if (null_timespec(mtime)) {
1290                 return true;
1291         }
1292
1293         fsp->write_time_forced = true;
1294         TALLOC_FREE(fsp->update_write_time_event);
1295
1296         return set_sticky_write_time_path(fsp->file_id, mtime);
1297 }
1298
1299 /******************************************************************
1300  Set a create time EA.
1301 ******************************************************************/
1302
1303 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1304                                 const struct smb_filename *psmb_fname,
1305                                 struct timespec create_time)
1306 {
1307         struct smb_filename *smb_fname;
1308         uint32_t dosmode;
1309         int ret;
1310
1311         if (!lp_store_dos_attributes(SNUM(conn))) {
1312                 return NT_STATUS_OK;
1313         }
1314
1315         smb_fname = synthetic_smb_fname(talloc_tos(),
1316                                         psmb_fname->base_name,
1317                                         NULL,
1318                                         &psmb_fname->st,
1319                                         psmb_fname->flags);
1320
1321         if (smb_fname == NULL) {
1322                 return NT_STATUS_NO_MEMORY;
1323         }
1324
1325         dosmode = dos_mode(conn, smb_fname);
1326
1327         smb_fname->st.st_ex_btime = create_time;
1328
1329         ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1330         if (ret == -1) {
1331                 return map_nt_error_from_unix(errno);
1332         }
1333
1334         DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1335                 smb_fname_str_dbg(smb_fname)));
1336
1337         return NT_STATUS_OK;
1338 }
1339
1340 /******************************************************************
1341  Return a create time.
1342 ******************************************************************/
1343
1344 struct timespec get_create_timespec(connection_struct *conn,
1345                                 struct files_struct *fsp,
1346                                 const struct smb_filename *smb_fname)
1347 {
1348         return smb_fname->st.st_ex_btime;
1349 }
1350
1351 /******************************************************************
1352  Return a change time (may look at EA in future).
1353 ******************************************************************/
1354
1355 struct timespec get_change_timespec(connection_struct *conn,
1356                                 struct files_struct *fsp,
1357                                 const struct smb_filename *smb_fname)
1358 {
1359         return smb_fname->st.st_ex_mtime;
1360 }
1361
1362 /****************************************************************************
1363  Get a real open file handle we can do meta-data operations on. As it's
1364  going to be used under root access only on meta-data we should look for
1365  any existing open file handle first, and use that in preference (also to
1366  avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1367 ****************************************************************************/
1368
1369 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1370                                 const struct smb_filename *smb_fname,
1371                                 files_struct **ret_fsp,
1372                                 bool *need_close)
1373 {
1374         NTSTATUS status;
1375         files_struct *fsp;
1376         struct file_id file_id;
1377         struct smb_filename *smb_fname_cp = NULL;
1378
1379         *need_close = false;
1380
1381         if (!VALID_STAT(smb_fname->st)) {
1382                 return NT_STATUS_INVALID_PARAMETER;
1383         }
1384
1385         file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1386
1387         for(fsp = file_find_di_first(conn->sconn, file_id);
1388                         fsp;
1389                         fsp = file_find_di_next(fsp)) {
1390                 if (fsp->fh->fd != -1) {
1391                         *ret_fsp = fsp;
1392                         return NT_STATUS_OK;
1393                 }
1394         }
1395
1396         smb_fname_cp = cp_smb_filename(talloc_tos(),
1397                                         smb_fname);
1398         if (smb_fname_cp == NULL) {
1399                 return NT_STATUS_NO_MEMORY;
1400         }
1401
1402         /* Opens an INTERNAL_OPEN_ONLY write handle. */
1403         status = SMB_VFS_CREATE_FILE(
1404                 conn,                                   /* conn */
1405                 NULL,                                   /* req */
1406                 0,                                      /* root_dir_fid */
1407                 smb_fname_cp,                           /* fname */
1408                 FILE_WRITE_ATTRIBUTES,                  /* access_mask */
1409                 (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
1410                         FILE_SHARE_DELETE),
1411                 FILE_OPEN,                              /* create_disposition*/
1412                 0,                                      /* create_options */
1413                 0,                                      /* file_attributes */
1414                 INTERNAL_OPEN_ONLY,                     /* oplock_request */
1415                 NULL,                                   /* lease */
1416                 0,                                      /* allocation_size */
1417                 0,                                      /* private_flags */
1418                 NULL,                                   /* sd */
1419                 NULL,                                   /* ea_list */
1420                 ret_fsp,                                /* result */
1421                 NULL,                                   /* pinfo */
1422                 NULL, NULL);                            /* create context */
1423
1424         TALLOC_FREE(smb_fname_cp);
1425
1426         if (NT_STATUS_IS_OK(status)) {
1427                 *need_close = true;
1428         }
1429         return status;
1430 }