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