s3:smbd: perform impersonation in dos_mode_at_vfs_get_dosmode_done()
[amitay/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         bool ok;
815
816         /*
817          * Make sure we run as the user again
818          */
819         ok = change_to_user_by_fsp(state->dir_fsp);
820         SMB_ASSERT(ok);
821
822         status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
823                                                  &aio_state,
824                                                  &state->dosmode);
825         TALLOC_FREE(subreq);
826         if (!NT_STATUS_IS_OK(status)) {
827                 /*
828                  * Both the sync dos_mode() as well as the async
829                  * dos_mode_at_[send|recv] have no real error return, the only
830                  * unhandled error is when the stat info in smb_fname is not
831                  * valid (cf the checks in dos_mode() and dos_mode_at_send().
832                  *
833                  * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
834                  * dos_mode_post() which also does the mapping of a last ressort
835                  * from S_IFMT(st_mode).
836                  *
837                  * Only if we get NT_STATUS_NOT_IMPLEMENTED from a stacked VFS
838                  * module we must fallback to sync processing.
839                  */
840                 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
841                         /*
842                          * state->dosmode should still be 0, but reset
843                          * it to be sure.
844                          */
845                         state->dosmode = 0;
846                         status = NT_STATUS_OK;
847                 }
848         }
849         if (NT_STATUS_IS_OK(status)) {
850                 state->dosmode = dos_mode_post(state->dosmode,
851                                                state->dir_fsp->conn,
852                                                state->smb_fname,
853                                                __func__);
854                 tevent_req_done(req);
855                 return;
856         }
857
858         /*
859          * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
860          */
861
862         path = talloc_asprintf(state,
863                                "%s/%s",
864                                state->dir_fsp->fsp_name->base_name,
865                                state->smb_fname->base_name);
866         if (tevent_req_nomem(path, req)) {
867                 return;
868         }
869
870         smb_path = synthetic_smb_fname(state, path, NULL, NULL, 0);
871         if (tevent_req_nomem(path, req)) {
872                 return;
873         }
874
875         state->dosmode = dos_mode(state->dir_fsp->conn, smb_path);
876         tevent_req_done(req);
877         return;
878 }
879
880 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
881 {
882         struct dos_mode_at_state *state =
883                 tevent_req_data(req,
884                 struct dos_mode_at_state);
885         NTSTATUS status;
886
887         if (tevent_req_is_nterror(req, &status)) {
888                 tevent_req_received(req);
889                 return status;
890         }
891
892         *dosmode = state->dosmode;
893         tevent_req_received(req);
894         return NT_STATUS_OK;
895 }
896
897 /*******************************************************************
898  chmod a file - but preserve some bits.
899  If "store dos attributes" is also set it will store the create time
900  from the stat struct in smb_fname (in NTTIME format) in the EA
901  attribute also.
902 ********************************************************************/
903
904 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
905                      uint32_t dosmode, const char *parent_dir, bool newfile)
906 {
907         int mask=0;
908         mode_t tmp;
909         mode_t unixmode;
910         int ret = -1, lret = -1;
911         files_struct *fsp = NULL;
912         bool need_close = false;
913         NTSTATUS status;
914
915         if (!CAN_WRITE(conn)) {
916                 errno = EROFS;
917                 return -1;
918         }
919
920         dosmode &= SAMBA_ATTRIBUTES_MASK;
921
922         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
923                   dosmode, smb_fname_str_dbg(smb_fname)));
924
925         unixmode = smb_fname->st.st_ex_mode;
926
927         get_acl_group_bits(conn, smb_fname,
928                         &smb_fname->st.st_ex_mode);
929
930         if (S_ISDIR(smb_fname->st.st_ex_mode))
931                 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
932         else
933                 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
934
935         /* Store the DOS attributes in an EA by preference. */
936         status = SMB_VFS_SET_DOS_ATTRIBUTES(conn, smb_fname, dosmode);
937         if (NT_STATUS_IS_OK(status)) {
938                 if (!newfile) {
939                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
940                                 FILE_NOTIFY_CHANGE_ATTRIBUTES,
941                                 smb_fname->base_name);
942                 }
943                 smb_fname->st.st_ex_mode = unixmode;
944                 return 0;
945         } else {
946                 /*
947                  * Only fall back to using UNIX modes if
948                  * we get NOT_IMPLEMENTED.
949                  */
950                 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
951                         errno = map_errno_from_nt_status(status);
952                         return -1;
953                 }
954         }
955
956         /* Fall back to UNIX modes. */
957         unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
958
959         /* preserve the file type bits */
960         mask |= S_IFMT;
961
962         /* preserve the s bits */
963         mask |= (S_ISUID | S_ISGID);
964
965         /* preserve the t bit */
966 #ifdef S_ISVTX
967         mask |= S_ISVTX;
968 #endif
969
970         /* possibly preserve the x bits */
971         if (!MAP_ARCHIVE(conn))
972                 mask |= S_IXUSR;
973         if (!MAP_SYSTEM(conn))
974                 mask |= S_IXGRP;
975         if (!MAP_HIDDEN(conn))
976                 mask |= S_IXOTH;
977
978         unixmode |= (smb_fname->st.st_ex_mode & mask);
979
980         /* if we previously had any r bits set then leave them alone */
981         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
982                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
983                 unixmode |= tmp;
984         }
985
986         /* if we previously had any w bits set then leave them alone 
987                 whilst adding in the new w bits, if the new mode is not rdonly */
988         if (!IS_DOS_READONLY(dosmode)) {
989                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
990         }
991
992         /*
993          * From the chmod 2 man page:
994          *
995          * "If the calling process is not privileged, and the group of the file
996          * does not match the effective group ID of the process or one of its
997          * supplementary group IDs, the S_ISGID bit will be turned off, but
998          * this will not cause an error to be returned."
999          *
1000          * Simply refuse to do the chmod in this case.
1001          */
1002
1003         if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
1004                         geteuid() != sec_initial_uid() &&
1005                         !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
1006                 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1007                         "set for directory %s\n",
1008                         smb_fname_str_dbg(smb_fname)));
1009                 errno = EPERM;
1010                 return -1;
1011         }
1012
1013         ret = SMB_VFS_CHMOD(conn, smb_fname, unixmode);
1014         if (ret == 0) {
1015                 if(!newfile || (lret != -1)) {
1016                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1017                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
1018                                      smb_fname->base_name);
1019                 }
1020                 smb_fname->st.st_ex_mode = unixmode;
1021                 return 0;
1022         }
1023
1024         if((errno != EPERM) && (errno != EACCES))
1025                 return -1;
1026
1027         if(!lp_dos_filemode(SNUM(conn)))
1028                 return -1;
1029
1030         /* We want DOS semantics, ie allow non owner with write permission to change the
1031                 bits on a file. Just like file_ntimes below.
1032         */
1033
1034         if (!can_write_to_file(conn, smb_fname)) {
1035                 errno = EACCES;
1036                 return -1;
1037         }
1038
1039         /*
1040          * We need to get an open file handle to do the
1041          * metadata operation under root.
1042          */
1043
1044         status = get_file_handle_for_metadata(conn,
1045                                               smb_fname,
1046                                               &fsp,
1047                                               &need_close);
1048         if (!NT_STATUS_IS_OK(status)) {
1049                 errno = map_errno_from_nt_status(status);
1050                 return -1;
1051         }
1052
1053         become_root();
1054         ret = SMB_VFS_FCHMOD(fsp, unixmode);
1055         unbecome_root();
1056         if (need_close) {
1057                 close_file(NULL, fsp, NORMAL_CLOSE);
1058         }
1059         if (!newfile) {
1060                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1061                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
1062                              smb_fname->base_name);
1063         }
1064         if (ret == 0) {
1065                 smb_fname->st.st_ex_mode = unixmode;
1066         }
1067
1068         return( ret );
1069 }
1070
1071
1072 NTSTATUS file_set_sparse(connection_struct *conn,
1073                          files_struct *fsp,
1074                          bool sparse)
1075 {
1076         uint32_t old_dosmode;
1077         uint32_t new_dosmode;
1078         NTSTATUS status;
1079
1080         if (!CAN_WRITE(conn)) {
1081                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1082                         "on readonly share[%s]\n",
1083                         smb_fname_str_dbg(fsp->fsp_name),
1084                         sparse,
1085                         lp_servicename(talloc_tos(), SNUM(conn))));
1086                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1087         }
1088
1089         /*
1090          * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1091          * following access flags are granted.
1092          */
1093         if ((fsp->access_mask & (FILE_WRITE_DATA
1094                                 | FILE_WRITE_ATTRIBUTES
1095                                 | SEC_FILE_APPEND_DATA)) == 0) {
1096                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1097                         "access_mask[0x%08X] - access denied\n",
1098                         smb_fname_str_dbg(fsp->fsp_name),
1099                         sparse,
1100                         fsp->access_mask));
1101                 return NT_STATUS_ACCESS_DENIED;
1102         }
1103
1104         if (fsp->is_directory) {
1105                 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1106                           (sparse ? "set" : "clear"),
1107                           smb_fname_str_dbg(fsp->fsp_name)));
1108                 return NT_STATUS_INVALID_PARAMETER;
1109         }
1110
1111         if (IS_IPC(conn) || IS_PRINT(conn)) {
1112                 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1113                           (sparse ? "set" : "clear")));
1114                 return NT_STATUS_INVALID_PARAMETER;
1115         }
1116
1117         DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1118                   sparse, smb_fname_str_dbg(fsp->fsp_name)));
1119
1120         if (!lp_store_dos_attributes(SNUM(conn))) {
1121                 return NT_STATUS_INVALID_DEVICE_REQUEST;
1122         }
1123
1124         status = vfs_stat_fsp(fsp);
1125         if (!NT_STATUS_IS_OK(status)) {
1126                 return status;
1127         }
1128
1129         old_dosmode = dos_mode(conn, fsp->fsp_name);
1130
1131         if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1132                 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1133         } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1134                 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1135         } else {
1136                 return NT_STATUS_OK;
1137         }
1138
1139         /* Store the DOS attributes in an EA. */
1140         status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1141         if (!NT_STATUS_IS_OK(status)) {
1142                 return status;
1143         }
1144
1145         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1146                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
1147                      fsp->fsp_name->base_name);
1148
1149         fsp->is_sparse = sparse;
1150
1151         return NT_STATUS_OK;
1152 }
1153
1154 /*******************************************************************
1155  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1156  than POSIX.
1157 *******************************************************************/
1158
1159 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
1160                 struct smb_file_time *ft)
1161 {
1162         int ret = -1;
1163
1164         errno = 0;
1165
1166         DEBUG(6, ("file_ntime: actime: %s",
1167                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
1168         DEBUG(6, ("file_ntime: modtime: %s",
1169                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
1170         DEBUG(6, ("file_ntime: ctime: %s",
1171                   time_to_asc(convert_timespec_to_time_t(ft->ctime))));
1172         DEBUG(6, ("file_ntime: createtime: %s",
1173                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
1174
1175         /* Don't update the time on read-only shares */
1176         /* We need this as set_filetime (which can be called on
1177            close and other paths) can end up calling this function
1178            without the NEED_WRITE protection. Found by : 
1179            Leo Weppelman <leo@wau.mis.ah.nl>
1180         */
1181
1182         if (!CAN_WRITE(conn)) {
1183                 return 0;
1184         }
1185
1186         if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
1187                 return 0;
1188         }
1189
1190         if((errno != EPERM) && (errno != EACCES)) {
1191                 return -1;
1192         }
1193
1194         if(!lp_dos_filetimes(SNUM(conn))) {
1195                 return -1;
1196         }
1197
1198         /* We have permission (given by the Samba admin) to
1199            break POSIX semantics and allow a user to change
1200            the time on a file they don't own but can write to
1201            (as DOS does).
1202          */
1203
1204         /* Check if we have write access. */
1205         if (can_write_to_file(conn, smb_fname)) {
1206                 /* We are allowed to become root and change the filetime. */
1207                 become_root();
1208                 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1209                 unbecome_root();
1210         }
1211
1212         return ret;
1213 }
1214
1215 /******************************************************************
1216  Force a "sticky" write time on a pathname. This will always be
1217  returned on all future write time queries and set on close.
1218 ******************************************************************/
1219
1220 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1221 {
1222         if (null_timespec(mtime)) {
1223                 return true;
1224         }
1225
1226         if (!set_sticky_write_time(fileid, mtime)) {
1227                 return false;
1228         }
1229
1230         return true;
1231 }
1232
1233 /******************************************************************
1234  Force a "sticky" write time on an fsp. This will always be
1235  returned on all future write time queries and set on close.
1236 ******************************************************************/
1237
1238 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1239 {
1240         if (null_timespec(mtime)) {
1241                 return true;
1242         }
1243
1244         fsp->write_time_forced = true;
1245         TALLOC_FREE(fsp->update_write_time_event);
1246
1247         return set_sticky_write_time_path(fsp->file_id, mtime);
1248 }
1249
1250 /******************************************************************
1251  Set a create time EA.
1252 ******************************************************************/
1253
1254 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1255                                 const struct smb_filename *psmb_fname,
1256                                 struct timespec create_time)
1257 {
1258         struct smb_filename *smb_fname;
1259         uint32_t dosmode;
1260         int ret;
1261
1262         if (!lp_store_dos_attributes(SNUM(conn))) {
1263                 return NT_STATUS_OK;
1264         }
1265
1266         smb_fname = synthetic_smb_fname(talloc_tos(),
1267                                         psmb_fname->base_name,
1268                                         NULL,
1269                                         &psmb_fname->st,
1270                                         psmb_fname->flags);
1271
1272         if (smb_fname == NULL) {
1273                 return NT_STATUS_NO_MEMORY;
1274         }
1275
1276         dosmode = dos_mode(conn, smb_fname);
1277
1278         smb_fname->st.st_ex_btime = create_time;
1279
1280         ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1281         if (ret == -1) {
1282                 return map_nt_error_from_unix(errno);
1283         }
1284
1285         DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1286                 smb_fname_str_dbg(smb_fname)));
1287
1288         return NT_STATUS_OK;
1289 }
1290
1291 /******************************************************************
1292  Return a create time.
1293 ******************************************************************/
1294
1295 struct timespec get_create_timespec(connection_struct *conn,
1296                                 struct files_struct *fsp,
1297                                 const struct smb_filename *smb_fname)
1298 {
1299         return smb_fname->st.st_ex_btime;
1300 }
1301
1302 /******************************************************************
1303  Return a change time (may look at EA in future).
1304 ******************************************************************/
1305
1306 struct timespec get_change_timespec(connection_struct *conn,
1307                                 struct files_struct *fsp,
1308                                 const struct smb_filename *smb_fname)
1309 {
1310         return smb_fname->st.st_ex_mtime;
1311 }
1312
1313 /****************************************************************************
1314  Get a real open file handle we can do meta-data operations on. As it's
1315  going to be used under root access only on meta-data we should look for
1316  any existing open file handle first, and use that in preference (also to
1317  avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1318 ****************************************************************************/
1319
1320 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1321                                 const struct smb_filename *smb_fname,
1322                                 files_struct **ret_fsp,
1323                                 bool *need_close)
1324 {
1325         NTSTATUS status;
1326         files_struct *fsp;
1327         struct file_id file_id;
1328         struct smb_filename *smb_fname_cp = NULL;
1329
1330         *need_close = false;
1331
1332         if (!VALID_STAT(smb_fname->st)) {
1333                 return NT_STATUS_INVALID_PARAMETER;
1334         }
1335
1336         file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1337
1338         for(fsp = file_find_di_first(conn->sconn, file_id);
1339                         fsp;
1340                         fsp = file_find_di_next(fsp)) {
1341                 if (fsp->fh->fd != -1) {
1342                         *ret_fsp = fsp;
1343                         return NT_STATUS_OK;
1344                 }
1345         }
1346
1347         smb_fname_cp = cp_smb_filename(talloc_tos(),
1348                                         smb_fname);
1349         if (smb_fname_cp == NULL) {
1350                 return NT_STATUS_NO_MEMORY;
1351         }
1352
1353         /* Opens an INTERNAL_OPEN_ONLY write handle. */
1354         status = SMB_VFS_CREATE_FILE(
1355                 conn,                                   /* conn */
1356                 NULL,                                   /* req */
1357                 0,                                      /* root_dir_fid */
1358                 smb_fname_cp,                           /* fname */
1359                 FILE_WRITE_ATTRIBUTES,                  /* access_mask */
1360                 (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
1361                         FILE_SHARE_DELETE),
1362                 FILE_OPEN,                              /* create_disposition*/
1363                 0,                                      /* create_options */
1364                 0,                                      /* file_attributes */
1365                 INTERNAL_OPEN_ONLY,                     /* oplock_request */
1366                 NULL,                                   /* lease */
1367                 0,                                      /* allocation_size */
1368                 0,                                      /* private_flags */
1369                 NULL,                                   /* sd */
1370                 NULL,                                   /* ea_list */
1371                 ret_fsp,                                /* result */
1372                 NULL,                                   /* pinfo */
1373                 NULL, NULL);                            /* create context */
1374
1375         TALLOC_FREE(smb_fname_cp);
1376
1377         if (NT_STATUS_IS_OK(status)) {
1378                 *need_close = true;
1379         }
1380         return status;
1381 }