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