LCOV - code coverage report
Current view: top level - source3/modules - vfs_fruit.c (source / functions) Hit Total Coverage
Test: coverage report for master 2b515b7d Lines: 1768 2343 75.5 %
Date: 2024-02-28 12:06:22 Functions: 116 128 90.6 %

          Line data    Source code
       1             : /*
       2             :  * OS X and Netatalk interoperability VFS module for Samba-3.x
       3             :  *
       4             :  * Copyright (C) Ralph Boehme, 2013, 2014
       5             :  *
       6             :  * This program is free software; you can redistribute it and/or modify
       7             :  * it under the terms of the GNU General Public License as published by
       8             :  * the Free Software Foundation; either version 3 of the License, or
       9             :  * (at your option) any later version.
      10             :  *
      11             :  * This program is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      14             :  * GNU General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License
      17             :  * along with this program; if not, see <http://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : #include "includes.h"
      21             : #include "MacExtensions.h"
      22             : #include "smbd/smbd.h"
      23             : #include "system/filesys.h"
      24             : #include "lib/util/time.h"
      25             : #include "system/shmem.h"
      26             : #include "locking/proto.h"
      27             : #include "smbd/globals.h"
      28             : #include "messages.h"
      29             : #include "libcli/security/security.h"
      30             : #include "../libcli/smb/smb2_create_ctx.h"
      31             : #include "lib/util/tevent_ntstatus.h"
      32             : #include "lib/util/tevent_unix.h"
      33             : #include "offload_token.h"
      34             : #include "string_replace.h"
      35             : #include "hash_inode.h"
      36             : #include "lib/adouble.h"
      37             : #include "lib/util_macstreams.h"
      38             : #include "source3/smbd/dir.h"
      39             : 
      40             : /*
      41             :  * Enhanced OS X and Netatalk compatibility
      42             :  * ========================================
      43             :  *
      44             :  * This modules takes advantage of vfs_streams_xattr and
      45             :  * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
      46             :  * loaded in the correct order:
      47             :  *
      48             :  *   vfs modules = catia fruit streams_xattr
      49             :  *
      50             :  * The module intercepts the OS X special streams "AFP_AfpInfo" and
      51             :  * "AFP_Resource" and handles them in a special way. All other named
      52             :  * streams are deferred to vfs_streams_xattr.
      53             :  *
      54             :  * The OS X client maps all NTFS illegal characters to the Unicode
      55             :  * private range. This module optionally stores the characters using
      56             :  * their native ASCII encoding using vfs_catia. If you're not enabling
      57             :  * this feature, you can skip catia from vfs modules.
      58             :  *
      59             :  * Finally, open modes are optionally checked against Netatalk AFP
      60             :  * share modes.
      61             :  *
      62             :  * The "AFP_AfpInfo" named stream is a binary blob containing OS X
      63             :  * extended metadata for files and directories. This module optionally
      64             :  * reads and stores this metadata in a way compatible with Netatalk 3
      65             :  * which stores the metadata in an EA "org.netatalk.metadata". Cf
      66             :  * source3/include/MacExtensions.h for a description of the binary
      67             :  * blobs content.
      68             :  *
      69             :  * The "AFP_Resource" named stream may be arbitrarily large, thus it
      70             :  * can't be stored in an xattr on most filesystem. ZFS on Solaris is
      71             :  * the only available filesystem where xattrs can be of any size and
      72             :  * the OS supports using the file APIs for xattrs.
      73             :  *
      74             :  * The AFP_Resource stream is stored in an AppleDouble file prepending
      75             :  * "._" to the filename. On Solaris with ZFS the stream is optionally
      76             :  * stored in an EA "org.netatalk.resource".
      77             :  *
      78             :  *
      79             :  * Extended Attributes
      80             :  * ===================
      81             :  *
      82             :  * The OS X SMB client sends xattrs as ADS too. For xattr interop with
      83             :  * other protocols you may want to adjust the xattr names the VFS
      84             :  * module vfs_streams_xattr uses for storing ADS's. This defaults to
      85             :  * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
      86             :  * these module parameters:
      87             :  *
      88             :  *   streams_xattr:prefix = user.
      89             :  *   streams_xattr:store_stream_type = false
      90             :  *
      91             :  *
      92             :  * TODO
      93             :  * ====
      94             :  *
      95             :  * - log diagnostic if any needed VFS module is not loaded
      96             :  *   (eg with lp_vfs_objects())
      97             :  * - add tests
      98             :  */
      99             : 
     100             : static int vfs_fruit_debug_level = DBGC_VFS;
     101             : 
     102             : static struct global_fruit_config {
     103             :         bool nego_aapl; /* client negotiated AAPL */
     104             : 
     105             : } global_fruit_config;
     106             : 
     107             : #undef DBGC_CLASS
     108             : #define DBGC_CLASS vfs_fruit_debug_level
     109             : 
     110             : #define FRUIT_PARAM_TYPE_NAME "fruit"
     111             : 
     112             : enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
     113             : 
     114             : enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
     115             : enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
     116             : enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
     117             : enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
     118             : 
     119             : struct fruit_config_data {
     120             :         enum fruit_rsrc rsrc;
     121             :         enum fruit_meta meta;
     122             :         enum fruit_locking locking;
     123             :         enum fruit_encoding encoding;
     124             :         bool use_aapl;          /* config from smb.conf */
     125             :         bool use_copyfile;
     126             :         bool readdir_attr_enabled;
     127             :         bool unix_info_enabled;
     128             :         bool copyfile_enabled;
     129             :         bool veto_appledouble;
     130             :         bool posix_rename;
     131             :         bool aapl_zero_file_id;
     132             :         const char *model;
     133             :         bool time_machine;
     134             :         off_t time_machine_max_size;
     135             :         bool convert_adouble;
     136             :         bool wipe_intentionally_left_blank_rfork;
     137             :         bool delete_empty_adfiles;
     138             :         bool validate_afpinfo;
     139             : 
     140             :         /*
     141             :          * Additional options, all enabled by default,
     142             :          * possibly useful for analyzing performance. The associated
     143             :          * operations with each of them may be expensive, so having
     144             :          * the chance to disable them individually gives a chance
     145             :          * tweaking the setup for the particular usecase.
     146             :          */
     147             :         bool readdir_attr_rsize;
     148             :         bool readdir_attr_finder_info;
     149             :         bool readdir_attr_max_access;
     150             :         /* Recursion guard. Will go away when we have STATX. */
     151             :         bool in_openat_pathref_fsp;
     152             : };
     153             : 
     154             : static const struct enum_list fruit_rsrc[] = {
     155             :         {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
     156             :         {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
     157             :         {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
     158             :         { -1, NULL}
     159             : };
     160             : 
     161             : static const struct enum_list fruit_meta[] = {
     162             :         {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
     163             :         {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
     164             :         { -1, NULL}
     165             : };
     166             : 
     167             : static const struct enum_list fruit_locking[] = {
     168             :         {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
     169             :         {FRUIT_LOCKING_NONE, "none"},
     170             :         { -1, NULL}
     171             : };
     172             : 
     173             : static const struct enum_list fruit_encoding[] = {
     174             :         {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
     175             :         {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
     176             :         { -1, NULL}
     177             : };
     178             : 
     179             : struct fio {
     180             :         vfs_handle_struct *handle;
     181             :         files_struct *fsp; /* backlink to itself */
     182             : 
     183             :         /* tcon config handle */
     184             :         struct fruit_config_data *config;
     185             : 
     186             :         /* Backend fsp for AppleDouble file, can be NULL */
     187             :         files_struct *ad_fsp;
     188             :         /* link from adouble_open_from_base_fsp() to fio */
     189             :         struct fio *real_fio;
     190             : 
     191             :         /* Denote stream type, meta or rsrc */
     192             :         adouble_type_t type;
     193             : 
     194             :         /*
     195             :          * AFP_AfpInfo stream created, but not written yet, thus still a fake
     196             :          * pipe fd. This is set to true in fruit_open_meta if there was no
     197             :          * existing stream but the caller requested O_CREAT. It is later set to
     198             :          * false when we get a write on the stream that then does open and
     199             :          * create the stream.
     200             :          */
     201             :         bool fake_fd;
     202             :         int flags;
     203             :         int mode;
     204             : };
     205             : 
     206             : /*****************************************************************************
     207             :  * Helper functions
     208             :  *****************************************************************************/
     209             : 
     210       64565 : static struct adouble *ad_get_meta_fsp(TALLOC_CTX *ctx,
     211             :                                        vfs_handle_struct *handle,
     212             :                                        const struct smb_filename *smb_fname)
     213             : {
     214             :         NTSTATUS status;
     215       64565 :         struct adouble *ad = NULL;
     216       64565 :         struct smb_filename *smb_fname_cp = NULL;
     217       64565 :         struct fruit_config_data *config = NULL;
     218             : 
     219       64565 :         if (smb_fname->fsp != NULL) {
     220        2949 :                 return ad_get(ctx, handle, smb_fname, ADOUBLE_META);
     221             :         }
     222             : 
     223       61616 :         SMB_VFS_HANDLE_GET_DATA(handle,
     224             :                                 config,
     225             :                                 struct fruit_config_data,
     226             :                                 return NULL);
     227             : 
     228       61616 :         if (config->in_openat_pathref_fsp) {
     229       25944 :                 return NULL;
     230             :         }
     231             : 
     232       35672 :         smb_fname_cp = cp_smb_filename(ctx,
     233             :                                        smb_fname);
     234       35672 :         if (smb_fname_cp == NULL) {
     235           0 :                 return NULL;
     236             :         }
     237       35672 :         TALLOC_FREE(smb_fname_cp->stream_name);
     238       35672 :         config->in_openat_pathref_fsp = true;
     239       35672 :         status = openat_pathref_fsp(handle->conn->cwd_fsp,
     240             :                                     smb_fname_cp);
     241       35672 :         config->in_openat_pathref_fsp = false;
     242       35672 :         if (!NT_STATUS_IS_OK(status)) {
     243       14935 :                 TALLOC_FREE(smb_fname_cp);
     244       14935 :                 return NULL;
     245             :         }
     246             : 
     247       20737 :         ad = ad_get(ctx, handle, smb_fname_cp, ADOUBLE_META);
     248       20737 :         TALLOC_FREE(smb_fname_cp);
     249       20737 :         return ad;
     250             : }
     251             : 
     252      115130 : static struct fio *fruit_get_complete_fio(vfs_handle_struct *handle,
     253             :                                           files_struct *fsp)
     254             : {
     255      115130 :         struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
     256             : 
     257      115130 :         if (fio == NULL) {
     258       93085 :                 return NULL;
     259             :         }
     260             : 
     261       22045 :         if (fio->real_fio != NULL) {
     262             :                 /*
     263             :                  * This is an fsp from adouble_open_from_base_fsp()
     264             :                  * we should just pass this to the next
     265             :                  * module.
     266             :                  */
     267          30 :                 return NULL;
     268             :         }
     269             : 
     270       22015 :         return fio;
     271             : }
     272             : 
     273             : /**
     274             :  * Initialize config struct from our smb.conf config parameters
     275             :  **/
     276         363 : static int init_fruit_config(vfs_handle_struct *handle)
     277             : {
     278             :         struct fruit_config_data *config;
     279         363 :         int enumval = -1;
     280         363 :         const char *tm_size_str = NULL;
     281             : 
     282         363 :         config = talloc_zero(handle->conn, struct fruit_config_data);
     283         363 :         if (!config) {
     284           0 :                 DEBUG(1, ("talloc_zero() failed\n"));
     285           0 :                 errno = ENOMEM;
     286           0 :                 return -1;
     287             :         }
     288             : 
     289         363 :         enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
     290             :                                "resource", fruit_rsrc, FRUIT_RSRC_ADFILE);
     291         363 :         if (enumval == -1) {
     292           0 :                 DEBUG(1, ("value for %s: resource type unknown\n",
     293             :                           FRUIT_PARAM_TYPE_NAME));
     294           0 :                 return -1;
     295             :         }
     296         363 :         config->rsrc = (enum fruit_rsrc)enumval;
     297             : 
     298         363 :         enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
     299             :                                "metadata", fruit_meta, FRUIT_META_NETATALK);
     300         363 :         if (enumval == -1) {
     301           0 :                 DEBUG(1, ("value for %s: metadata type unknown\n",
     302             :                           FRUIT_PARAM_TYPE_NAME));
     303           0 :                 return -1;
     304             :         }
     305         363 :         config->meta = (enum fruit_meta)enumval;
     306             : 
     307         363 :         enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
     308             :                                "locking", fruit_locking, FRUIT_LOCKING_NONE);
     309         363 :         if (enumval == -1) {
     310           0 :                 DEBUG(1, ("value for %s: locking type unknown\n",
     311             :                           FRUIT_PARAM_TYPE_NAME));
     312           0 :                 return -1;
     313             :         }
     314         363 :         config->locking = (enum fruit_locking)enumval;
     315             : 
     316         363 :         enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
     317             :                                "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
     318         363 :         if (enumval == -1) {
     319           0 :                 DEBUG(1, ("value for %s: encoding type unknown\n",
     320             :                           FRUIT_PARAM_TYPE_NAME));
     321           0 :                 return -1;
     322             :         }
     323         363 :         config->encoding = (enum fruit_encoding)enumval;
     324             : 
     325         363 :         if (config->rsrc == FRUIT_RSRC_ADFILE) {
     326         278 :                 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
     327             :                                                         FRUIT_PARAM_TYPE_NAME,
     328             :                                                         "veto_appledouble",
     329             :                                                         true);
     330             :         }
     331             : 
     332         363 :         config->use_aapl = lp_parm_bool(
     333             :                 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
     334             : 
     335         363 :         config->time_machine = lp_parm_bool(
     336         363 :                 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
     337             : 
     338         363 :         config->unix_info_enabled = lp_parm_bool(
     339             :                 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
     340             : 
     341         363 :         config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
     342             :                                            "copyfile", false);
     343             : 
     344         363 :         config->posix_rename = lp_parm_bool(
     345         363 :                 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
     346             : 
     347         363 :         config->aapl_zero_file_id =
     348         363 :             lp_parm_bool(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
     349             :                          "zero_file_id", true);
     350             : 
     351         363 :         config->readdir_attr_rsize = lp_parm_bool(
     352         363 :                 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
     353             : 
     354         363 :         config->readdir_attr_finder_info = lp_parm_bool(
     355         363 :                 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
     356             : 
     357         363 :         config->readdir_attr_max_access = lp_parm_bool(
     358         363 :                 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
     359             : 
     360         363 :         config->model = lp_parm_const_string(
     361             :                 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
     362             : 
     363         363 :         tm_size_str = lp_parm_const_string(
     364         363 :                 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
     365             :                 "time machine max size", NULL);
     366         363 :         if (tm_size_str != NULL) {
     367           8 :                 config->time_machine_max_size = conv_str_size(tm_size_str);
     368             :         }
     369             : 
     370         363 :         config->convert_adouble = lp_parm_bool(
     371         363 :                 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
     372             :                 "convert_adouble", true);
     373             : 
     374         363 :         config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
     375         363 :                 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
     376             :                 "wipe_intentionally_left_blank_rfork", false);
     377             : 
     378         363 :         config->delete_empty_adfiles = lp_parm_bool(
     379         363 :                 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
     380             :                 "delete_empty_adfiles", false);
     381             : 
     382         363 :         config->validate_afpinfo = lp_parm_bool(
     383         363 :                 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
     384             :                 "validate_afpinfo", true);
     385             : 
     386         363 :         SMB_VFS_HANDLE_SET_DATA(handle, config,
     387             :                                 NULL, struct fruit_config_data,
     388             :                                 return -1);
     389             : 
     390         363 :         return 0;
     391             : }
     392             : 
     393         484 : static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
     394             :                              struct stream_struct **streams,
     395             :                              const char *name, off_t size,
     396             :                              off_t alloc_size)
     397             : {
     398             :         struct stream_struct *tmp;
     399             : 
     400         484 :         tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
     401             :                              (*num_streams)+1);
     402         484 :         if (tmp == NULL) {
     403           0 :                 return false;
     404             :         }
     405             : 
     406         484 :         tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
     407         484 :         if (tmp[*num_streams].name == NULL) {
     408           0 :                 return false;
     409             :         }
     410             : 
     411         484 :         tmp[*num_streams].size = size;
     412         484 :         tmp[*num_streams].alloc_size = alloc_size;
     413             : 
     414         484 :         *streams = tmp;
     415         484 :         *num_streams += 1;
     416         484 :         return true;
     417             : }
     418             : 
     419        1206 : static bool filter_empty_rsrc_stream(unsigned int *num_streams,
     420             :                                      struct stream_struct **streams)
     421             : {
     422        1206 :         struct stream_struct *tmp = *streams;
     423             :         unsigned int i;
     424             : 
     425        1206 :         if (*num_streams == 0) {
     426           0 :                 return true;
     427             :         }
     428             : 
     429        2506 :         for (i = 0; i < *num_streams; i++) {
     430        1392 :                 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
     431          92 :                         break;
     432             :                 }
     433             :         }
     434             : 
     435        1206 :         if (i == *num_streams) {
     436        1114 :                 return true;
     437             :         }
     438             : 
     439          92 :         if (tmp[i].size > 0) {
     440          88 :                 return true;
     441             :         }
     442             : 
     443           4 :         TALLOC_FREE(tmp[i].name);
     444           4 :         ARRAY_DEL_ELEMENT(tmp, i, *num_streams);
     445           4 :         *num_streams -= 1;
     446           4 :         return true;
     447             : }
     448             : 
     449        2645 : static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
     450             :                              struct stream_struct **streams,
     451             :                              const char *name)
     452             : {
     453        2645 :         struct stream_struct *tmp = *streams;
     454             :         unsigned int i;
     455             : 
     456        2645 :         if (*num_streams == 0) {
     457         984 :                 return true;
     458             :         }
     459             : 
     460        3394 :         for (i = 0; i < *num_streams; i++) {
     461        1759 :                 if (strequal_m(tmp[i].name, name)) {
     462          26 :                         break;
     463             :                 }
     464             :         }
     465             : 
     466        1661 :         if (i == *num_streams) {
     467        1635 :                 return true;
     468             :         }
     469             : 
     470          26 :         TALLOC_FREE(tmp[i].name);
     471          26 :         ARRAY_DEL_ELEMENT(tmp, i, *num_streams);
     472          26 :         *num_streams -= 1;
     473          26 :         return true;
     474             : }
     475             : 
     476         196 : static bool ad_empty_finderinfo(const struct adouble *ad)
     477             : {
     478             :         int cmp;
     479         196 :         char emptybuf[ADEDLEN_FINDERI] = {0};
     480         196 :         char *fi = NULL;
     481             : 
     482         196 :         fi = ad_get_entry(ad, ADEID_FINDERI);
     483         196 :         if (fi == NULL) {
     484           0 :                 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
     485           0 :                 return false;
     486             :         }
     487             : 
     488         196 :         cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
     489         196 :         return (cmp == 0);
     490             : }
     491             : 
     492         543 : static bool ai_empty_finderinfo(const AfpInfo *ai)
     493             : {
     494             :         int cmp;
     495         543 :         char emptybuf[ADEDLEN_FINDERI] = {0};
     496             : 
     497         543 :         cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
     498         543 :         return (cmp == 0);
     499             : }
     500             : 
     501             : /**
     502             :  * Update btime with btime from Netatalk
     503             :  **/
     504      121993 : static void update_btime(vfs_handle_struct *handle,
     505             :                          struct smb_filename *smb_fname)
     506             : {
     507             :         uint32_t t;
     508      121993 :         struct timespec creation_time = {0};
     509             :         struct adouble *ad;
     510      121993 :         struct fruit_config_data *config = NULL;
     511             : 
     512      121993 :         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
     513             :                                 return);
     514             : 
     515      121993 :         switch (config->meta) {
     516       60113 :         case FRUIT_META_STREAM:
     517       60113 :                 return;
     518       61880 :         case FRUIT_META_NETATALK:
     519             :                 /* Handled below */
     520       61880 :                 break;
     521           0 :         default:
     522           0 :                 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
     523           0 :                 return;
     524             :         }
     525             : 
     526       61880 :         ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
     527       61880 :         if (ad == NULL) {
     528       61513 :                 return;
     529             :         }
     530         367 :         if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
     531           0 :                 TALLOC_FREE(ad);
     532           0 :                 return;
     533             :         }
     534         367 :         TALLOC_FREE(ad);
     535             : 
     536         367 :         creation_time.tv_sec = convert_uint32_t_to_time_t(t);
     537         367 :         update_stat_ex_create_time(&smb_fname->st, creation_time);
     538             : 
     539         367 :         return;
     540             : }
     541             : 
     542             : /**
     543             :  * Map an access mask to a Netatalk single byte byte range lock
     544             :  **/
     545        2354 : static off_t access_to_netatalk_brl(enum apple_fork fork_type,
     546             :                                     uint32_t access_mask)
     547             : {
     548             :         off_t offset;
     549             : 
     550        2354 :         switch (access_mask) {
     551        1179 :         case FILE_READ_DATA:
     552        1179 :                 offset = AD_FILELOCK_OPEN_RD;
     553        1179 :                 break;
     554             : 
     555        1175 :         case FILE_WRITE_DATA:
     556             :         case FILE_APPEND_DATA:
     557        1175 :                 offset = AD_FILELOCK_OPEN_WR;
     558        1175 :                 break;
     559             : 
     560           0 :         default:
     561           0 :                 offset = AD_FILELOCK_OPEN_NONE;
     562           0 :                 break;
     563             :         }
     564             : 
     565        2354 :         if (fork_type == APPLE_FORK_RSRC) {
     566           0 :                 if (offset == AD_FILELOCK_OPEN_NONE) {
     567           0 :                         offset = AD_FILELOCK_RSRC_OPEN_NONE;
     568             :                 } else {
     569           0 :                         offset += 2;
     570             :                 }
     571             :         }
     572             : 
     573        2354 :         return offset;
     574             : }
     575             : 
     576             : /**
     577             :  * Map a deny mode to a Netatalk brl
     578             :  **/
     579        1478 : static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
     580             :                                       uint32_t deny_mode)
     581             : {
     582        1478 :         off_t offset = 0;
     583             : 
     584        1478 :         switch (deny_mode) {
     585         739 :         case DENY_READ:
     586         739 :                 offset = AD_FILELOCK_DENY_RD;
     587         739 :                 break;
     588             : 
     589         739 :         case DENY_WRITE:
     590         739 :                 offset = AD_FILELOCK_DENY_WR;
     591         739 :                 break;
     592             : 
     593           0 :         default:
     594           0 :                 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
     595             :         }
     596             : 
     597        1478 :         if (fork_type == APPLE_FORK_RSRC) {
     598           0 :                 offset += 2;
     599             :         }
     600             : 
     601        1478 :         return offset;
     602             : }
     603             : 
     604             : /**
     605             :  * Call fcntl() with an exclusive F_GETLK request in order to
     606             :  * determine if there's an existing shared lock
     607             :  *
     608             :  * @return true if the requested lock was found or any error occurred
     609             :  *         false if the lock was not found
     610             :  **/
     611        2948 : static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
     612             : {
     613             :         bool result;
     614        2948 :         off_t offset = in_offset;
     615        2948 :         off_t len = 1;
     616        2948 :         int type = F_WRLCK;
     617        2948 :         pid_t pid = 0;
     618             : 
     619        2948 :         result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
     620        2948 :         if (result == false) {
     621           0 :                 return true;
     622             :         }
     623             : 
     624        2948 :         if (type != F_UNLCK) {
     625          24 :                 return true;
     626             :         }
     627             : 
     628        2924 :         return false;
     629             : }
     630             : 
     631         737 : static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
     632             :                                    files_struct *fsp,
     633             :                                    uint32_t access_mask,
     634             :                                    uint32_t share_mode)
     635             : {
     636         737 :         NTSTATUS status = NT_STATUS_OK;
     637             :         off_t off;
     638         737 :         bool share_for_read = (share_mode & FILE_SHARE_READ);
     639         737 :         bool share_for_write = (share_mode & FILE_SHARE_WRITE);
     640         737 :         bool netatalk_already_open_for_reading = false;
     641         737 :         bool netatalk_already_open_for_writing = false;
     642         737 :         bool netatalk_already_open_with_deny_read = false;
     643         737 :         bool netatalk_already_open_with_deny_write = false;
     644         737 :         struct GUID req_guid = GUID_random();
     645             : 
     646             :         /* FIXME: hardcoded data fork, add resource fork */
     647         737 :         enum apple_fork fork_type = APPLE_FORK_DATA;
     648             : 
     649         737 :         DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
     650             :                   fsp_str_dbg(fsp),
     651             :                   access_mask & FILE_READ_DATA ? "READ" :"-",
     652             :                   access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
     653             :                   share_mode);
     654             : 
     655         737 :         if (fsp_get_io_fd(fsp) == -1) {
     656           0 :                 return NT_STATUS_OK;
     657             :         }
     658             : 
     659             :         /* Read NetATalk opens and deny modes on the file. */
     660         737 :         netatalk_already_open_for_reading = test_netatalk_lock(fsp,
     661             :                                 access_to_netatalk_brl(fork_type,
     662             :                                         FILE_READ_DATA));
     663             : 
     664         737 :         netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
     665             :                                 denymode_to_netatalk_brl(fork_type,
     666             :                                         DENY_READ));
     667             : 
     668         737 :         netatalk_already_open_for_writing = test_netatalk_lock(fsp,
     669             :                                 access_to_netatalk_brl(fork_type,
     670             :                                         FILE_WRITE_DATA));
     671             : 
     672         737 :         netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
     673             :                                 denymode_to_netatalk_brl(fork_type,
     674             :                                         DENY_WRITE));
     675             : 
     676             :         /* If there are any conflicts - sharing violation. */
     677         737 :         if ((access_mask & FILE_READ_DATA) &&
     678             :                         netatalk_already_open_with_deny_read) {
     679           0 :                 return NT_STATUS_SHARING_VIOLATION;
     680             :         }
     681             : 
     682         737 :         if (!share_for_read &&
     683             :                         netatalk_already_open_for_reading) {
     684           0 :                 return NT_STATUS_SHARING_VIOLATION;
     685             :         }
     686             : 
     687         737 :         if ((access_mask & FILE_WRITE_DATA) &&
     688             :                         netatalk_already_open_with_deny_write) {
     689           0 :                 return NT_STATUS_SHARING_VIOLATION;
     690             :         }
     691             : 
     692         737 :         if (!share_for_write &&
     693             :                         netatalk_already_open_for_writing) {
     694           2 :                 return NT_STATUS_SHARING_VIOLATION;
     695             :         }
     696             : 
     697         735 :         if (!(access_mask & FILE_READ_DATA)) {
     698             :                 /*
     699             :                  * Nothing we can do here, we need read access
     700             :                  * to set locks.
     701             :                  */
     702         293 :                 return NT_STATUS_OK;
     703             :         }
     704             : 
     705             :         /* Set NetAtalk locks matching our access */
     706         442 :         if (access_mask & FILE_READ_DATA) {
     707         442 :                 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
     708         442 :                 req_guid.time_hi_and_version = __LINE__;
     709         442 :                 status = do_lock(
     710             :                         fsp,
     711             :                         talloc_tos(),
     712             :                         &req_guid,
     713         442 :                         fsp->op->global->open_persistent_id,
     714             :                         1,
     715             :                         off,
     716             :                         READ_LOCK,
     717             :                         POSIX_LOCK,
     718             :                         NULL,
     719             :                         NULL);
     720             : 
     721         442 :                 if (!NT_STATUS_IS_OK(status))  {
     722           0 :                         return status;
     723             :                 }
     724             :         }
     725             : 
     726         442 :         if (!share_for_read) {
     727           2 :                 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
     728           2 :                 req_guid.time_hi_and_version = __LINE__;
     729           2 :                 status = do_lock(
     730             :                         fsp,
     731             :                         talloc_tos(),
     732             :                         &req_guid,
     733           2 :                         fsp->op->global->open_persistent_id,
     734             :                         1,
     735             :                         off,
     736             :                         READ_LOCK,
     737             :                         POSIX_LOCK,
     738             :                         NULL,
     739             :                         NULL);
     740             : 
     741           2 :                 if (!NT_STATUS_IS_OK(status)) {
     742           0 :                         return status;
     743             :                 }
     744             :         }
     745             : 
     746         442 :         if (access_mask & FILE_WRITE_DATA) {
     747         438 :                 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
     748         438 :                 req_guid.time_hi_and_version = __LINE__;
     749         438 :                 status = do_lock(
     750             :                         fsp,
     751             :                         talloc_tos(),
     752             :                         &req_guid,
     753         438 :                         fsp->op->global->open_persistent_id,
     754             :                         1,
     755             :                         off,
     756             :                         READ_LOCK,
     757             :                         POSIX_LOCK,
     758             :                         NULL,
     759             :                         NULL);
     760             : 
     761         438 :                 if (!NT_STATUS_IS_OK(status)) {
     762           0 :                         return status;
     763             :                 }
     764             :         }
     765             : 
     766         442 :         if (!share_for_write) {
     767           2 :                 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
     768           2 :                 req_guid.time_hi_and_version = __LINE__;
     769           2 :                 status = do_lock(
     770             :                         fsp,
     771             :                         talloc_tos(),
     772             :                         &req_guid,
     773           2 :                         fsp->op->global->open_persistent_id,
     774             :                         1,
     775             :                         off,
     776             :                         READ_LOCK,
     777             :                         POSIX_LOCK,
     778             :                         NULL,
     779             :                         NULL);
     780             : 
     781           2 :                 if (!NT_STATUS_IS_OK(status)) {
     782           0 :                         return status;
     783             :                 }
     784             :         }
     785             : 
     786         442 :         return NT_STATUS_OK;
     787             : }
     788             : 
     789       10617 : static NTSTATUS check_aapl(vfs_handle_struct *handle,
     790             :                            struct smb_request *req,
     791             :                            const struct smb2_create_blobs *in_context_blobs,
     792             :                            struct smb2_create_blobs *out_context_blobs)
     793             : {
     794             :         struct fruit_config_data *config;
     795             :         NTSTATUS status;
     796       10617 :         struct smb2_create_blob *aapl = NULL;
     797             :         uint32_t cmd;
     798             :         bool ok;
     799             :         uint8_t p[16];
     800       10617 :         DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
     801             :         uint64_t req_bitmap, client_caps;
     802       10617 :         uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
     803             :         smb_ucs2_t *model;
     804             :         size_t modellen;
     805             : 
     806       10617 :         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
     807             :                                 return NT_STATUS_UNSUCCESSFUL);
     808             : 
     809       10617 :         if (!config->use_aapl
     810       10617 :             || in_context_blobs == NULL
     811        9989 :             || out_context_blobs == NULL) {
     812         628 :                 return NT_STATUS_OK;
     813             :         }
     814             : 
     815        9989 :         aapl = smb2_create_blob_find(in_context_blobs,
     816             :                                      SMB2_CREATE_TAG_AAPL);
     817        9989 :         if (aapl == NULL) {
     818        9861 :                 return NT_STATUS_OK;
     819             :         }
     820             : 
     821         128 :         if (aapl->data.length != 24) {
     822           0 :                 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
     823             :                           (uintmax_t)aapl->data.length));
     824           0 :                 return NT_STATUS_INVALID_PARAMETER;
     825             :         }
     826             : 
     827         128 :         cmd = IVAL(aapl->data.data, 0);
     828         128 :         if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
     829           0 :                 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
     830           0 :                 return NT_STATUS_INVALID_PARAMETER;
     831             :         }
     832             : 
     833         128 :         req_bitmap = BVAL(aapl->data.data, 8);
     834         128 :         client_caps = BVAL(aapl->data.data, 16);
     835             : 
     836         128 :         SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
     837         128 :         SIVAL(p, 4, 0);
     838         128 :         SBVAL(p, 8, req_bitmap);
     839         128 :         ok = data_blob_append(req, &blob, p, 16);
     840         128 :         if (!ok) {
     841           0 :                 return NT_STATUS_UNSUCCESSFUL;
     842             :         }
     843             : 
     844         128 :         if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
     845         128 :                 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
     846         120 :                     (handle->conn->fs_capabilities & FILE_NAMED_STREAMS)) {
     847         120 :                         server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
     848         120 :                         config->readdir_attr_enabled = true;
     849             :                 }
     850             : 
     851         128 :                 if (config->use_copyfile) {
     852         128 :                         server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
     853         128 :                         config->copyfile_enabled = true;
     854             :                 }
     855             : 
     856             :                 /*
     857             :                  * The client doesn't set the flag, so we can't check
     858             :                  * for it and just set it unconditionally
     859             :                  */
     860         128 :                 if (config->unix_info_enabled) {
     861         128 :                         server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
     862             :                 }
     863             : 
     864         128 :                 SBVAL(p, 0, server_caps);
     865         128 :                 ok = data_blob_append(req, &blob, p, 8);
     866         128 :                 if (!ok) {
     867           0 :                         return NT_STATUS_UNSUCCESSFUL;
     868             :                 }
     869             :         }
     870             : 
     871         128 :         if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
     872         120 :                 int val = lp_case_sensitive(SNUM(handle->conn));
     873         120 :                 uint64_t caps = 0;
     874             : 
     875         120 :                 switch (val) {
     876         120 :                 case Auto:
     877         120 :                         break;
     878             : 
     879           0 :                 case True:
     880           0 :                         caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
     881           0 :                         break;
     882             : 
     883           0 :                 default:
     884           0 :                         break;
     885             :                 }
     886             : 
     887         120 :                 if (config->time_machine) {
     888           2 :                         caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
     889             :                 }
     890             : 
     891         120 :                 SBVAL(p, 0, caps);
     892             : 
     893         120 :                 ok = data_blob_append(req, &blob, p, 8);
     894         120 :                 if (!ok) {
     895           0 :                         return NT_STATUS_UNSUCCESSFUL;
     896             :                 }
     897             :         }
     898             : 
     899         128 :         if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
     900         120 :                 ok = convert_string_talloc(req,
     901             :                                            CH_UNIX, CH_UTF16LE,
     902         120 :                                            config->model, strlen(config->model),
     903             :                                            &model, &modellen);
     904         120 :                 if (!ok) {
     905           0 :                         return NT_STATUS_UNSUCCESSFUL;
     906             :                 }
     907             : 
     908         120 :                 SIVAL(p, 0, 0);
     909         120 :                 SIVAL(p + 4, 0, modellen);
     910         120 :                 ok = data_blob_append(req, &blob, p, 8);
     911         120 :                 if (!ok) {
     912           0 :                         talloc_free(model);
     913           0 :                         return NT_STATUS_UNSUCCESSFUL;
     914             :                 }
     915             : 
     916         120 :                 ok = data_blob_append(req, &blob, model, modellen);
     917         120 :                 talloc_free(model);
     918         120 :                 if (!ok) {
     919           0 :                         return NT_STATUS_UNSUCCESSFUL;
     920             :                 }
     921             :         }
     922             : 
     923         128 :         status = smb2_create_blob_add(out_context_blobs,
     924             :                                       out_context_blobs,
     925             :                                       SMB2_CREATE_TAG_AAPL,
     926             :                                       blob);
     927         128 :         if (NT_STATUS_IS_OK(status)) {
     928         128 :                 global_fruit_config.nego_aapl = true;
     929             :         }
     930             : 
     931         128 :         return status;
     932             : }
     933             : 
     934         238 : static bool readdir_attr_meta_finderi_stream(
     935             :         struct vfs_handle_struct *handle,
     936             :         const struct smb_filename *smb_fname,
     937             :         AfpInfo *ai)
     938             : {
     939         238 :         struct smb_filename *stream_name = NULL;
     940         238 :         files_struct *fsp = NULL;
     941             :         ssize_t nread;
     942             :         NTSTATUS status;
     943             :         bool ok;
     944             :         uint8_t buf[AFP_INFO_SIZE];
     945             : 
     946         238 :         status = synthetic_pathref(talloc_tos(),
     947         238 :                                    handle->conn->cwd_fsp,
     948         238 :                                    smb_fname->base_name,
     949             :                                    AFPINFO_STREAM_NAME,
     950             :                                    NULL,
     951         238 :                                    smb_fname->twrp,
     952         238 :                                    smb_fname->flags,
     953             :                                    &stream_name);
     954         238 :         if (!NT_STATUS_IS_OK(status)) {
     955         206 :                 return false;
     956             :         }
     957             : 
     958          32 :         status = SMB_VFS_CREATE_FILE(
     959             :                 handle->conn,                           /* conn */
     960             :                 NULL,                                   /* req */
     961             :                 NULL,                                   /* dirfsp */
     962             :                 stream_name,                            /* fname */
     963             :                 FILE_READ_DATA,                         /* access_mask */
     964             :                 (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
     965             :                         FILE_SHARE_DELETE),
     966             :                 FILE_OPEN,                              /* create_disposition*/
     967             :                 0,                                      /* create_options */
     968             :                 0,                                      /* file_attributes */
     969             :                 INTERNAL_OPEN_ONLY,                     /* oplock_request */
     970             :                 NULL,                                   /* lease */
     971             :                 0,                                      /* allocation_size */
     972             :                 0,                                      /* private_flags */
     973             :                 NULL,                                   /* sd */
     974             :                 NULL,                                   /* ea_list */
     975             :                 &fsp,                                   /* result */
     976             :                 NULL,                                   /* pinfo */
     977             :                 NULL, NULL);                            /* create context */
     978             : 
     979          32 :         TALLOC_FREE(stream_name);
     980             : 
     981          32 :         if (!NT_STATUS_IS_OK(status)) {
     982           0 :                 return false;
     983             :         }
     984             : 
     985          32 :         nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
     986          32 :         if (nread != AFP_INFO_SIZE) {
     987           0 :                 DBG_ERR("short read [%s] [%zd/%d]\n",
     988             :                         smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
     989           0 :                 ok = false;
     990           0 :                 goto fail;
     991             :         }
     992             : 
     993          32 :         memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
     994             :                AFP_FinderSize);
     995             : 
     996          32 :         ok = true;
     997             : 
     998          32 : fail:
     999          32 :         if (fsp != NULL) {
    1000          32 :                 close_file_free(NULL, &fsp, NORMAL_CLOSE);
    1001             :         }
    1002             : 
    1003          32 :         return ok;
    1004             : }
    1005             : 
    1006          66 : static bool readdir_attr_meta_finderi_netatalk(
    1007             :         struct vfs_handle_struct *handle,
    1008             :         const struct smb_filename *smb_fname,
    1009             :         AfpInfo *ai)
    1010             : {
    1011          66 :         struct adouble *ad = NULL;
    1012          66 :         char *p = NULL;
    1013             : 
    1014          66 :         ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
    1015          66 :         if (ad == NULL) {
    1016          58 :                 return false;
    1017             :         }
    1018             : 
    1019           8 :         p = ad_get_entry(ad, ADEID_FINDERI);
    1020           8 :         if (p == NULL) {
    1021           0 :                 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
    1022           0 :                 TALLOC_FREE(ad);
    1023           0 :                 return false;
    1024             :         }
    1025             : 
    1026           8 :         memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
    1027           8 :         TALLOC_FREE(ad);
    1028           8 :         return true;
    1029             : }
    1030             : 
    1031         304 : static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
    1032             :                                       const struct smb_filename *smb_fname,
    1033             :                                       struct readdir_attr_data *attr_data)
    1034             : {
    1035         304 :         struct fruit_config_data *config = NULL;
    1036             :         uint32_t date_added;
    1037         304 :         AfpInfo ai = {0};
    1038             :         bool ok;
    1039             : 
    1040         304 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1041             :                                 struct fruit_config_data,
    1042             :                                 return false);
    1043             : 
    1044         304 :         switch (config->meta) {
    1045          66 :         case FRUIT_META_NETATALK:
    1046          66 :                 ok = readdir_attr_meta_finderi_netatalk(
    1047             :                         handle, smb_fname, &ai);
    1048          66 :                 break;
    1049             : 
    1050         238 :         case FRUIT_META_STREAM:
    1051         238 :                 ok = readdir_attr_meta_finderi_stream(
    1052             :                         handle, smb_fname, &ai);
    1053         238 :                 break;
    1054             : 
    1055           0 :         default:
    1056           0 :                 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
    1057           0 :                 return false;
    1058             :         }
    1059             : 
    1060         304 :         if (!ok) {
    1061             :                 /* Don't bother with errors, it's likely ENOENT */
    1062         264 :                 return true;
    1063             :         }
    1064             : 
    1065          40 :         if (S_ISREG(smb_fname->st.st_ex_mode)) {
    1066             :                 /* finder_type */
    1067          40 :                 memcpy(&attr_data->attr_data.aapl.finder_info[0],
    1068             :                        &ai.afpi_FinderInfo[0], 4);
    1069             : 
    1070             :                 /* finder_creator */
    1071          40 :                 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
    1072             :                        &ai.afpi_FinderInfo[4], 4);
    1073             :         }
    1074             : 
    1075             :         /* finder_flags */
    1076          40 :         memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
    1077             :                &ai.afpi_FinderInfo[8], 2);
    1078             : 
    1079             :         /* finder_ext_flags */
    1080          40 :         memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
    1081             :                &ai.afpi_FinderInfo[24], 2);
    1082             : 
    1083             :         /* creation date */
    1084          40 :         date_added = convert_time_t_to_uint32_t(
    1085          40 :                 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
    1086             : 
    1087          40 :         RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
    1088             : 
    1089          40 :         return true;
    1090             : }
    1091             : 
    1092         240 : static uint64_t readdir_attr_rfork_size_adouble(
    1093             :         struct vfs_handle_struct *handle,
    1094             :         const struct smb_filename *smb_fname)
    1095             : {
    1096         240 :         struct adouble *ad = NULL;
    1097             :         uint64_t rfork_size;
    1098             : 
    1099         240 :         ad = ad_get(talloc_tos(), handle, smb_fname,
    1100             :                     ADOUBLE_RSRC);
    1101         240 :         if (ad == NULL) {
    1102         208 :                 return 0;
    1103             :         }
    1104             : 
    1105          32 :         rfork_size = ad_getentrylen(ad, ADEID_RFORK);
    1106          32 :         TALLOC_FREE(ad);
    1107             : 
    1108          32 :         return rfork_size;
    1109             : }
    1110             : 
    1111          64 : static uint64_t readdir_attr_rfork_size_stream(
    1112             :         struct vfs_handle_struct *handle,
    1113             :         const struct smb_filename *smb_fname)
    1114             : {
    1115          64 :         struct smb_filename *stream_name = NULL;
    1116             :         int ret;
    1117             :         uint64_t rfork_size;
    1118             : 
    1119          64 :         stream_name = synthetic_smb_fname(talloc_tos(),
    1120          64 :                                           smb_fname->base_name,
    1121             :                                           AFPRESOURCE_STREAM_NAME,
    1122             :                                           NULL,
    1123          64 :                                           smb_fname->twrp,
    1124             :                                           0);
    1125          64 :         if (stream_name == NULL) {
    1126           0 :                 return 0;
    1127             :         }
    1128             : 
    1129          64 :         ret = SMB_VFS_STAT(handle->conn, stream_name);
    1130          64 :         if (ret != 0) {
    1131          56 :                 TALLOC_FREE(stream_name);
    1132          56 :                 return 0;
    1133             :         }
    1134             : 
    1135           8 :         rfork_size = stream_name->st.st_ex_size;
    1136           8 :         TALLOC_FREE(stream_name);
    1137             : 
    1138           8 :         return rfork_size;
    1139             : }
    1140             : 
    1141         304 : static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
    1142             :                                         const struct smb_filename *smb_fname)
    1143             : {
    1144         304 :         struct fruit_config_data *config = NULL;
    1145             :         uint64_t rfork_size;
    1146             : 
    1147         304 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1148             :                                 struct fruit_config_data,
    1149             :                                 return 0);
    1150             : 
    1151         304 :         switch (config->rsrc) {
    1152         240 :         case FRUIT_RSRC_ADFILE:
    1153         240 :                 rfork_size = readdir_attr_rfork_size_adouble(handle,
    1154             :                                                              smb_fname);
    1155         240 :                 break;
    1156             : 
    1157          64 :         case FRUIT_RSRC_XATTR:
    1158             :         case FRUIT_RSRC_STREAM:
    1159          64 :                 rfork_size = readdir_attr_rfork_size_stream(handle,
    1160             :                                                             smb_fname);
    1161          64 :                 break;
    1162             : 
    1163           0 :         default:
    1164           0 :                 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
    1165           0 :                 rfork_size = 0;
    1166           0 :                 break;
    1167             :         }
    1168             : 
    1169         304 :         return rfork_size;
    1170             : }
    1171             : 
    1172         304 : static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
    1173             :                                      const struct smb_filename *smb_fname,
    1174             :                                      struct readdir_attr_data *attr_data)
    1175             : {
    1176         304 :         NTSTATUS status = NT_STATUS_OK;
    1177         304 :         struct fruit_config_data *config = NULL;
    1178             :         bool ok;
    1179             : 
    1180         304 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1181             :                                 struct fruit_config_data,
    1182             :                                 return NT_STATUS_UNSUCCESSFUL);
    1183             : 
    1184             : 
    1185             :         /* Ensure we return a default value in the creation_date field */
    1186         304 :         RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
    1187             : 
    1188             :         /*
    1189             :          * Resource fork length
    1190             :          */
    1191             : 
    1192         304 :         if (config->readdir_attr_rsize) {
    1193             :                 uint64_t rfork_size;
    1194             : 
    1195         304 :                 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
    1196         304 :                 attr_data->attr_data.aapl.rfork_size = rfork_size;
    1197             :         }
    1198             : 
    1199             :         /*
    1200             :          * FinderInfo
    1201             :          */
    1202             : 
    1203         304 :         if (config->readdir_attr_finder_info) {
    1204         304 :                 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
    1205         304 :                 if (!ok) {
    1206           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1207             :                 }
    1208             :         }
    1209             : 
    1210         304 :         return status;
    1211             : }
    1212             : 
    1213        5076 : static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
    1214             : {
    1215             :         NTSTATUS status;
    1216             :         uint32_t i;
    1217             : 
    1218        5076 :         if (psd->dacl == NULL) {
    1219           0 :                 return NT_STATUS_OK;
    1220             :         }
    1221             : 
    1222       26968 :         for (i = 0; i < psd->dacl->num_aces; i++) {
    1223             :                 /* MS NFS style mode/uid/gid */
    1224       21892 :                 int cmp = dom_sid_compare_domain(
    1225             :                                 &global_sid_Unix_NFS,
    1226       21892 :                                 &psd->dacl->aces[i].trustee);
    1227       21892 :                 if (cmp != 0) {
    1228             :                         /* Normal ACE entry. */
    1229       21884 :                         continue;
    1230             :                 }
    1231             : 
    1232             :                 /*
    1233             :                  * security_descriptor_dacl_del()
    1234             :                  * *must* return NT_STATUS_OK as we know
    1235             :                  * we have something to remove.
    1236             :                  */
    1237             : 
    1238           8 :                 status = security_descriptor_dacl_del(psd,
    1239           8 :                                 &psd->dacl->aces[i].trustee);
    1240           8 :                 if (!NT_STATUS_IS_OK(status)) {
    1241           0 :                         DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
    1242             :                                 nt_errstr(status));
    1243           0 :                         return status;
    1244             :                 }
    1245             : 
    1246             :                 /*
    1247             :                  * security_descriptor_dacl_del() may delete more
    1248             :                  * then one entry subsequent to this one if the
    1249             :                  * SID matches, but we only need to ensure that
    1250             :                  * we stay looking at the same element in the array.
    1251             :                  */
    1252           8 :                 i--;
    1253             :         }
    1254        5076 :         return NT_STATUS_OK;
    1255             : }
    1256             : 
    1257             : /* Search MS NFS style ACE with UNIX mode */
    1258        1161 : static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
    1259             :                              files_struct *fsp,
    1260             :                              struct security_descriptor *psd,
    1261             :                              mode_t *pmode,
    1262             :                              bool *pdo_chmod)
    1263             : {
    1264             :         uint32_t i;
    1265        1161 :         struct fruit_config_data *config = NULL;
    1266             : 
    1267        1161 :         *pdo_chmod = false;
    1268             : 
    1269        1161 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1270             :                                 struct fruit_config_data,
    1271             :                                 return NT_STATUS_UNSUCCESSFUL);
    1272             : 
    1273        1161 :         if (!global_fruit_config.nego_aapl) {
    1274         799 :                 return NT_STATUS_OK;
    1275             :         }
    1276         362 :         if (psd->dacl == NULL || !config->unix_info_enabled) {
    1277           0 :                 return NT_STATUS_OK;
    1278             :         }
    1279             : 
    1280        1552 :         for (i = 0; i < psd->dacl->num_aces; i++) {
    1281        1198 :                 if (dom_sid_compare_domain(
    1282             :                             &global_sid_Unix_NFS_Mode,
    1283        1198 :                             &psd->dacl->aces[i].trustee) == 0) {
    1284           8 :                         *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
    1285           8 :                         *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
    1286           8 :                         *pdo_chmod = true;
    1287             : 
    1288           8 :                         DEBUG(10, ("MS NFS chmod request %s, %04o\n",
    1289             :                                    fsp_str_dbg(fsp), (unsigned)(*pmode)));
    1290           8 :                         break;
    1291             :                 }
    1292             :         }
    1293             : 
    1294             :         /*
    1295             :          * Remove any incoming virtual ACE entries generated by
    1296             :          * fruit_fget_nt_acl().
    1297             :          */
    1298             : 
    1299         362 :         return remove_virtual_nfs_aces(psd);
    1300             : }
    1301             : 
    1302             : /****************************************************************************
    1303             :  * VFS ops
    1304             :  ****************************************************************************/
    1305             : 
    1306         363 : static int fruit_connect(vfs_handle_struct *handle,
    1307             :                          const char *service,
    1308             :                          const char *user)
    1309             : {
    1310             :         int rc;
    1311         363 :         char *list = NULL, *newlist = NULL;
    1312             :         struct fruit_config_data *config;
    1313             :         const struct loadparm_substitution *lp_sub =
    1314         363 :                 loadparm_s3_global_substitution();
    1315             : 
    1316         363 :         DEBUG(10, ("fruit_connect\n"));
    1317             : 
    1318         363 :         rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
    1319         363 :         if (rc < 0) {
    1320           0 :                 return rc;
    1321             :         }
    1322             : 
    1323         363 :         rc = init_fruit_config(handle);
    1324         363 :         if (rc != 0) {
    1325           0 :                 return rc;
    1326             :         }
    1327             : 
    1328         363 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1329             :                                 struct fruit_config_data, return -1);
    1330             : 
    1331         363 :         if (config->veto_appledouble) {
    1332          19 :                 list = lp_veto_files(talloc_tos(), lp_sub, SNUM(handle->conn));
    1333             : 
    1334          19 :                 if (list) {
    1335          19 :                         if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
    1336          19 :                                 newlist = talloc_asprintf(
    1337             :                                         list,
    1338             :                                         "%s/" ADOUBLE_NAME_PREFIX "*/",
    1339             :                                         list);
    1340          19 :                                 lp_do_parameter(SNUM(handle->conn),
    1341             :                                                 "veto files",
    1342             :                                                 newlist);
    1343             :                         }
    1344             :                 } else {
    1345           0 :                         lp_do_parameter(SNUM(handle->conn),
    1346             :                                         "veto files",
    1347             :                                         "/" ADOUBLE_NAME_PREFIX "*/");
    1348             :                 }
    1349             : 
    1350          19 :                 TALLOC_FREE(list);
    1351             :         }
    1352             : 
    1353         363 :         if (config->encoding == FRUIT_ENC_NATIVE) {
    1354         101 :                 lp_do_parameter(SNUM(handle->conn),
    1355             :                                 "catia:mappings",
    1356             :                                 macos_string_replace_map);
    1357             :         }
    1358             : 
    1359         363 :         if (config->time_machine) {
    1360           8 :                 DBG_NOTICE("Enabling durable handles for Time Machine "
    1361             :                            "support on [%s]\n", service);
    1362           8 :                 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
    1363           8 :                 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
    1364           8 :                 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
    1365           8 :                 if (!lp_strict_sync(SNUM(handle->conn))) {
    1366           0 :                         DBG_WARNING("Time Machine without strict sync is not "
    1367             :                                     "recommended!\n");
    1368             :                 }
    1369           8 :                 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
    1370             :         }
    1371             : 
    1372         363 :         return rc;
    1373             : }
    1374             : 
    1375         822 : static void fio_ref_destroy_fn(void *p_data)
    1376             : {
    1377         822 :         struct fio *ref_fio = (struct fio *)p_data;
    1378         822 :         if (ref_fio->real_fio != NULL) {
    1379         822 :                 SMB_ASSERT(ref_fio->real_fio->ad_fsp == ref_fio->fsp);
    1380         822 :                 ref_fio->real_fio->ad_fsp = NULL;
    1381         822 :                 ref_fio->real_fio = NULL;
    1382             :         }
    1383         822 : }
    1384             : 
    1385        6139 : static void fio_close_ad_fsp(struct fio *fio)
    1386             : {
    1387        6139 :         if (fio->ad_fsp != NULL) {
    1388         822 :                 fd_close(fio->ad_fsp);
    1389         822 :                 file_free(NULL, fio->ad_fsp);
    1390             :                 /* fio_ref_destroy_fn() should have cleared this */
    1391         822 :                 SMB_ASSERT(fio->ad_fsp == NULL);
    1392             :         }
    1393        6139 : }
    1394             : 
    1395        5329 : static void fio_destroy_fn(void *p_data)
    1396             : {
    1397        5329 :         struct fio *fio = (struct fio *)p_data;
    1398        5329 :         fio_close_ad_fsp(fio);
    1399        5329 : }
    1400             : 
    1401        4020 : static int fruit_open_meta_stream(vfs_handle_struct *handle,
    1402             :                                   const struct files_struct *dirfsp,
    1403             :                                   const struct smb_filename *smb_fname,
    1404             :                                   files_struct *fsp,
    1405             :                                   int flags,
    1406             :                                   mode_t mode)
    1407             : {
    1408        4020 :         struct fruit_config_data *config = NULL;
    1409        4020 :         struct fio *fio = NULL;
    1410        4020 :         struct vfs_open_how how = {
    1411        4020 :                 .flags = flags & ~O_CREAT,
    1412             :                 .mode = mode,
    1413             :         };
    1414             :         int fd;
    1415             : 
    1416        4020 :         DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
    1417             : 
    1418        4020 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1419             :                                 struct fruit_config_data, return -1);
    1420             : 
    1421        4020 :         fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, fio_destroy_fn);
    1422        4020 :         fio->handle = handle;
    1423        4020 :         fio->fsp = fsp;
    1424        4020 :         fio->type = ADOUBLE_META;
    1425        4020 :         fio->config = config;
    1426             : 
    1427        4020 :         fd = SMB_VFS_NEXT_OPENAT(handle,
    1428             :                                  dirfsp,
    1429             :                                  smb_fname,
    1430             :                                  fsp,
    1431             :                                  &how);
    1432        4020 :         if (fd != -1) {
    1433        1452 :                 return fd;
    1434             :         }
    1435             : 
    1436        2568 :         if (!(flags & O_CREAT)) {
    1437        1465 :                 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
    1438        1465 :                 return -1;
    1439             :         }
    1440             : 
    1441        1103 :         fd = vfs_fake_fd();
    1442        1103 :         if (fd == -1) {
    1443           0 :                 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
    1444           0 :                 return -1;
    1445             :         }
    1446             : 
    1447        1103 :         fio->fake_fd = true;
    1448        1103 :         fio->flags = flags;
    1449        1103 :         fio->mode = mode;
    1450             : 
    1451        1103 :         return fd;
    1452             : }
    1453             : 
    1454        1300 : static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
    1455             :                                     const struct files_struct *dirfsp,
    1456             :                                     const struct smb_filename *smb_fname,
    1457             :                                     files_struct *fsp,
    1458             :                                     int flags,
    1459             :                                     mode_t mode)
    1460             : {
    1461        1300 :         struct fruit_config_data *config = NULL;
    1462        1300 :         struct fio *fio = NULL;
    1463        1300 :         struct adouble *ad = NULL;
    1464        1300 :         bool meta_exists = false;
    1465             :         int fd;
    1466             : 
    1467        1300 :         DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
    1468             : 
    1469             :         /*
    1470             :          * We know this is a stream open, so fsp->base_fsp must
    1471             :          * already be open.
    1472             :          */
    1473        1300 :         SMB_ASSERT(fsp_is_alternate_stream(fsp));
    1474        1300 :         SMB_ASSERT(fsp->base_fsp->fsp_name->fsp == fsp->base_fsp);
    1475             : 
    1476        1300 :         ad = ad_get(talloc_tos(), handle, fsp->base_fsp->fsp_name, ADOUBLE_META);
    1477        1300 :         if (ad != NULL) {
    1478         510 :                 meta_exists = true;
    1479             :         }
    1480             : 
    1481        1300 :         TALLOC_FREE(ad);
    1482             : 
    1483        1300 :         if (!meta_exists && !(flags & O_CREAT)) {
    1484         421 :                 errno = ENOENT;
    1485         421 :                 return -1;
    1486             :         }
    1487             : 
    1488         879 :         fd = vfs_fake_fd();
    1489         879 :         if (fd == -1) {
    1490           0 :                 return -1;
    1491             :         }
    1492             : 
    1493         879 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1494             :                                 struct fruit_config_data, return -1);
    1495             : 
    1496         879 :         fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, fio_destroy_fn);
    1497         879 :         fio->handle = handle;
    1498         879 :         fio->fsp = fsp;
    1499         879 :         fio->type = ADOUBLE_META;
    1500         879 :         fio->config = config;
    1501         879 :         fio->fake_fd = true;
    1502         879 :         fio->flags = flags;
    1503         879 :         fio->mode = mode;
    1504             : 
    1505         879 :         return fd;
    1506             : }
    1507             : 
    1508        5320 : static int fruit_open_meta(vfs_handle_struct *handle,
    1509             :                            const struct files_struct *dirfsp,
    1510             :                            const struct smb_filename *smb_fname,
    1511             :                            files_struct *fsp, int flags, mode_t mode)
    1512             : {
    1513             :         int fd;
    1514        5320 :         struct fruit_config_data *config = NULL;
    1515             : 
    1516        5320 :         DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
    1517             : 
    1518        5320 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1519             :                                 struct fruit_config_data, return -1);
    1520             : 
    1521        5320 :         switch (config->meta) {
    1522        4020 :         case FRUIT_META_STREAM:
    1523        4020 :                 fd = fruit_open_meta_stream(handle, dirfsp, smb_fname,
    1524             :                                             fsp, flags, mode);
    1525        4020 :                 break;
    1526             : 
    1527        1300 :         case FRUIT_META_NETATALK:
    1528        1300 :                 fd = fruit_open_meta_netatalk(handle, dirfsp, smb_fname,
    1529             :                                               fsp, flags, mode);
    1530        1300 :                 break;
    1531             : 
    1532           0 :         default:
    1533           0 :                 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
    1534           0 :                 return -1;
    1535             :         }
    1536             : 
    1537        5320 :         DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
    1538             : 
    1539        5320 :         return fd;
    1540             : }
    1541             : 
    1542        1012 : static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
    1543             :                                    const struct files_struct *dirfsp,
    1544             :                                    const struct smb_filename *smb_fname,
    1545             :                                    files_struct *fsp,
    1546             :                                    int flags,
    1547             :                                    mode_t mode)
    1548             : {
    1549        1012 :         int rc = 0;
    1550        1012 :         struct fruit_config_data *config = NULL;
    1551        1012 :         struct files_struct *ad_fsp = NULL;
    1552        1012 :         struct fio *fio = NULL;
    1553        1012 :         struct fio *ref_fio = NULL;
    1554             :         NTSTATUS status;
    1555        1012 :         int fd = -1;
    1556             : 
    1557        1012 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1558             :                                 struct fruit_config_data, return -1);
    1559             : 
    1560        1012 :         if ((!(flags & O_CREAT)) &&
    1561         874 :             S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
    1562             :         {
    1563             :                 /* sorry, but directories don't have a resource fork */
    1564           0 :                 errno = ENOENT;
    1565           0 :                 rc = -1;
    1566           0 :                 goto exit;
    1567             :         }
    1568             : 
    1569             :         /*
    1570             :          * We return a fake_fd to the vfs modules above,
    1571             :          * while we open an internal backend fsp for the
    1572             :          * '._' file for the next vfs modules.
    1573             :          *
    1574             :          * Note that adouble_open_from_base_fsp() recurses
    1575             :          * into fruit_openat(), but it'll just pass to
    1576             :          * the next module as just opens a flat file on
    1577             :          * disk.
    1578             :          */
    1579             : 
    1580        1012 :         fd = vfs_fake_fd();
    1581        1012 :         if (fd == -1) {
    1582           0 :                 rc = fd;
    1583           0 :                 goto exit;
    1584             :         }
    1585             : 
    1586        1012 :         status = adouble_open_from_base_fsp(fsp->conn->cwd_fsp,
    1587             :                                             fsp->base_fsp,
    1588             :                                             ADOUBLE_RSRC,
    1589             :                                             flags,
    1590             :                                             mode,
    1591             :                                             &ad_fsp);
    1592        1012 :         if (!NT_STATUS_IS_OK(status)) {
    1593         190 :                 errno = map_errno_from_nt_status(status);
    1594         190 :                 rc = -1;
    1595         190 :                 goto exit;
    1596             :         }
    1597             : 
    1598             :         /*
    1599             :          * Now we need to glue both handles together,
    1600             :          * so that they automatically detach each other
    1601             :          * on close.
    1602             :          */
    1603         822 :         fio = fruit_get_complete_fio(handle, fsp);
    1604         822 :         if (fio == NULL) {
    1605           0 :                 DBG_ERR("fio=NULL for [%s]\n", fsp_str_dbg(fsp));
    1606           0 :                 errno = EBADF;
    1607           0 :                 rc = -1;
    1608           0 :                 goto exit;
    1609             :         }
    1610             : 
    1611         822 :         ref_fio = VFS_ADD_FSP_EXTENSION(handle, ad_fsp,
    1612             :                                         struct fio,
    1613             :                                         fio_ref_destroy_fn);
    1614         822 :         if (ref_fio == NULL) {
    1615           0 :                 int saved_errno = errno;
    1616           0 :                 fd_close(ad_fsp);
    1617           0 :                 file_free(NULL, ad_fsp);
    1618           0 :                 ad_fsp = NULL;
    1619           0 :                 errno = saved_errno;
    1620           0 :                 rc = -1;
    1621           0 :                 goto exit;
    1622             :         }
    1623             : 
    1624         822 :         SMB_ASSERT(ref_fio->fsp == NULL);
    1625         822 :         ref_fio->handle = handle;
    1626         822 :         ref_fio->fsp = ad_fsp;
    1627         822 :         ref_fio->type = ADOUBLE_RSRC;
    1628         822 :         ref_fio->config = config;
    1629         822 :         ref_fio->real_fio = fio;
    1630         822 :         SMB_ASSERT(fio->ad_fsp == NULL);
    1631         822 :         fio->ad_fsp = ad_fsp;
    1632         822 :         fio->fake_fd = true;
    1633             : 
    1634        1012 : exit:
    1635             : 
    1636        1012 :         DEBUG(10, ("fruit_open resource fork: rc=%d\n", rc));
    1637        1012 :         if (rc != 0) {
    1638         190 :                 int saved_errno = errno;
    1639         190 :                 if (fd != -1) {
    1640         190 :                         vfs_fake_fd_close(fd);
    1641             :                 }
    1642         190 :                 errno = saved_errno;
    1643         190 :                 return rc;
    1644             :         }
    1645         822 :         return fd;
    1646             : }
    1647             : 
    1648           0 : static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
    1649             :                                  const struct files_struct *dirfsp,
    1650             :                                  const struct smb_filename *smb_fname,
    1651             :                                  files_struct *fsp,
    1652             :                                  int flags,
    1653             :                                  mode_t mode)
    1654             : {
    1655             : #ifdef HAVE_ATTROPEN
    1656             :         int fd = -1;
    1657             : 
    1658             :         /*
    1659             :          * As there's no attropenat() this is only going to work with AT_FDCWD.
    1660             :          */
    1661             :         SMB_ASSERT(fsp_get_pathref_fd(dirfsp) == AT_FDCWD);
    1662             : 
    1663             :         fd = attropen(smb_fname->base_name,
    1664             :                       AFPRESOURCE_EA_NETATALK,
    1665             :                       flags,
    1666             :                       mode);
    1667             :         if (fd == -1) {
    1668             :                 return -1;
    1669             :         }
    1670             : 
    1671             :         return fd;
    1672             : 
    1673             : #else
    1674           0 :         errno = ENOSYS;
    1675           0 :         return -1;
    1676             : #endif
    1677             : }
    1678             : 
    1679        1344 : static int fruit_open_rsrc(vfs_handle_struct *handle,
    1680             :                            const struct files_struct *dirfsp,
    1681             :                            const struct smb_filename *smb_fname,
    1682             :                            files_struct *fsp, int flags, mode_t mode)
    1683             : {
    1684             :         int fd;
    1685        1344 :         struct fruit_config_data *config = NULL;
    1686        1344 :         struct fio *fio = NULL;
    1687             : 
    1688        1344 :         DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
    1689             : 
    1690        1344 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1691             :                                 struct fruit_config_data, return -1);
    1692             : 
    1693        1344 :         fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, fio_destroy_fn);
    1694        1344 :         fio->handle = handle;
    1695        1344 :         fio->fsp = fsp;
    1696        1344 :         fio->type = ADOUBLE_RSRC;
    1697        1344 :         fio->config = config;
    1698             : 
    1699        1344 :         switch (config->rsrc) {
    1700         332 :         case FRUIT_RSRC_STREAM: {
    1701         332 :                 struct vfs_open_how how = {
    1702             :                         .flags = flags, .mode = mode,
    1703             :                 };
    1704         332 :                 fd = SMB_VFS_NEXT_OPENAT(handle,
    1705             :                                          dirfsp,
    1706             :                                          smb_fname,
    1707             :                                          fsp,
    1708             :                                          &how);
    1709         332 :                 break;
    1710             :         }
    1711             : 
    1712        1012 :         case FRUIT_RSRC_ADFILE:
    1713        1012 :                 fd = fruit_open_rsrc_adouble(handle, dirfsp, smb_fname,
    1714             :                                              fsp, flags, mode);
    1715        1012 :                 break;
    1716             : 
    1717           0 :         case FRUIT_RSRC_XATTR:
    1718           0 :                 fd = fruit_open_rsrc_xattr(handle, dirfsp, smb_fname,
    1719             :                                            fsp, flags, mode);
    1720           0 :                 break;
    1721             : 
    1722           0 :         default:
    1723           0 :                 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
    1724           0 :                 errno = EINVAL;
    1725           0 :                 return -1;
    1726             :         }
    1727             : 
    1728        1344 :         DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
    1729             : 
    1730        1344 :         if (fd == -1) {
    1731         244 :                 return -1;
    1732             :         }
    1733             : 
    1734        1100 :         return fd;
    1735             : }
    1736             : 
    1737       95802 : static int fruit_openat(vfs_handle_struct *handle,
    1738             :                         const struct files_struct *dirfsp,
    1739             :                         const struct smb_filename *smb_fname,
    1740             :                         files_struct *fsp,
    1741             :                         const struct vfs_open_how *how)
    1742             : {
    1743             :         int fd;
    1744             : 
    1745       95802 :         DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
    1746             : 
    1747       95802 :         if (!is_named_stream(smb_fname)) {
    1748       88064 :                 return SMB_VFS_NEXT_OPENAT(handle,
    1749             :                                            dirfsp,
    1750             :                                            smb_fname,
    1751             :                                            fsp,
    1752             :                                            how);
    1753             :         }
    1754             : 
    1755        7738 :         if (how->resolve != 0) {
    1756           0 :                 errno = ENOSYS;
    1757           0 :                 return -1;
    1758             :         }
    1759             : 
    1760        7738 :         SMB_ASSERT(fsp_is_alternate_stream(fsp));
    1761             : 
    1762        7738 :         if (is_afpinfo_stream(smb_fname->stream_name)) {
    1763        5320 :                 fd = fruit_open_meta(handle,
    1764             :                                      dirfsp,
    1765             :                                      smb_fname,
    1766             :                                      fsp,
    1767        5320 :                                      how->flags,
    1768        5320 :                                      how->mode);
    1769        2418 :         } else if (is_afpresource_stream(smb_fname->stream_name)) {
    1770        1344 :                 fd = fruit_open_rsrc(handle,
    1771             :                                      dirfsp,
    1772             :                                      smb_fname,
    1773             :                                      fsp,
    1774        1344 :                                      how->flags,
    1775        1344 :                                      how->mode);
    1776             :         } else {
    1777        1074 :                 fd = SMB_VFS_NEXT_OPENAT(handle,
    1778             :                                          dirfsp,
    1779             :                                          smb_fname,
    1780             :                                          fsp,
    1781             :                                          how);
    1782             :         }
    1783             : 
    1784        7738 :         DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
    1785             : 
    1786             :         /* Prevent reopen optimisation */
    1787        7738 :         fsp->fsp_flags.have_proc_fds = false;
    1788        7738 :         return fd;
    1789             : }
    1790             : 
    1791        3434 : static int fruit_close_meta(vfs_handle_struct *handle,
    1792             :                             files_struct *fsp)
    1793             : {
    1794             :         int ret;
    1795        3434 :         struct fruit_config_data *config = NULL;
    1796             : 
    1797        3434 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1798             :                                 struct fruit_config_data, return -1);
    1799             : 
    1800        3434 :         switch (config->meta) {
    1801        2555 :         case FRUIT_META_STREAM:
    1802             :         {
    1803        2555 :                 struct fio *fio = fruit_get_complete_fio(handle, fsp);
    1804        2555 :                 if (fio == NULL) {
    1805           0 :                         return -1;
    1806             :                 }
    1807        2555 :                 if (fio->fake_fd) {
    1808         738 :                         ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
    1809         738 :                         fsp_set_fd(fsp, -1);
    1810             :                 } else {
    1811        1817 :                         ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
    1812             :                 }
    1813        2555 :                 break;
    1814             :         }
    1815         879 :         case FRUIT_META_NETATALK:
    1816         879 :                 ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
    1817         879 :                 fsp_set_fd(fsp, -1);
    1818         879 :                 break;
    1819             : 
    1820           0 :         default:
    1821           0 :                 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
    1822           0 :                 return -1;
    1823             :         }
    1824             : 
    1825        3434 :         return ret;
    1826             : }
    1827             : 
    1828             : 
    1829        1084 : static int fruit_close_rsrc(vfs_handle_struct *handle,
    1830             :                             files_struct *fsp)
    1831             : {
    1832             :         int ret;
    1833        1084 :         struct fruit_config_data *config = NULL;
    1834             : 
    1835        1084 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1836             :                                 struct fruit_config_data, return -1);
    1837             : 
    1838        1084 :         switch (config->rsrc) {
    1839         274 :         case FRUIT_RSRC_STREAM:
    1840         274 :                 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
    1841         274 :                 break;
    1842             : 
    1843         810 :         case FRUIT_RSRC_ADFILE:
    1844             :         {
    1845         810 :                 struct fio *fio = fruit_get_complete_fio(handle, fsp);
    1846         810 :                 if (fio == NULL) {
    1847           0 :                         return -1;
    1848             :                 }
    1849         810 :                 fio_close_ad_fsp(fio);
    1850         810 :                 ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
    1851         810 :                 fsp_set_fd(fsp, -1);
    1852         810 :                 break;
    1853             :         }
    1854             : 
    1855           0 :         case FRUIT_RSRC_XATTR:
    1856           0 :                 ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
    1857           0 :                 fsp_set_fd(fsp, -1);
    1858           0 :                 break;
    1859             : 
    1860           0 :         default:
    1861           0 :                 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
    1862           0 :                 return -1;
    1863             :         }
    1864             : 
    1865        1084 :         return ret;
    1866             : }
    1867             : 
    1868       64499 : static int fruit_close(vfs_handle_struct *handle,
    1869             :                        files_struct *fsp)
    1870             : {
    1871             :         int ret;
    1872             :         int fd;
    1873             : 
    1874       64499 :         fd = fsp_get_pathref_fd(fsp);
    1875             : 
    1876       64499 :         DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
    1877             : 
    1878       64499 :         if (!fsp_is_alternate_stream(fsp)) {
    1879       59069 :                 return SMB_VFS_NEXT_CLOSE(handle, fsp);
    1880             :         }
    1881             : 
    1882        5430 :         if (is_afpinfo_stream(fsp->fsp_name->stream_name)) {
    1883        3434 :                 ret = fruit_close_meta(handle, fsp);
    1884        1996 :         } else if (is_afpresource_stream(fsp->fsp_name->stream_name)) {
    1885        1084 :                 ret = fruit_close_rsrc(handle, fsp);
    1886             :         } else {
    1887         912 :                 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
    1888             :         }
    1889             : 
    1890        5430 :         return ret;
    1891             : }
    1892             : 
    1893           8 : static int fruit_renameat(struct vfs_handle_struct *handle,
    1894             :                         files_struct *srcfsp,
    1895             :                         const struct smb_filename *smb_fname_src,
    1896             :                         files_struct *dstfsp,
    1897             :                         const struct smb_filename *smb_fname_dst)
    1898             : {
    1899           8 :         int rc = -1;
    1900           8 :         struct fruit_config_data *config = NULL;
    1901           8 :         struct smb_filename *src_adp_smb_fname = NULL;
    1902           8 :         struct smb_filename *dst_adp_smb_fname = NULL;
    1903             : 
    1904           8 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1905             :                                 struct fruit_config_data, return -1);
    1906             : 
    1907           8 :         if (!VALID_STAT(smb_fname_src->st)) {
    1908           0 :                 DBG_ERR("Need valid stat for [%s]\n",
    1909             :                         smb_fname_str_dbg(smb_fname_src));
    1910           0 :                 return -1;
    1911             :         }
    1912             : 
    1913           8 :         rc = SMB_VFS_NEXT_RENAMEAT(handle,
    1914             :                                 srcfsp,
    1915             :                                 smb_fname_src,
    1916             :                                 dstfsp,
    1917             :                                 smb_fname_dst);
    1918           8 :         if (rc != 0) {
    1919           0 :                 return -1;
    1920             :         }
    1921             : 
    1922           8 :         if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
    1923           6 :             (!S_ISREG(smb_fname_src->st.st_ex_mode)))
    1924             :         {
    1925           8 :                 return 0;
    1926             :         }
    1927             : 
    1928           0 :         rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
    1929           0 :         if (rc != 0) {
    1930           0 :                 goto done;
    1931             :         }
    1932             : 
    1933           0 :         rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
    1934           0 :         if (rc != 0) {
    1935           0 :                 goto done;
    1936             :         }
    1937             : 
    1938           0 :         DBG_DEBUG("%s -> %s\n",
    1939             :                   smb_fname_str_dbg(src_adp_smb_fname),
    1940             :                   smb_fname_str_dbg(dst_adp_smb_fname));
    1941             : 
    1942           0 :         rc = SMB_VFS_NEXT_RENAMEAT(handle,
    1943             :                         srcfsp,
    1944             :                         src_adp_smb_fname,
    1945             :                         dstfsp,
    1946             :                         dst_adp_smb_fname);
    1947           0 :         if (errno == ENOENT) {
    1948           0 :                 rc = 0;
    1949             :         }
    1950             : 
    1951           0 : done:
    1952           0 :         TALLOC_FREE(src_adp_smb_fname);
    1953           0 :         TALLOC_FREE(dst_adp_smb_fname);
    1954           0 :         return rc;
    1955             : }
    1956             : 
    1957         361 : static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
    1958             :                                 struct files_struct *dirfsp,
    1959             :                                 const struct smb_filename *smb_fname)
    1960             : {
    1961         361 :         return SMB_VFS_NEXT_UNLINKAT(handle,
    1962             :                                 dirfsp,
    1963             :                                 smb_fname,
    1964             :                                 0);
    1965             : }
    1966             : 
    1967         125 : static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
    1968             :                                       const struct smb_filename *smb_fname)
    1969             : {
    1970         125 :         SMB_ASSERT(smb_fname->fsp != NULL);
    1971         125 :         SMB_ASSERT(fsp_is_alternate_stream(smb_fname->fsp));
    1972         125 :         return SMB_VFS_FREMOVEXATTR(smb_fname->fsp->base_fsp,
    1973             :                                    AFPINFO_EA_NETATALK);
    1974             : }
    1975             : 
    1976         486 : static int fruit_unlink_meta(vfs_handle_struct *handle,
    1977             :                         struct files_struct *dirfsp,
    1978             :                         const struct smb_filename *smb_fname)
    1979             : {
    1980         486 :         struct fruit_config_data *config = NULL;
    1981             :         int rc;
    1982             : 
    1983         486 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    1984             :                                 struct fruit_config_data, return -1);
    1985             : 
    1986         486 :         switch (config->meta) {
    1987         361 :         case FRUIT_META_STREAM:
    1988         361 :                 rc = fruit_unlink_meta_stream(handle,
    1989             :                                 dirfsp,
    1990             :                                 smb_fname);
    1991         361 :                 break;
    1992             : 
    1993         125 :         case FRUIT_META_NETATALK:
    1994         125 :                 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
    1995         125 :                 break;
    1996             : 
    1997           0 :         default:
    1998           0 :                 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
    1999           0 :                 return -1;
    2000             :         }
    2001             : 
    2002         486 :         return rc;
    2003             : }
    2004             : 
    2005         163 : static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
    2006             :                                 struct files_struct *dirfsp,
    2007             :                                 const struct smb_filename *smb_fname,
    2008             :                                 bool force_unlink)
    2009             : {
    2010             :         int ret;
    2011             : 
    2012         163 :         if (!force_unlink) {
    2013          38 :                 struct smb_filename *full_fname = NULL;
    2014             :                 off_t size;
    2015             : 
    2016             :                 /*
    2017             :                  * TODO: use SMB_VFS_STATX() once we have it.
    2018             :                  */
    2019             : 
    2020          38 :                 full_fname = full_path_from_dirfsp_atname(talloc_tos(),
    2021             :                                                           dirfsp,
    2022             :                                                           smb_fname);
    2023          38 :                 if (full_fname == NULL) {
    2024           0 :                         return -1;
    2025             :                 }
    2026             : 
    2027             :                 /*
    2028             :                  * 0 byte resource fork streams are not listed by
    2029             :                  * vfs_streaminfo, as a result stream cleanup/deletion of file
    2030             :                  * deletion doesn't remove the resourcefork stream.
    2031             :                  */
    2032             : 
    2033          38 :                 ret = SMB_VFS_NEXT_STAT(handle, full_fname);
    2034          38 :                 if (ret != 0) {
    2035           0 :                         TALLOC_FREE(full_fname);
    2036           0 :                         DBG_ERR("stat [%s] failed [%s]\n",
    2037             :                                 smb_fname_str_dbg(full_fname), strerror(errno));
    2038           0 :                         return -1;
    2039             :                 }
    2040             : 
    2041          38 :                 size = full_fname->st.st_ex_size;
    2042          38 :                 TALLOC_FREE(full_fname);
    2043             : 
    2044          38 :                 if (size > 0) {
    2045             :                         /* OS X ignores resource fork stream delete requests */
    2046          38 :                         return 0;
    2047             :                 }
    2048             :         }
    2049             : 
    2050         125 :         ret = SMB_VFS_NEXT_UNLINKAT(handle,
    2051             :                         dirfsp,
    2052             :                         smb_fname,
    2053             :                         0);
    2054         125 :         if ((ret != 0) && (errno == ENOENT) && force_unlink) {
    2055          81 :                 ret = 0;
    2056             :         }
    2057             : 
    2058         125 :         return ret;
    2059             : }
    2060             : 
    2061         702 : static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
    2062             :                                 struct files_struct *dirfsp,
    2063             :                                 const struct smb_filename *smb_fname,
    2064             :                                 bool force_unlink)
    2065             : {
    2066             :         int rc;
    2067         702 :         struct adouble *ad = NULL;
    2068         702 :         struct smb_filename *adp_smb_fname = NULL;
    2069             : 
    2070         702 :         if (!force_unlink) {
    2071         120 :                 struct smb_filename *full_fname = NULL;
    2072             : 
    2073         120 :                 full_fname = full_path_from_dirfsp_atname(talloc_tos(),
    2074             :                                                           dirfsp,
    2075             :                                                           smb_fname);
    2076         120 :                 if (full_fname == NULL) {
    2077           0 :                         return -1;
    2078             :                 }
    2079             : 
    2080         120 :                 ad = ad_get(talloc_tos(), handle, full_fname,
    2081             :                             ADOUBLE_RSRC);
    2082         120 :                 TALLOC_FREE(full_fname);
    2083         120 :                 if (ad == NULL) {
    2084           0 :                         errno = ENOENT;
    2085           0 :                         return -1;
    2086             :                 }
    2087             : 
    2088             : 
    2089             :                 /*
    2090             :                  * 0 byte resource fork streams are not listed by
    2091             :                  * vfs_streaminfo, as a result stream cleanup/deletion of file
    2092             :                  * deletion doesn't remove the resourcefork stream.
    2093             :                  */
    2094             : 
    2095         120 :                 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
    2096             :                         /* OS X ignores resource fork stream delete requests */
    2097         120 :                         TALLOC_FREE(ad);
    2098         120 :                         return 0;
    2099             :                 }
    2100             : 
    2101           0 :                 TALLOC_FREE(ad);
    2102             :         }
    2103             : 
    2104         582 :         rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
    2105         582 :         if (rc != 0) {
    2106           0 :                 return -1;
    2107             :         }
    2108             : 
    2109         582 :         rc = SMB_VFS_NEXT_UNLINKAT(handle,
    2110             :                         dirfsp,
    2111             :                         adp_smb_fname,
    2112             :                         0);
    2113         582 :         TALLOC_FREE(adp_smb_fname);
    2114         582 :         if ((rc != 0) && (errno == ENOENT || errno == ENAMETOOLONG) && force_unlink) {
    2115         454 :                 rc = 0;
    2116             :         }
    2117             : 
    2118         582 :         return rc;
    2119             : }
    2120             : 
    2121           0 : static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
    2122             :                                    const struct smb_filename *smb_fname,
    2123             :                                    bool force_unlink)
    2124             : {
    2125             :         /*
    2126             :          * OS X ignores resource fork stream delete requests, so nothing to do
    2127             :          * here. Removing the file will remove the xattr anyway, so we don't
    2128             :          * have to take care of removing 0 byte resource forks that could be
    2129             :          * left behind.
    2130             :          */
    2131           0 :         return 0;
    2132             : }
    2133             : 
    2134         865 : static int fruit_unlink_rsrc(vfs_handle_struct *handle,
    2135             :                         struct files_struct *dirfsp,
    2136             :                         const struct smb_filename *smb_fname,
    2137             :                         bool force_unlink)
    2138             : {
    2139         865 :         struct fruit_config_data *config = NULL;
    2140             :         int rc;
    2141             : 
    2142         865 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    2143             :                                 struct fruit_config_data, return -1);
    2144             : 
    2145         865 :         switch (config->rsrc) {
    2146         163 :         case FRUIT_RSRC_STREAM:
    2147         163 :                 rc = fruit_unlink_rsrc_stream(handle,
    2148             :                                 dirfsp,
    2149             :                                 smb_fname,
    2150             :                                 force_unlink);
    2151         163 :                 break;
    2152             : 
    2153         702 :         case FRUIT_RSRC_ADFILE:
    2154         702 :                 rc = fruit_unlink_rsrc_adouble(handle,
    2155             :                                 dirfsp,
    2156             :                                 smb_fname,
    2157             :                                 force_unlink);
    2158         702 :                 break;
    2159             : 
    2160           0 :         case FRUIT_RSRC_XATTR:
    2161           0 :                 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
    2162           0 :                 break;
    2163             : 
    2164           0 :         default:
    2165           0 :                 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
    2166           0 :                 return -1;
    2167             :         }
    2168             : 
    2169         865 :         return rc;
    2170             : }
    2171             : 
    2172           8 : static int fruit_fchmod(vfs_handle_struct *handle,
    2173             :                       struct files_struct *fsp,
    2174             :                       mode_t mode)
    2175             : {
    2176           8 :         int rc = -1;
    2177           8 :         struct fruit_config_data *config = NULL;
    2178           8 :         struct smb_filename *smb_fname_adp = NULL;
    2179           8 :         const struct smb_filename *smb_fname = NULL;
    2180             :         NTSTATUS status;
    2181             : 
    2182           8 :         rc = SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
    2183           8 :         if (rc != 0) {
    2184           0 :                 return rc;
    2185             :         }
    2186             : 
    2187           8 :         smb_fname = fsp->fsp_name;
    2188           8 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    2189             :                                 struct fruit_config_data, return -1);
    2190             : 
    2191           8 :         if (config->rsrc != FRUIT_RSRC_ADFILE) {
    2192           2 :                 return 0;
    2193             :         }
    2194             : 
    2195           6 :         if (!VALID_STAT(smb_fname->st)) {
    2196           0 :                 return 0;
    2197             :         }
    2198             : 
    2199           6 :         if (!S_ISREG(smb_fname->st.st_ex_mode)) {
    2200           0 :                 return 0;
    2201             :         }
    2202             : 
    2203           6 :         rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
    2204           6 :         if (rc != 0) {
    2205           0 :                 return -1;
    2206             :         }
    2207             : 
    2208           6 :         status = openat_pathref_fsp(handle->conn->cwd_fsp,
    2209             :                                     smb_fname_adp);
    2210           6 :         if (!NT_STATUS_IS_OK(status)) {
    2211             :                 /* detect ENOENT (mapped to OBJECT_NAME_NOT_FOUND) */
    2212           6 :                 if (NT_STATUS_EQUAL(status,
    2213             :                                     NT_STATUS_OBJECT_NAME_NOT_FOUND)){
    2214           6 :                         rc = 0;
    2215           6 :                         goto out;
    2216             :                 }
    2217           0 :                 rc = -1;
    2218           0 :                 goto out;
    2219             :         }
    2220             : 
    2221           0 :         DBG_DEBUG("%s\n", smb_fname_adp->base_name);
    2222             : 
    2223           0 :         rc = SMB_VFS_NEXT_FCHMOD(handle, smb_fname_adp->fsp, mode);
    2224           0 :         if (errno == ENOENT) {
    2225           0 :                 rc = 0;
    2226             :         }
    2227           0 : out:
    2228           6 :         TALLOC_FREE(smb_fname_adp);
    2229           6 :         return rc;
    2230             : }
    2231             : 
    2232        1901 : static int fruit_unlinkat(vfs_handle_struct *handle,
    2233             :                         struct files_struct *dirfsp,
    2234             :                         const struct smb_filename *smb_fname,
    2235             :                         int flags)
    2236             : {
    2237        1901 :         struct fruit_config_data *config = NULL;
    2238        1901 :         struct smb_filename *rsrc_smb_fname = NULL;
    2239             :         int ret;
    2240             : 
    2241        1901 :         if (flags & AT_REMOVEDIR) {
    2242         422 :                 return SMB_VFS_NEXT_UNLINKAT(handle,
    2243             :                                              dirfsp,
    2244             :                                              smb_fname,
    2245             :                                              AT_REMOVEDIR);
    2246             :         }
    2247             : 
    2248        1479 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    2249             :                                 struct fruit_config_data, return -1);
    2250             : 
    2251        1479 :         if (is_afpinfo_stream(smb_fname->stream_name)) {
    2252         486 :                 return fruit_unlink_meta(handle,
    2253             :                                 dirfsp,
    2254             :                                 smb_fname);
    2255         993 :         } else if (is_afpresource_stream(smb_fname->stream_name)) {
    2256         158 :                 return fruit_unlink_rsrc(handle,
    2257             :                                 dirfsp,
    2258             :                                 smb_fname,
    2259             :                                 false);
    2260         835 :         } else if (is_named_stream(smb_fname)) {
    2261         110 :                 return SMB_VFS_NEXT_UNLINKAT(handle,
    2262             :                                 dirfsp,
    2263             :                                 smb_fname,
    2264             :                                 0);
    2265         725 :         } else if (is_adouble_file(smb_fname->base_name)) {
    2266          18 :                 return SMB_VFS_NEXT_UNLINKAT(handle,
    2267             :                                 dirfsp,
    2268             :                                 smb_fname,
    2269             :                                 0);
    2270             :         }
    2271             : 
    2272             :         /*
    2273             :          * A request to delete the base file. Because 0 byte resource
    2274             :          * fork streams are not listed by fruit_streaminfo,
    2275             :          * delete_all_streams() can't remove 0 byte resource fork
    2276             :          * streams, so we have to cleanup this here.
    2277             :          */
    2278         707 :         rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
    2279         707 :                                              smb_fname->base_name,
    2280             :                                              AFPRESOURCE_STREAM_NAME,
    2281             :                                              NULL,
    2282         707 :                                              smb_fname->twrp,
    2283         707 :                                              smb_fname->flags);
    2284         707 :         if (rsrc_smb_fname == NULL) {
    2285           0 :                 return -1;
    2286             :         }
    2287             : 
    2288         707 :         ret = fruit_unlink_rsrc(handle, dirfsp, rsrc_smb_fname, true);
    2289         707 :         if ((ret != 0) && (errno != ENOENT)) {
    2290           0 :                 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
    2291             :                         smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
    2292           0 :                 TALLOC_FREE(rsrc_smb_fname);
    2293           0 :                 return -1;
    2294             :         }
    2295         707 :         TALLOC_FREE(rsrc_smb_fname);
    2296             : 
    2297         707 :         return SMB_VFS_NEXT_UNLINKAT(handle,
    2298             :                         dirfsp,
    2299             :                         smb_fname,
    2300             :                         0);
    2301             : }
    2302             : 
    2303         405 : static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
    2304             :                                        files_struct *fsp, void *data,
    2305             :                                        size_t n, off_t offset)
    2306             : {
    2307         405 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    2308             :         ssize_t nread;
    2309             :         int ret;
    2310             : 
    2311         405 :         if ((fio == NULL) || fio->fake_fd) {
    2312          12 :                 return -1;
    2313             :         }
    2314             : 
    2315         393 :         nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
    2316         393 :         if (nread == -1 || nread == n) {
    2317         393 :                 return nread;
    2318             :         }
    2319             : 
    2320           0 :         DBG_ERR("Removing [%s] after short read [%zd]\n",
    2321             :                 fsp_str_dbg(fsp), nread);
    2322             : 
    2323           0 :         ret = SMB_VFS_NEXT_UNLINKAT(handle,
    2324             :                         fsp->conn->cwd_fsp,
    2325             :                         fsp->fsp_name,
    2326             :                         0);
    2327           0 :         if (ret != 0) {
    2328           0 :                 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
    2329           0 :                 return -1;
    2330             :         }
    2331             : 
    2332           0 :         errno = EINVAL;
    2333           0 :         return -1;
    2334             : }
    2335             : 
    2336         139 : static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
    2337             :                                         files_struct *fsp, void *data,
    2338             :                                         size_t n, off_t offset)
    2339             : {
    2340         139 :         AfpInfo *ai = NULL;
    2341         139 :         struct adouble *ad = NULL;
    2342             :         char afpinfo_buf[AFP_INFO_SIZE];
    2343         139 :         char *p = NULL;
    2344             :         ssize_t nread;
    2345             : 
    2346         139 :         ai = afpinfo_new(talloc_tos());
    2347         139 :         if (ai == NULL) {
    2348           0 :                 return -1;
    2349             :         }
    2350             : 
    2351         139 :         ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
    2352         139 :         if (ad == NULL) {
    2353           4 :                 nread = -1;
    2354           4 :                 goto fail;
    2355             :         }
    2356             : 
    2357         135 :         p = ad_get_entry(ad, ADEID_FINDERI);
    2358         135 :         if (p == NULL) {
    2359           0 :                 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
    2360           0 :                 nread = -1;
    2361           0 :                 goto fail;
    2362             :         }
    2363             : 
    2364         135 :         memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
    2365             : 
    2366         135 :         nread = afpinfo_pack(ai, afpinfo_buf);
    2367         135 :         if (nread != AFP_INFO_SIZE) {
    2368           0 :                 nread = -1;
    2369           0 :                 goto fail;
    2370             :         }
    2371             : 
    2372         135 :         memcpy(data, afpinfo_buf, n);
    2373         135 :         nread = n;
    2374             : 
    2375         139 : fail:
    2376         139 :         TALLOC_FREE(ai);
    2377         139 :         return nread;
    2378             : }
    2379             : 
    2380         554 : static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
    2381             :                                 files_struct *fsp, void *data,
    2382             :                                 size_t n, off_t offset)
    2383             : {
    2384         554 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    2385             :         ssize_t nread;
    2386             :         ssize_t to_return;
    2387             : 
    2388             :         /*
    2389             :          * OS X has a off-by-1 error in the offset calculation, so we're
    2390             :          * bug compatible here. It won't hurt, as any relevant real
    2391             :          * world read requests from the AFP_AfpInfo stream will be
    2392             :          * offset=0 n=60. offset is ignored anyway, see below.
    2393             :          */
    2394         554 :         if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
    2395          10 :                 return 0;
    2396             :         }
    2397             : 
    2398         544 :         if (fio == NULL) {
    2399           0 :                 DBG_ERR("Failed to fetch fsp extension\n");
    2400           0 :                 return -1;
    2401             :         }
    2402             : 
    2403             :         /* Yes, macOS always reads from offset 0 */
    2404         544 :         offset = 0;
    2405         544 :         to_return = MIN(n, AFP_INFO_SIZE);
    2406             : 
    2407         544 :         switch (fio->config->meta) {
    2408         405 :         case FRUIT_META_STREAM:
    2409         405 :                 nread = fruit_pread_meta_stream(handle, fsp, data,
    2410             :                                                 to_return, offset);
    2411         405 :                 break;
    2412             : 
    2413         139 :         case FRUIT_META_NETATALK:
    2414         139 :                 nread = fruit_pread_meta_adouble(handle, fsp, data,
    2415             :                                                  to_return, offset);
    2416         139 :                 break;
    2417             : 
    2418           0 :         default:
    2419           0 :                 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
    2420           0 :                 return -1;
    2421             :         }
    2422             : 
    2423         544 :         if (nread == -1 && fio->fake_fd) {
    2424          16 :                 AfpInfo *ai = NULL;
    2425             :                 char afpinfo_buf[AFP_INFO_SIZE];
    2426             : 
    2427          16 :                 ai = afpinfo_new(talloc_tos());
    2428          16 :                 if (ai == NULL) {
    2429           0 :                         return -1;
    2430             :                 }
    2431             : 
    2432          16 :                 nread = afpinfo_pack(ai, afpinfo_buf);
    2433          16 :                 TALLOC_FREE(ai);
    2434          16 :                 if (nread != AFP_INFO_SIZE) {
    2435           0 :                         return -1;
    2436             :                 }
    2437             : 
    2438          16 :                 memcpy(data, afpinfo_buf, to_return);
    2439          16 :                 return to_return;
    2440             :         }
    2441             : 
    2442         528 :         return nread;
    2443             : }
    2444             : 
    2445          20 : static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
    2446             :                                        files_struct *fsp, void *data,
    2447             :                                        size_t n, off_t offset)
    2448             : {
    2449          20 :         return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
    2450             : }
    2451             : 
    2452           0 : static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
    2453             :                                       files_struct *fsp, void *data,
    2454             :                                       size_t n, off_t offset)
    2455             : {
    2456           0 :         return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
    2457             : }
    2458             : 
    2459          76 : static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
    2460             :                                         files_struct *fsp, void *data,
    2461             :                                         size_t n, off_t offset)
    2462             : {
    2463          76 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    2464          76 :         struct adouble *ad = NULL;
    2465             :         ssize_t nread;
    2466             : 
    2467          76 :         if (fio == NULL || fio->ad_fsp == NULL) {
    2468           0 :                 DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
    2469           0 :                 errno = EBADF;
    2470           0 :                 return -1;
    2471             :         }
    2472             : 
    2473          76 :         ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
    2474          76 :         if (ad == NULL) {
    2475           0 :                 DBG_ERR("ad_fget [%s] failed [%s]\n",
    2476             :                         fsp_str_dbg(fio->ad_fsp), strerror(errno));
    2477           0 :                 return -1;
    2478             :         }
    2479             : 
    2480          76 :         nread = SMB_VFS_NEXT_PREAD(handle, fio->ad_fsp, data, n,
    2481             :                                    offset + ad_getentryoff(ad, ADEID_RFORK));
    2482             : 
    2483          76 :         TALLOC_FREE(ad);
    2484          76 :         return nread;
    2485             : }
    2486             : 
    2487          96 : static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
    2488             :                                 files_struct *fsp, void *data,
    2489             :                                 size_t n, off_t offset)
    2490             : {
    2491          96 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    2492             :         ssize_t nread;
    2493             : 
    2494          96 :         if (fio == NULL) {
    2495           0 :                 errno = EINVAL;
    2496           0 :                 return -1;
    2497             :         }
    2498             : 
    2499          96 :         switch (fio->config->rsrc) {
    2500          20 :         case FRUIT_RSRC_STREAM:
    2501          20 :                 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
    2502          20 :                 break;
    2503             : 
    2504          76 :         case FRUIT_RSRC_ADFILE:
    2505          76 :                 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
    2506          76 :                 break;
    2507             : 
    2508           0 :         case FRUIT_RSRC_XATTR:
    2509           0 :                 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
    2510           0 :                 break;
    2511             : 
    2512           0 :         default:
    2513           0 :                 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
    2514           0 :                 return -1;
    2515             :         }
    2516             : 
    2517          96 :         return nread;
    2518             : }
    2519             : 
    2520         752 : static ssize_t fruit_pread(vfs_handle_struct *handle,
    2521             :                            files_struct *fsp, void *data,
    2522             :                            size_t n, off_t offset)
    2523             : {
    2524         752 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    2525             :         ssize_t nread;
    2526             : 
    2527         752 :         DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
    2528             :                   fsp_str_dbg(fsp), (intmax_t)offset, n);
    2529             : 
    2530         752 :         if (fio == NULL) {
    2531         102 :                 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
    2532             :         }
    2533             : 
    2534         650 :         if (fio->type == ADOUBLE_META) {
    2535         554 :                 nread = fruit_pread_meta(handle, fsp, data, n, offset);
    2536             :         } else {
    2537          96 :                 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
    2538             :         }
    2539             : 
    2540         650 :         DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
    2541         650 :         return nread;
    2542             : }
    2543             : 
    2544         116 : static bool fruit_must_handle_aio_stream(struct fio *fio)
    2545             : {
    2546         116 :         if (fio == NULL) {
    2547          76 :                 return false;
    2548             :         };
    2549             : 
    2550          40 :         if (fio->type == ADOUBLE_META) {
    2551          16 :                 return true;
    2552             :         }
    2553             : 
    2554          24 :         if ((fio->type == ADOUBLE_RSRC) &&
    2555          24 :             (fio->config->rsrc == FRUIT_RSRC_ADFILE))
    2556             :         {
    2557          18 :                 return true;
    2558             :         }
    2559             : 
    2560           6 :         return false;
    2561             : }
    2562             : 
    2563             : struct fruit_pread_state {
    2564             :         ssize_t nread;
    2565             :         struct vfs_aio_state vfs_aio_state;
    2566             : };
    2567             : 
    2568             : static void fruit_pread_done(struct tevent_req *subreq);
    2569             : 
    2570          32 : static struct tevent_req *fruit_pread_send(
    2571             :         struct vfs_handle_struct *handle,
    2572             :         TALLOC_CTX *mem_ctx,
    2573             :         struct tevent_context *ev,
    2574             :         struct files_struct *fsp,
    2575             :         void *data,
    2576             :         size_t n, off_t offset)
    2577             : {
    2578          32 :         struct tevent_req *req = NULL;
    2579          32 :         struct tevent_req *subreq = NULL;
    2580          32 :         struct fruit_pread_state *state = NULL;
    2581          32 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    2582             : 
    2583          32 :         req = tevent_req_create(mem_ctx, &state,
    2584             :                                 struct fruit_pread_state);
    2585          32 :         if (req == NULL) {
    2586           0 :                 return NULL;
    2587             :         }
    2588             : 
    2589          32 :         if (fruit_must_handle_aio_stream(fio)) {
    2590          14 :                 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
    2591          14 :                 if (state->nread != n) {
    2592           0 :                         if (state->nread != -1) {
    2593           0 :                                 errno = EIO;
    2594             :                         }
    2595           0 :                         tevent_req_error(req, errno);
    2596           0 :                         return tevent_req_post(req, ev);
    2597             :                 }
    2598          14 :                 tevent_req_done(req);
    2599          14 :                 return tevent_req_post(req, ev);
    2600             :         }
    2601             : 
    2602          18 :         subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
    2603             :                                          data, n, offset);
    2604          18 :         if (tevent_req_nomem(req, subreq)) {
    2605           0 :                 return tevent_req_post(req, ev);
    2606             :         }
    2607          18 :         tevent_req_set_callback(subreq, fruit_pread_done, req);
    2608          18 :         return req;
    2609             : }
    2610             : 
    2611          18 : static void fruit_pread_done(struct tevent_req *subreq)
    2612             : {
    2613          18 :         struct tevent_req *req = tevent_req_callback_data(
    2614             :                 subreq, struct tevent_req);
    2615          18 :         struct fruit_pread_state *state = tevent_req_data(
    2616             :                 req, struct fruit_pread_state);
    2617             : 
    2618          18 :         state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
    2619          18 :         TALLOC_FREE(subreq);
    2620             : 
    2621          18 :         if (tevent_req_error(req, state->vfs_aio_state.error)) {
    2622           0 :                 return;
    2623             :         }
    2624          18 :         tevent_req_done(req);
    2625             : }
    2626             : 
    2627          32 : static ssize_t fruit_pread_recv(struct tevent_req *req,
    2628             :                                         struct vfs_aio_state *vfs_aio_state)
    2629             : {
    2630          32 :         struct fruit_pread_state *state = tevent_req_data(
    2631             :                 req, struct fruit_pread_state);
    2632          32 :         ssize_t retval = -1;
    2633             : 
    2634          32 :         if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
    2635           0 :                 tevent_req_received(req);
    2636           0 :                 return -1;
    2637             :         }
    2638             : 
    2639          32 :         *vfs_aio_state = state->vfs_aio_state;
    2640          32 :         retval = state->nread;
    2641          32 :         tevent_req_received(req);
    2642          32 :         return retval;
    2643             : }
    2644             : 
    2645         406 : static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
    2646             :                                         files_struct *fsp, const void *indata,
    2647             :                                         size_t n, off_t offset)
    2648             : {
    2649         406 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    2650         406 :         const void *data = indata;
    2651             :         char afpinfo_buf[AFP_INFO_SIZE];
    2652         406 :         AfpInfo *ai = NULL;
    2653             :         size_t nwritten;
    2654             :         int ret;
    2655             :         bool ok;
    2656             : 
    2657         406 :         DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
    2658             :                   fsp_str_dbg(fsp), (intmax_t)offset, n);
    2659             : 
    2660         406 :         if (fio == NULL) {
    2661           0 :                 return -1;
    2662             :         }
    2663             : 
    2664         406 :         if (fio->fake_fd) {
    2665         365 :                 struct vfs_open_how how = {
    2666         365 :                         .flags = fio->flags, .mode = fio->mode,
    2667             :                 };
    2668         365 :                 int fd = fsp_get_pathref_fd(fsp);
    2669             : 
    2670         365 :                 ret = vfs_fake_fd_close(fd);
    2671         365 :                 fsp_set_fd(fsp, -1);
    2672         365 :                 if (ret != 0) {
    2673           0 :                         DBG_ERR("Close [%s] failed: %s\n",
    2674             :                                 fsp_str_dbg(fsp), strerror(errno));
    2675           0 :                         return -1;
    2676             :                 }
    2677             : 
    2678         365 :                 fd = SMB_VFS_NEXT_OPENAT(handle,
    2679             :                                          NULL, /* opening a stream */
    2680             :                                          fsp->fsp_name,
    2681             :                                          fsp,
    2682             :                                          &how);
    2683         365 :                 if (fd == -1) {
    2684           0 :                         DBG_ERR("On-demand create [%s] in write failed: %s\n",
    2685             :                                 fsp_str_dbg(fsp), strerror(errno));
    2686           0 :                         return -1;
    2687             :                 }
    2688         365 :                 fsp_set_fd(fsp, fd);
    2689         365 :                 fio->fake_fd = false;
    2690             :         }
    2691             : 
    2692         406 :         ai = afpinfo_unpack(talloc_tos(), data, fio->config->validate_afpinfo);
    2693         406 :         if (ai == NULL) {
    2694           0 :                 return -1;
    2695             :         }
    2696             : 
    2697         406 :         if (ai_empty_finderinfo(ai)) {
    2698             :                 /*
    2699             :                  * Writing an all 0 blob to the metadata stream results in the
    2700             :                  * stream being removed on a macOS server. This ensures we
    2701             :                  * behave the same and it verified by the "delete AFP_AfpInfo by
    2702             :                  * writing all 0" test.
    2703             :                  */
    2704           6 :                 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
    2705           6 :                 if (ret != 0) {
    2706           0 :                         DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
    2707             :                                 fsp_str_dbg(fsp));
    2708           0 :                         return -1;
    2709             :                 }
    2710             : 
    2711           6 :                 ok = set_delete_on_close(
    2712             :                         fsp,
    2713             :                         true,
    2714           6 :                         handle->conn->session_info->security_token,
    2715           6 :                         handle->conn->session_info->unix_token);
    2716           6 :                 if (!ok) {
    2717           0 :                         DBG_ERR("set_delete_on_close on [%s] failed\n",
    2718             :                                 fsp_str_dbg(fsp));
    2719           0 :                         return -1;
    2720             :                 }
    2721           6 :                 return n;
    2722             :         }
    2723             : 
    2724         400 :         if (!fio->config->validate_afpinfo) {
    2725             :                 /*
    2726             :                  * Ensure the buffer contains a valid header, so marshall
    2727             :                  * the data from the afpinfo struck back into a buffer
    2728             :                  * and write that instead of the possibly malformed data
    2729             :                  * we got from the client.
    2730             :                  */
    2731           6 :                 nwritten = afpinfo_pack(ai, afpinfo_buf);
    2732           6 :                 if (nwritten != AFP_INFO_SIZE) {
    2733           0 :                         errno = EINVAL;
    2734           0 :                         return -1;
    2735             :                 }
    2736           6 :                 data = afpinfo_buf;
    2737             :         }
    2738             : 
    2739         400 :         nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
    2740         400 :         if (nwritten != n) {
    2741           0 :                 return -1;
    2742             :         }
    2743             : 
    2744         400 :         return n;
    2745             : }
    2746             : 
    2747         137 : static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
    2748             :                                           files_struct *fsp, const void *data,
    2749             :                                           size_t n, off_t offset)
    2750             : {
    2751         137 :         struct fruit_config_data *config = NULL;
    2752         137 :         struct adouble *ad = NULL;
    2753         137 :         AfpInfo *ai = NULL;
    2754         137 :         char *p = NULL;
    2755             :         int ret;
    2756             :         bool ok;
    2757             : 
    2758         137 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    2759             :                                 struct fruit_config_data, return -1);
    2760             : 
    2761         137 :         ai = afpinfo_unpack(talloc_tos(), data, config->validate_afpinfo);
    2762         137 :         if (ai == NULL) {
    2763           0 :                 return -1;
    2764             :         }
    2765             : 
    2766         137 :         ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
    2767         137 :         if (ad == NULL) {
    2768         123 :                 ad = ad_init(talloc_tos(), ADOUBLE_META);
    2769         123 :                 if (ad == NULL) {
    2770           0 :                         return -1;
    2771             :                 }
    2772             :         }
    2773         137 :         p = ad_get_entry(ad, ADEID_FINDERI);
    2774         137 :         if (p == NULL) {
    2775           0 :                 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
    2776           0 :                 TALLOC_FREE(ad);
    2777           0 :                 return -1;
    2778             :         }
    2779             : 
    2780         137 :         memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
    2781             : 
    2782         137 :         ret = ad_fset(handle, ad, fsp);
    2783         137 :         if (ret != 0) {
    2784           0 :                 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
    2785           0 :                 TALLOC_FREE(ad);
    2786           0 :                 return -1;
    2787             :         }
    2788             : 
    2789         137 :         TALLOC_FREE(ad);
    2790             : 
    2791         137 :         if (!ai_empty_finderinfo(ai)) {
    2792         135 :                 return n;
    2793             :         }
    2794             : 
    2795             :         /*
    2796             :          * Writing an all 0 blob to the metadata stream results in the stream
    2797             :          * being removed on a macOS server. This ensures we behave the same and
    2798             :          * it verified by the "delete AFP_AfpInfo by writing all 0" test.
    2799             :          */
    2800             : 
    2801           2 :         ok = set_delete_on_close(
    2802             :                 fsp,
    2803             :                 true,
    2804           2 :                 handle->conn->session_info->security_token,
    2805           2 :                 handle->conn->session_info->unix_token);
    2806           2 :         if (!ok) {
    2807           0 :                 DBG_ERR("set_delete_on_close on [%s] failed\n",
    2808             :                         fsp_str_dbg(fsp));
    2809           0 :                 return -1;
    2810             :         }
    2811             : 
    2812           2 :         return n;
    2813             : }
    2814             : 
    2815        2786 : static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
    2816             :                                  files_struct *fsp, const void *data,
    2817             :                                  size_t n, off_t offset)
    2818             : {
    2819        2786 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    2820             :         ssize_t nwritten;
    2821             :         uint8_t buf[AFP_INFO_SIZE];
    2822             :         size_t to_write;
    2823             :         size_t to_copy;
    2824             :         int cmp;
    2825             : 
    2826        2786 :         if (fio == NULL) {
    2827           0 :                 DBG_ERR("Failed to fetch fsp extension\n");
    2828           0 :                 return -1;
    2829             :         }
    2830             : 
    2831        2786 :         if (n < 3) {
    2832         256 :                 errno = EINVAL;
    2833         256 :                 return -1;
    2834             :         }
    2835             : 
    2836        2530 :         if (offset != 0 && n < 60) {
    2837        1568 :                 errno = EINVAL;
    2838        1568 :                 return -1;
    2839             :         }
    2840             : 
    2841         962 :         if (fio->config->validate_afpinfo) {
    2842         956 :                 cmp = memcmp(data, "AFP", 3);
    2843         956 :                 if (cmp != 0) {
    2844         379 :                         errno = EINVAL;
    2845         379 :                         return -1;
    2846             :                 }
    2847             :         }
    2848             : 
    2849         583 :         if (n <= AFP_OFF_FinderInfo) {
    2850             :                 /*
    2851             :                  * Nothing to do here really, just return
    2852             :                  */
    2853          40 :                 return n;
    2854             :         }
    2855             : 
    2856         543 :         offset = 0;
    2857             : 
    2858         543 :         to_copy = n;
    2859         543 :         if (to_copy > AFP_INFO_SIZE) {
    2860         200 :                 to_copy = AFP_INFO_SIZE;
    2861             :         }
    2862         543 :         memcpy(buf, data, to_copy);
    2863             : 
    2864         543 :         to_write = n;
    2865         543 :         if (to_write != AFP_INFO_SIZE) {
    2866         272 :                 to_write = AFP_INFO_SIZE;
    2867             :         }
    2868             : 
    2869         543 :         switch (fio->config->meta) {
    2870         406 :         case FRUIT_META_STREAM:
    2871         406 :                 nwritten = fruit_pwrite_meta_stream(handle,
    2872             :                                                     fsp,
    2873             :                                                     buf,
    2874             :                                                     to_write,
    2875             :                                                     offset);
    2876         406 :                 break;
    2877             : 
    2878         137 :         case FRUIT_META_NETATALK:
    2879         137 :                 nwritten = fruit_pwrite_meta_netatalk(handle,
    2880             :                                                       fsp,
    2881             :                                                       buf,
    2882             :                                                       to_write,
    2883             :                                                       offset);
    2884         137 :                 break;
    2885             : 
    2886           0 :         default:
    2887           0 :                 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
    2888           0 :                 return -1;
    2889             :         }
    2890             : 
    2891         543 :         if (nwritten != to_write) {
    2892           0 :                 return -1;
    2893             :         }
    2894             : 
    2895             :         /*
    2896             :          * Return the requested amount, verified against macOS SMB server
    2897             :          */
    2898         543 :         return n;
    2899             : }
    2900             : 
    2901          40 : static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
    2902             :                                         files_struct *fsp, const void *data,
    2903             :                                         size_t n, off_t offset)
    2904             : {
    2905          40 :         return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
    2906             : }
    2907             : 
    2908           0 : static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
    2909             :                                        files_struct *fsp, const void *data,
    2910             :                                        size_t n, off_t offset)
    2911             : {
    2912           0 :         return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
    2913             : }
    2914             : 
    2915         124 : static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
    2916             :                                          files_struct *fsp, const void *data,
    2917             :                                          size_t n, off_t offset)
    2918             : {
    2919         124 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    2920         124 :         struct adouble *ad = NULL;
    2921             :         ssize_t nwritten;
    2922             :         int ret;
    2923             : 
    2924         124 :         if (fio == NULL || fio->ad_fsp == NULL) {
    2925           0 :                 DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
    2926           0 :                 errno = EBADF;
    2927           0 :                 return -1;
    2928             :         }
    2929             : 
    2930         124 :         ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
    2931         124 :         if (ad == NULL) {
    2932           0 :                 DBG_ERR("ad_fget [%s] failed [%s]\n",
    2933             :                         fsp_str_dbg(fio->ad_fsp), strerror(errno));
    2934           0 :                 return -1;
    2935             :         }
    2936             : 
    2937         124 :         nwritten = SMB_VFS_NEXT_PWRITE(handle, fio->ad_fsp, data, n,
    2938             :                                        offset + ad_getentryoff(ad, ADEID_RFORK));
    2939         124 :         if (nwritten != n) {
    2940           0 :                 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
    2941             :                         fsp_str_dbg(fio->ad_fsp), nwritten, n);
    2942           0 :                 TALLOC_FREE(ad);
    2943           0 :                 return -1;
    2944             :         }
    2945             : 
    2946         124 :         if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
    2947         118 :                 ad_setentrylen(ad, ADEID_RFORK, n + offset);
    2948         118 :                 ret = ad_fset(handle, ad, fio->ad_fsp);
    2949         118 :                 if (ret != 0) {
    2950           0 :                         DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fio->ad_fsp));
    2951           0 :                         TALLOC_FREE(ad);
    2952           0 :                         return -1;
    2953             :                 }
    2954             :         }
    2955             : 
    2956         124 :         TALLOC_FREE(ad);
    2957         124 :         return n;
    2958             : }
    2959             : 
    2960         164 : static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
    2961             :                                  files_struct *fsp, const void *data,
    2962             :                                  size_t n, off_t offset)
    2963             : {
    2964         164 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    2965             :         ssize_t nwritten;
    2966             : 
    2967         164 :         if (fio == NULL) {
    2968           0 :                 DBG_ERR("Failed to fetch fsp extension\n");
    2969           0 :                 return -1;
    2970             :         }
    2971             : 
    2972         164 :         switch (fio->config->rsrc) {
    2973          40 :         case FRUIT_RSRC_STREAM:
    2974          40 :                 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
    2975          40 :                 break;
    2976             : 
    2977         124 :         case FRUIT_RSRC_ADFILE:
    2978         124 :                 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
    2979         124 :                 break;
    2980             : 
    2981           0 :         case FRUIT_RSRC_XATTR:
    2982           0 :                 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
    2983           0 :                 break;
    2984             : 
    2985           0 :         default:
    2986           0 :                 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
    2987           0 :                 return -1;
    2988             :         }
    2989             : 
    2990         164 :         return nwritten;
    2991             : }
    2992             : 
    2993        3174 : static ssize_t fruit_pwrite(vfs_handle_struct *handle,
    2994             :                             files_struct *fsp, const void *data,
    2995             :                             size_t n, off_t offset)
    2996             : {
    2997        3174 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    2998             :         ssize_t nwritten;
    2999             : 
    3000        3174 :         DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
    3001             :                   fsp_str_dbg(fsp), (intmax_t)offset, n);
    3002             : 
    3003        3174 :         if (fio == NULL) {
    3004         224 :                 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
    3005             :         }
    3006             : 
    3007        2950 :         if (fio->type == ADOUBLE_META) {
    3008        2786 :                 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
    3009             :         } else {
    3010         164 :                 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
    3011             :         }
    3012             : 
    3013        2950 :         DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
    3014        2950 :         return nwritten;
    3015             : }
    3016             : 
    3017             : struct fruit_pwrite_state {
    3018             :         ssize_t nwritten;
    3019             :         struct vfs_aio_state vfs_aio_state;
    3020             : };
    3021             : 
    3022             : static void fruit_pwrite_done(struct tevent_req *subreq);
    3023             : 
    3024          76 : static struct tevent_req *fruit_pwrite_send(
    3025             :         struct vfs_handle_struct *handle,
    3026             :         TALLOC_CTX *mem_ctx,
    3027             :         struct tevent_context *ev,
    3028             :         struct files_struct *fsp,
    3029             :         const void *data,
    3030             :         size_t n, off_t offset)
    3031             : {
    3032          76 :         struct tevent_req *req = NULL;
    3033          76 :         struct tevent_req *subreq = NULL;
    3034          76 :         struct fruit_pwrite_state *state = NULL;
    3035          76 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    3036             : 
    3037          76 :         req = tevent_req_create(mem_ctx, &state,
    3038             :                                 struct fruit_pwrite_state);
    3039          76 :         if (req == NULL) {
    3040           0 :                 return NULL;
    3041             :         }
    3042             : 
    3043          76 :         if (fruit_must_handle_aio_stream(fio)) {
    3044          14 :                 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
    3045          14 :                 if (state->nwritten != n) {
    3046           0 :                         if (state->nwritten != -1) {
    3047           0 :                                 errno = EIO;
    3048             :                         }
    3049           0 :                         tevent_req_error(req, errno);
    3050           0 :                         return tevent_req_post(req, ev);
    3051             :                 }
    3052          14 :                 tevent_req_done(req);
    3053          14 :                 return tevent_req_post(req, ev);
    3054             :         }
    3055             : 
    3056          62 :         subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
    3057             :                                           data, n, offset);
    3058          62 :         if (tevent_req_nomem(req, subreq)) {
    3059           0 :                 return tevent_req_post(req, ev);
    3060             :         }
    3061          62 :         tevent_req_set_callback(subreq, fruit_pwrite_done, req);
    3062          62 :         return req;
    3063             : }
    3064             : 
    3065          62 : static void fruit_pwrite_done(struct tevent_req *subreq)
    3066             : {
    3067          62 :         struct tevent_req *req = tevent_req_callback_data(
    3068             :                 subreq, struct tevent_req);
    3069          62 :         struct fruit_pwrite_state *state = tevent_req_data(
    3070             :                 req, struct fruit_pwrite_state);
    3071             : 
    3072          62 :         state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
    3073          62 :         TALLOC_FREE(subreq);
    3074             : 
    3075          62 :         if (tevent_req_error(req, state->vfs_aio_state.error)) {
    3076           0 :                 return;
    3077             :         }
    3078          62 :         tevent_req_done(req);
    3079             : }
    3080             : 
    3081          76 : static ssize_t fruit_pwrite_recv(struct tevent_req *req,
    3082             :                                          struct vfs_aio_state *vfs_aio_state)
    3083             : {
    3084          76 :         struct fruit_pwrite_state *state = tevent_req_data(
    3085             :                 req, struct fruit_pwrite_state);
    3086          76 :         ssize_t retval = -1;
    3087             : 
    3088          76 :         if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
    3089           0 :                 tevent_req_received(req);
    3090           0 :                 return -1;
    3091             :         }
    3092             : 
    3093          76 :         *vfs_aio_state = state->vfs_aio_state;
    3094          76 :         retval = state->nwritten;
    3095          76 :         tevent_req_received(req);
    3096          76 :         return retval;
    3097             : }
    3098             : 
    3099             : struct fruit_fsync_state {
    3100             :         int ret;
    3101             :         struct vfs_aio_state vfs_aio_state;
    3102             : };
    3103             : 
    3104             : static void fruit_fsync_done(struct tevent_req *subreq);
    3105             : 
    3106           8 : static struct tevent_req *fruit_fsync_send(
    3107             :         struct vfs_handle_struct *handle,
    3108             :         TALLOC_CTX *mem_ctx,
    3109             :         struct tevent_context *ev,
    3110             :         struct files_struct *fsp)
    3111             : {
    3112           8 :         struct tevent_req *req = NULL;
    3113           8 :         struct tevent_req *subreq = NULL;
    3114           8 :         struct fruit_fsync_state *state = NULL;
    3115           8 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    3116             : 
    3117           8 :         req = tevent_req_create(mem_ctx, &state,
    3118             :                                 struct fruit_fsync_state);
    3119           8 :         if (req == NULL) {
    3120           0 :                 return NULL;
    3121             :         }
    3122             : 
    3123           8 :         if (fruit_must_handle_aio_stream(fio)) {
    3124           6 :                 struct adouble *ad = NULL;
    3125             : 
    3126           6 :                 if (fio->type == ADOUBLE_META) {
    3127             :                         /*
    3128             :                          * We must never pass a fake_fd
    3129             :                          * to lower level fsync calls.
    3130             :                          * Everything is already done
    3131             :                          * synchronously, so just return
    3132             :                          * true.
    3133             :                          */
    3134           0 :                         SMB_ASSERT(fio->fake_fd);
    3135           0 :                         tevent_req_done(req);
    3136           0 :                         return tevent_req_post(req, ev);
    3137             :                 }
    3138             : 
    3139             :                 /*
    3140             :                  * We know the following must be true,
    3141             :                  * as it's the condition for fruit_must_handle_aio_stream()
    3142             :                  * to return true if fio->type == ADOUBLE_RSRC.
    3143             :                  */
    3144           6 :                 SMB_ASSERT(fio->config->rsrc == FRUIT_RSRC_ADFILE);
    3145           6 :                 if (fio->ad_fsp == NULL) {
    3146           0 :                         tevent_req_error(req, EBADF);
    3147           0 :                         return tevent_req_post(req, ev);
    3148             :                 }
    3149           6 :                 ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
    3150           6 :                 if (ad == NULL) {
    3151           0 :                         tevent_req_error(req, ENOMEM);
    3152           0 :                         return tevent_req_post(req, ev);
    3153             :                 }
    3154           6 :                 fsp = fio->ad_fsp;
    3155             :         }
    3156             : 
    3157           8 :         subreq = SMB_VFS_NEXT_FSYNC_SEND(state, ev, handle, fsp);
    3158           8 :         if (tevent_req_nomem(req, subreq)) {
    3159           0 :                 return tevent_req_post(req, ev);
    3160             :         }
    3161           8 :         tevent_req_set_callback(subreq, fruit_fsync_done, req);
    3162           8 :         return req;
    3163             : }
    3164             : 
    3165           8 : static void fruit_fsync_done(struct tevent_req *subreq)
    3166             : {
    3167           8 :         struct tevent_req *req = tevent_req_callback_data(
    3168             :                 subreq, struct tevent_req);
    3169           8 :         struct fruit_fsync_state *state = tevent_req_data(
    3170             :                 req, struct fruit_fsync_state);
    3171             : 
    3172           8 :         state->ret = SMB_VFS_FSYNC_RECV(subreq, &state->vfs_aio_state);
    3173           8 :         TALLOC_FREE(subreq);
    3174           8 :         if (state->ret != 0) {
    3175           0 :                 tevent_req_error(req, errno);
    3176           0 :                 return;
    3177             :         }
    3178           8 :         tevent_req_done(req);
    3179             : }
    3180             : 
    3181           8 : static int fruit_fsync_recv(struct tevent_req *req,
    3182             :                                         struct vfs_aio_state *vfs_aio_state)
    3183             : {
    3184           8 :         struct fruit_fsync_state *state = tevent_req_data(
    3185             :                 req, struct fruit_fsync_state);
    3186           8 :         int retval = -1;
    3187             : 
    3188           8 :         if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
    3189           0 :                 tevent_req_received(req);
    3190           0 :                 return -1;
    3191             :         }
    3192             : 
    3193           8 :         *vfs_aio_state = state->vfs_aio_state;
    3194           8 :         retval = state->ret;
    3195           8 :         tevent_req_received(req);
    3196           8 :         return retval;
    3197             : }
    3198             : 
    3199             : /**
    3200             :  * Helper to stat/lstat the base file of an smb_fname.
    3201             :  */
    3202        5030 : static int fruit_stat_base(vfs_handle_struct *handle,
    3203             :                            struct smb_filename *smb_fname,
    3204             :                            bool follow_links)
    3205             : {
    3206             :         char *tmp_stream_name;
    3207             :         int rc;
    3208             : 
    3209        5030 :         tmp_stream_name = smb_fname->stream_name;
    3210        5030 :         smb_fname->stream_name = NULL;
    3211        5030 :         if (follow_links) {
    3212           6 :                 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
    3213             :         } else {
    3214        5024 :                 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
    3215             :         }
    3216        5030 :         smb_fname->stream_name = tmp_stream_name;
    3217             : 
    3218        5030 :         DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
    3219             :                   smb_fname->base_name,
    3220             :                   (uintmax_t)smb_fname->st.st_ex_dev,
    3221             :                   (uintmax_t)smb_fname->st.st_ex_ino);
    3222        5030 :         return rc;
    3223             : }
    3224             : 
    3225           0 : static int fruit_stat_meta_stream(vfs_handle_struct *handle,
    3226             :                                   struct smb_filename *smb_fname,
    3227             :                                   bool follow_links)
    3228             : {
    3229             :         int ret;
    3230             :         ino_t ino;
    3231             : 
    3232           0 :         ret = fruit_stat_base(handle, smb_fname, false);
    3233           0 :         if (ret != 0) {
    3234           0 :                 return -1;
    3235             :         }
    3236             : 
    3237           0 :         ino = hash_inode(&smb_fname->st, smb_fname->stream_name);
    3238             : 
    3239           0 :         if (follow_links) {
    3240           0 :                 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
    3241             :         } else {
    3242           0 :                 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
    3243             :         }
    3244             : 
    3245           0 :         smb_fname->st.st_ex_ino = ino;
    3246             : 
    3247           0 :         return ret;
    3248             : }
    3249             : 
    3250           0 : static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
    3251             :                                     struct smb_filename *smb_fname,
    3252             :                                     bool follow_links)
    3253             : {
    3254           0 :         struct adouble *ad = NULL;
    3255             : 
    3256             :         /* Populate the stat struct with info from the base file. */
    3257           0 :         if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
    3258           0 :                 return -1;
    3259             :         }
    3260             : 
    3261           0 :         ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
    3262           0 :         if (ad == NULL) {
    3263           0 :                 DBG_INFO("fruit_stat_meta %s: %s\n",
    3264             :                          smb_fname_str_dbg(smb_fname), strerror(errno));
    3265           0 :                 errno = ENOENT;
    3266           0 :                 return -1;
    3267             :         }
    3268           0 :         TALLOC_FREE(ad);
    3269             : 
    3270           0 :         smb_fname->st.st_ex_size = AFP_INFO_SIZE;
    3271           0 :         smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
    3272           0 :                                               smb_fname->stream_name);
    3273           0 :         return 0;
    3274             : }
    3275             : 
    3276           0 : static int fruit_stat_meta(vfs_handle_struct *handle,
    3277             :                            struct smb_filename *smb_fname,
    3278             :                            bool follow_links)
    3279             : {
    3280           0 :         struct fruit_config_data *config = NULL;
    3281             :         int ret;
    3282             : 
    3283           0 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    3284             :                                 struct fruit_config_data, return -1);
    3285             : 
    3286           0 :         switch (config->meta) {
    3287           0 :         case FRUIT_META_STREAM:
    3288           0 :                 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
    3289           0 :                 break;
    3290             : 
    3291           0 :         case FRUIT_META_NETATALK:
    3292           0 :                 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
    3293           0 :                 break;
    3294             : 
    3295           0 :         default:
    3296           0 :                 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
    3297           0 :                 return -1;
    3298             :         }
    3299             : 
    3300           0 :         return ret;
    3301             : }
    3302             : 
    3303          12 : static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
    3304             :                                     struct smb_filename *smb_fname,
    3305             :                                     bool follow_links)
    3306             : {
    3307          12 :         struct adouble *ad = NULL;
    3308             :         int ret;
    3309             : 
    3310          12 :         ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
    3311          12 :         if (ad == NULL) {
    3312           6 :                 errno = ENOENT;
    3313           6 :                 return -1;
    3314             :         }
    3315             : 
    3316             :         /* Populate the stat struct with info from the base file. */
    3317           6 :         ret = fruit_stat_base(handle, smb_fname, follow_links);
    3318           6 :         if (ret != 0) {
    3319           0 :                 TALLOC_FREE(ad);
    3320           0 :                 return -1;
    3321             :         }
    3322             : 
    3323           6 :         smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
    3324          12 :         smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
    3325           6 :                                               smb_fname->stream_name);
    3326           6 :         TALLOC_FREE(ad);
    3327           6 :         return 0;
    3328             : }
    3329             : 
    3330          68 : static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
    3331             :                                   struct smb_filename *smb_fname,
    3332             :                                   bool follow_links)
    3333             : {
    3334             :         int ret;
    3335             : 
    3336          68 :         if (follow_links) {
    3337          68 :                 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
    3338             :         } else {
    3339           0 :                 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
    3340             :         }
    3341             : 
    3342          68 :         return ret;
    3343             : }
    3344             : 
    3345           0 : static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
    3346             :                                  struct smb_filename *smb_fname,
    3347             :                                  bool follow_links)
    3348             : {
    3349             : #ifdef HAVE_ATTROPEN
    3350             :         int ret;
    3351             :         int fd = -1;
    3352             : 
    3353             :         /* Populate the stat struct with info from the base file. */
    3354             :         ret = fruit_stat_base(handle, smb_fname, follow_links);
    3355             :         if (ret != 0) {
    3356             :                 return -1;
    3357             :         }
    3358             : 
    3359             :         fd = attropen(smb_fname->base_name,
    3360             :                       AFPRESOURCE_EA_NETATALK,
    3361             :                       O_RDONLY);
    3362             :         if (fd == -1) {
    3363             :                 return 0;
    3364             :         }
    3365             : 
    3366             :         ret = sys_fstat(fd, &smb_fname->st, false);
    3367             :         if (ret != 0) {
    3368             :                 close(fd);
    3369             :                 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
    3370             :                         AFPRESOURCE_EA_NETATALK);
    3371             :                 return -1;
    3372             :         }
    3373             :         close(fd);
    3374             :         fd = -1;
    3375             : 
    3376             :         smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
    3377             :                                              smb_fname->stream_name);
    3378             : 
    3379             :         return ret;
    3380             : 
    3381             : #else
    3382           0 :         errno = ENOSYS;
    3383           0 :         return -1;
    3384             : #endif
    3385             : }
    3386             : 
    3387          80 : static int fruit_stat_rsrc(vfs_handle_struct *handle,
    3388             :                            struct smb_filename *smb_fname,
    3389             :                            bool follow_links)
    3390             : {
    3391          80 :         struct fruit_config_data *config = NULL;
    3392             :         int ret;
    3393             : 
    3394          80 :         DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
    3395             : 
    3396          80 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    3397             :                                 struct fruit_config_data, return -1);
    3398             : 
    3399          80 :         switch (config->rsrc) {
    3400          68 :         case FRUIT_RSRC_STREAM:
    3401          68 :                 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
    3402          68 :                 break;
    3403             : 
    3404           0 :         case FRUIT_RSRC_XATTR:
    3405           0 :                 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
    3406           0 :                 break;
    3407             : 
    3408          12 :         case FRUIT_RSRC_ADFILE:
    3409          12 :                 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
    3410          12 :                 break;
    3411             : 
    3412           0 :         default:
    3413           0 :                 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
    3414           0 :                 return -1;
    3415             :         }
    3416             : 
    3417          80 :         return ret;
    3418             : }
    3419             : 
    3420      121655 : static int fruit_stat(vfs_handle_struct *handle,
    3421             :                       struct smb_filename *smb_fname)
    3422             : {
    3423      121655 :         int rc = -1;
    3424             : 
    3425      121655 :         DEBUG(10, ("fruit_stat called for %s\n",
    3426             :                    smb_fname_str_dbg(smb_fname)));
    3427             : 
    3428      121655 :         if (!is_named_stream(smb_fname)) {
    3429      121559 :                 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
    3430      121559 :                 if (rc == 0) {
    3431      121531 :                         update_btime(handle, smb_fname);
    3432             :                 }
    3433      121559 :                 return rc;
    3434             :         }
    3435             : 
    3436             :         /*
    3437             :          * Note if lp_posix_paths() is true, we can never
    3438             :          * get here as is_ntfs_stream_smb_fname() is
    3439             :          * always false. So we never need worry about
    3440             :          * not following links here.
    3441             :          */
    3442             : 
    3443          96 :         if (is_afpinfo_stream(smb_fname->stream_name)) {
    3444           0 :                 rc = fruit_stat_meta(handle, smb_fname, true);
    3445          96 :         } else if (is_afpresource_stream(smb_fname->stream_name)) {
    3446          80 :                 rc = fruit_stat_rsrc(handle, smb_fname, true);
    3447             :         } else {
    3448          16 :                 return SMB_VFS_NEXT_STAT(handle, smb_fname);
    3449             :         }
    3450             : 
    3451          80 :         if (rc == 0) {
    3452          16 :                 update_btime(handle, smb_fname);
    3453          16 :                 smb_fname->st.st_ex_mode &= ~S_IFMT;
    3454          16 :                 smb_fname->st.st_ex_mode |= S_IFREG;
    3455          16 :                 smb_fname->st.st_ex_blocks =
    3456          16 :                         smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
    3457             :         }
    3458          80 :         return rc;
    3459             : }
    3460             : 
    3461         446 : static int fruit_lstat(vfs_handle_struct *handle,
    3462             :                        struct smb_filename *smb_fname)
    3463             : {
    3464         446 :         int rc = -1;
    3465             : 
    3466         446 :         DEBUG(10, ("fruit_lstat called for %s\n",
    3467             :                    smb_fname_str_dbg(smb_fname)));
    3468             : 
    3469         446 :         if (!is_named_stream(smb_fname)) {
    3470         446 :                 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
    3471         446 :                 if (rc == 0) {
    3472         446 :                         update_btime(handle, smb_fname);
    3473             :                 }
    3474         446 :                 return rc;
    3475             :         }
    3476             : 
    3477           0 :         if (is_afpinfo_stream(smb_fname->stream_name)) {
    3478           0 :                 rc = fruit_stat_meta(handle, smb_fname, false);
    3479           0 :         } else if (is_afpresource_stream(smb_fname->stream_name)) {
    3480           0 :                 rc = fruit_stat_rsrc(handle, smb_fname, false);
    3481             :         } else {
    3482           0 :                 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
    3483             :         }
    3484             : 
    3485           0 :         if (rc == 0) {
    3486           0 :                 update_btime(handle, smb_fname);
    3487           0 :                 smb_fname->st.st_ex_mode &= ~S_IFMT;
    3488           0 :                 smb_fname->st.st_ex_mode |= S_IFREG;
    3489           0 :                 smb_fname->st.st_ex_blocks =
    3490           0 :                         smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
    3491             :         }
    3492           0 :         return rc;
    3493             : }
    3494             : 
    3495        3053 : static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
    3496             :                                    files_struct *fsp,
    3497             :                                    SMB_STRUCT_STAT *sbuf)
    3498             : {
    3499        3053 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    3500             :         struct smb_filename smb_fname;
    3501             :         ino_t ino;
    3502             :         int ret;
    3503             : 
    3504        3053 :         if (fio == NULL) {
    3505           0 :                 return -1;
    3506             :         }
    3507             : 
    3508        3053 :         if (fio->fake_fd) {
    3509        1241 :                 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
    3510        1241 :                 if (ret != 0) {
    3511           0 :                         return -1;
    3512             :                 }
    3513             : 
    3514        1241 :                 *sbuf = fsp->base_fsp->fsp_name->st;
    3515        1241 :                 sbuf->st_ex_size = AFP_INFO_SIZE;
    3516        1241 :                 sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
    3517        1241 :                 return 0;
    3518             :         }
    3519             : 
    3520        1812 :         smb_fname = (struct smb_filename) {
    3521        1812 :                 .base_name = fsp->fsp_name->base_name,
    3522        1812 :                 .twrp = fsp->fsp_name->twrp,
    3523             :         };
    3524             : 
    3525        1812 :         ret = fruit_stat_base(handle, &smb_fname, false);
    3526        1812 :         if (ret != 0) {
    3527           0 :                 return -1;
    3528             :         }
    3529        1812 :         *sbuf = smb_fname.st;
    3530             : 
    3531        1812 :         ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
    3532             : 
    3533        1812 :         ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
    3534        1812 :         if (ret != 0) {
    3535           0 :                 return -1;
    3536             :         }
    3537             : 
    3538        1812 :         sbuf->st_ex_ino = ino;
    3539        1812 :         return 0;
    3540             : }
    3541             : 
    3542        1005 : static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
    3543             :                                      files_struct *fsp,
    3544             :                                      SMB_STRUCT_STAT *sbuf)
    3545             : {
    3546             :         int ret;
    3547             : 
    3548        1005 :         ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
    3549        1005 :         if (ret != 0) {
    3550           0 :                 return -1;
    3551             :         }
    3552             : 
    3553        1005 :         *sbuf = fsp->base_fsp->fsp_name->st;
    3554        1005 :         sbuf->st_ex_size = AFP_INFO_SIZE;
    3555        1005 :         sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
    3556             : 
    3557        1005 :         return 0;
    3558             : }
    3559             : 
    3560        4058 : static int fruit_fstat_meta(vfs_handle_struct *handle,
    3561             :                             files_struct *fsp,
    3562             :                             SMB_STRUCT_STAT *sbuf,
    3563             :                             struct fio *fio)
    3564             : {
    3565             :         int ret;
    3566             : 
    3567        4058 :         DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
    3568             : 
    3569        4058 :         switch (fio->config->meta) {
    3570        3053 :         case FRUIT_META_STREAM:
    3571        3053 :                 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
    3572        3053 :                 break;
    3573             : 
    3574        1005 :         case FRUIT_META_NETATALK:
    3575        1005 :                 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
    3576        1005 :                 break;
    3577             : 
    3578           0 :         default:
    3579           0 :                 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
    3580           0 :                 return -1;
    3581             :         }
    3582             : 
    3583        4058 :         DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
    3584        4058 :         return ret;
    3585             : }
    3586             : 
    3587           0 : static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
    3588             :                                   files_struct *fsp,
    3589             :                                   SMB_STRUCT_STAT *sbuf)
    3590             : {
    3591           0 :         return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
    3592             : }
    3593             : 
    3594         384 : static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
    3595             :                                    files_struct *fsp,
    3596             :                                    SMB_STRUCT_STAT *sbuf)
    3597             : {
    3598         384 :         return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
    3599             : }
    3600             : 
    3601         966 : static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
    3602             :                                     files_struct *fsp,
    3603             :                                     SMB_STRUCT_STAT *sbuf)
    3604             : {
    3605         966 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    3606         966 :         struct adouble *ad = NULL;
    3607             :         int ret;
    3608             : 
    3609         966 :         if (fio == NULL || fio->ad_fsp == NULL) {
    3610           0 :                 DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
    3611           0 :                 errno = EBADF;
    3612           0 :                 return -1;
    3613             :         }
    3614             : 
    3615             :         /* Populate the stat struct with info from the base file. */
    3616         966 :         ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
    3617         966 :         if (ret == -1) {
    3618           0 :                 return -1;
    3619             :         }
    3620             : 
    3621         966 :         ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
    3622         966 :         if (ad == NULL) {
    3623           0 :                 DBG_ERR("ad_fget [%s] failed [%s]\n",
    3624             :                         fsp_str_dbg(fio->ad_fsp), strerror(errno));
    3625           0 :                 return -1;
    3626             :         }
    3627             : 
    3628         966 :         *sbuf = fsp->base_fsp->fsp_name->st;
    3629         966 :         sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
    3630         966 :         sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
    3631             : 
    3632         966 :         TALLOC_FREE(ad);
    3633         966 :         return 0;
    3634             : }
    3635             : 
    3636        1350 : static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
    3637             :                             SMB_STRUCT_STAT *sbuf, struct fio *fio)
    3638             : {
    3639             :         int ret;
    3640             : 
    3641        1350 :         switch (fio->config->rsrc) {
    3642         384 :         case FRUIT_RSRC_STREAM:
    3643         384 :                 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
    3644         384 :                 break;
    3645             : 
    3646         966 :         case FRUIT_RSRC_ADFILE:
    3647         966 :                 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
    3648         966 :                 break;
    3649             : 
    3650           0 :         case FRUIT_RSRC_XATTR:
    3651           0 :                 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
    3652           0 :                 break;
    3653             : 
    3654           0 :         default:
    3655           0 :                 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
    3656           0 :                 return -1;
    3657             :         }
    3658             : 
    3659        1350 :         return ret;
    3660             : }
    3661             : 
    3662       98023 : static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
    3663             :                        SMB_STRUCT_STAT *sbuf)
    3664             : {
    3665       98023 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    3666             :         int rc;
    3667             : 
    3668       98023 :         if (fio == NULL) {
    3669       92615 :                 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
    3670             :         }
    3671             : 
    3672        5408 :         DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
    3673             : 
    3674        5408 :         if (fio->type == ADOUBLE_META) {
    3675        4058 :                 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
    3676             :         } else {
    3677        1350 :                 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
    3678             :         }
    3679             : 
    3680        5408 :         if (rc == 0) {
    3681        5408 :                 sbuf->st_ex_mode &= ~S_IFMT;
    3682        5408 :                 sbuf->st_ex_mode |= S_IFREG;
    3683        5408 :                 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
    3684             :         }
    3685             : 
    3686        5408 :         DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
    3687             :                   fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
    3688        5408 :         return rc;
    3689             : }
    3690             : 
    3691          18 : static NTSTATUS delete_invalid_meta_stream(
    3692             :         vfs_handle_struct *handle,
    3693             :         const struct smb_filename *smb_fname,
    3694             :         TALLOC_CTX *mem_ctx,
    3695             :         unsigned int *pnum_streams,
    3696             :         struct stream_struct **pstreams,
    3697             :         off_t size)
    3698             : {
    3699          18 :         struct smb_filename *sname = NULL;
    3700             :         NTSTATUS status;
    3701             :         int ret;
    3702             :         bool ok;
    3703             : 
    3704          18 :         ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
    3705          18 :         if (!ok) {
    3706           0 :                 return NT_STATUS_INTERNAL_ERROR;
    3707             :         }
    3708             : 
    3709          18 :         if (size == 0) {
    3710          12 :                 return NT_STATUS_OK;
    3711             :         }
    3712             : 
    3713           6 :         status = synthetic_pathref(talloc_tos(),
    3714           6 :                                    handle->conn->cwd_fsp,
    3715           6 :                                    smb_fname->base_name,
    3716             :                                    AFPINFO_STREAM_NAME,
    3717             :                                    NULL,
    3718           6 :                                    smb_fname->twrp,
    3719             :                                    0,
    3720             :                                    &sname);
    3721           6 :         if (!NT_STATUS_IS_OK(status)) {
    3722           0 :                 return NT_STATUS_NO_MEMORY;
    3723             :         }
    3724             : 
    3725           6 :         ret = SMB_VFS_NEXT_UNLINKAT(handle,
    3726             :                         handle->conn->cwd_fsp,
    3727             :                         sname,
    3728             :                         0);
    3729           6 :         if (ret != 0) {
    3730           0 :                 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
    3731           0 :                 TALLOC_FREE(sname);
    3732           0 :                 return map_nt_error_from_unix(errno);
    3733             :         }
    3734             : 
    3735           6 :         TALLOC_FREE(sname);
    3736           6 :         return NT_STATUS_OK;
    3737             : }
    3738             : 
    3739        4129 : static NTSTATUS fruit_streaminfo_meta_stream(
    3740             :         vfs_handle_struct *handle,
    3741             :         struct files_struct *fsp,
    3742             :         const struct smb_filename *smb_fname,
    3743             :         TALLOC_CTX *mem_ctx,
    3744             :         unsigned int *pnum_streams,
    3745             :         struct stream_struct **pstreams)
    3746             : {
    3747        4129 :         struct stream_struct *stream = *pstreams;
    3748        4129 :         unsigned int num_streams = *pnum_streams;
    3749             :         int i;
    3750             : 
    3751        7520 :         for (i = 0; i < num_streams; i++) {
    3752        3959 :                 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
    3753         568 :                         break;
    3754             :                 }
    3755             :         }
    3756             : 
    3757        4129 :         if (i == num_streams) {
    3758        3561 :                 return NT_STATUS_OK;
    3759             :         }
    3760             : 
    3761         568 :         if (stream[i].size != AFP_INFO_SIZE) {
    3762          18 :                 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
    3763             :                         (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
    3764             : 
    3765          18 :                 return delete_invalid_meta_stream(handle,
    3766             :                                                   smb_fname,
    3767             :                                                   mem_ctx,
    3768             :                                                   pnum_streams,
    3769             :                                                   pstreams,
    3770          18 :                                                   stream[i].size);
    3771             :         }
    3772             : 
    3773             : 
    3774         550 :         return NT_STATUS_OK;
    3775             : }
    3776             : 
    3777        2619 : static NTSTATUS fruit_streaminfo_meta_netatalk(
    3778             :         vfs_handle_struct *handle,
    3779             :         struct files_struct *fsp,
    3780             :         const struct smb_filename *smb_fname,
    3781             :         TALLOC_CTX *mem_ctx,
    3782             :         unsigned int *pnum_streams,
    3783             :         struct stream_struct **pstreams)
    3784             : {
    3785        2619 :         struct stream_struct *stream = *pstreams;
    3786        2619 :         unsigned int num_streams = *pnum_streams;
    3787        2619 :         struct adouble *ad = NULL;
    3788             :         bool is_fi_empty;
    3789             :         int i;
    3790             :         bool ok;
    3791             : 
    3792             :         /* Remove the Netatalk xattr from the list */
    3793        2619 :         ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
    3794             :                               ":" NETATALK_META_XATTR ":$DATA");
    3795        2619 :         if (!ok) {
    3796           0 :                 return NT_STATUS_NO_MEMORY;
    3797             :         }
    3798             : 
    3799             :         /*
    3800             :          * Check if there's a AFPINFO_STREAM from the VFS streams
    3801             :          * backend and if yes, remove it from the list
    3802             :          */
    3803        4336 :         for (i = 0; i < num_streams; i++) {
    3804        1725 :                 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
    3805           8 :                         break;
    3806             :                 }
    3807             :         }
    3808             : 
    3809        2619 :         if (i < num_streams) {
    3810           8 :                 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
    3811             :                             smb_fname_str_dbg(smb_fname));
    3812             : 
    3813           8 :                 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
    3814             :                                       AFPINFO_STREAM);
    3815           8 :                 if (!ok) {
    3816           0 :                         return NT_STATUS_INTERNAL_ERROR;
    3817             :                 }
    3818             :         }
    3819             : 
    3820        2619 :         ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
    3821        2619 :         if (ad == NULL) {
    3822        2423 :                 return NT_STATUS_OK;
    3823             :         }
    3824             : 
    3825         196 :         is_fi_empty = ad_empty_finderinfo(ad);
    3826         196 :         TALLOC_FREE(ad);
    3827             : 
    3828         196 :         if (is_fi_empty) {
    3829           4 :                 return NT_STATUS_OK;
    3830             :         }
    3831             : 
    3832         192 :         ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
    3833             :                               AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
    3834         192 :                               smb_roundup(handle->conn, AFP_INFO_SIZE));
    3835         192 :         if (!ok) {
    3836           0 :                 return NT_STATUS_NO_MEMORY;
    3837             :         }
    3838             : 
    3839         192 :         return NT_STATUS_OK;
    3840             : }
    3841             : 
    3842        6748 : static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
    3843             :                                       struct files_struct *fsp,
    3844             :                                       const struct smb_filename *smb_fname,
    3845             :                                       TALLOC_CTX *mem_ctx,
    3846             :                                       unsigned int *pnum_streams,
    3847             :                                       struct stream_struct **pstreams)
    3848             : {
    3849        6748 :         struct fruit_config_data *config = NULL;
    3850             :         NTSTATUS status;
    3851             : 
    3852        6748 :         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
    3853             :                                 return NT_STATUS_INTERNAL_ERROR);
    3854             : 
    3855        6748 :         switch (config->meta) {
    3856        2619 :         case FRUIT_META_NETATALK:
    3857        2619 :                 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
    3858             :                                                         mem_ctx, pnum_streams,
    3859             :                                                         pstreams);
    3860        2619 :                 break;
    3861             : 
    3862        4129 :         case FRUIT_META_STREAM:
    3863        4129 :                 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
    3864             :                                                       mem_ctx, pnum_streams,
    3865             :                                                       pstreams);
    3866        4129 :                 break;
    3867             : 
    3868           0 :         default:
    3869           0 :                 return NT_STATUS_INTERNAL_ERROR;
    3870             :         }
    3871             : 
    3872        6748 :         return status;
    3873             : }
    3874             : 
    3875        1206 : static NTSTATUS fruit_streaminfo_rsrc_stream(
    3876             :         vfs_handle_struct *handle,
    3877             :         struct files_struct *fsp,
    3878             :         const struct smb_filename *smb_fname,
    3879             :         TALLOC_CTX *mem_ctx,
    3880             :         unsigned int *pnum_streams,
    3881             :         struct stream_struct **pstreams)
    3882             : {
    3883             :         bool ok;
    3884             : 
    3885        1206 :         ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
    3886        1206 :         if (!ok) {
    3887           0 :                 DBG_ERR("Filtering resource stream failed\n");
    3888           0 :                 return NT_STATUS_INTERNAL_ERROR;
    3889             :         }
    3890        1206 :         return NT_STATUS_OK;
    3891             : }
    3892             : 
    3893           0 : static NTSTATUS fruit_streaminfo_rsrc_xattr(
    3894             :         vfs_handle_struct *handle,
    3895             :         struct files_struct *fsp,
    3896             :         const struct smb_filename *smb_fname,
    3897             :         TALLOC_CTX *mem_ctx,
    3898             :         unsigned int *pnum_streams,
    3899             :         struct stream_struct **pstreams)
    3900             : {
    3901             :         bool ok;
    3902             : 
    3903           0 :         ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
    3904           0 :         if (!ok) {
    3905           0 :                 DBG_ERR("Filtering resource stream failed\n");
    3906           0 :                 return NT_STATUS_INTERNAL_ERROR;
    3907             :         }
    3908           0 :         return NT_STATUS_OK;
    3909             : }
    3910             : 
    3911        4040 : static NTSTATUS fruit_streaminfo_rsrc_adouble(
    3912             :         vfs_handle_struct *handle,
    3913             :         struct files_struct *fsp,
    3914             :         const struct smb_filename *smb_fname,
    3915             :         TALLOC_CTX *mem_ctx,
    3916             :         unsigned int *pnum_streams,
    3917             :         struct stream_struct **pstreams)
    3918             : {
    3919        4040 :         struct stream_struct *stream = *pstreams;
    3920        4040 :         unsigned int num_streams = *pnum_streams;
    3921        4040 :         struct adouble *ad = NULL;
    3922             :         bool ok;
    3923             :         size_t rlen;
    3924             :         int i;
    3925             : 
    3926             :         /*
    3927             :          * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
    3928             :          * and if yes, remove it from the list
    3929             :          */
    3930        8822 :         for (i = 0; i < num_streams; i++) {
    3931        4782 :                 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
    3932           0 :                         break;
    3933             :                 }
    3934             :         }
    3935             : 
    3936        4040 :         if (i < num_streams) {
    3937           0 :                 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
    3938             :                             smb_fname_str_dbg(smb_fname));
    3939             : 
    3940           0 :                 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
    3941             :                                       AFPRESOURCE_STREAM);
    3942           0 :                 if (!ok) {
    3943           0 :                         return NT_STATUS_INTERNAL_ERROR;
    3944             :                 }
    3945             :         }
    3946             : 
    3947        4040 :         ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
    3948        4040 :         if (ad == NULL) {
    3949        3648 :                 return NT_STATUS_OK;
    3950             :         }
    3951             : 
    3952         392 :         rlen = ad_getentrylen(ad, ADEID_RFORK);
    3953         392 :         TALLOC_FREE(ad);
    3954             : 
    3955         392 :         if (rlen == 0) {
    3956         100 :                 return NT_STATUS_OK;
    3957             :         }
    3958             : 
    3959         292 :         ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
    3960             :                               AFPRESOURCE_STREAM_NAME, rlen,
    3961         292 :                               smb_roundup(handle->conn, rlen));
    3962         292 :         if (!ok) {
    3963           0 :                 return NT_STATUS_NO_MEMORY;
    3964             :         }
    3965             : 
    3966         292 :         return NT_STATUS_OK;
    3967             : }
    3968             : 
    3969        6748 : static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
    3970             :                                       struct files_struct *fsp,
    3971             :                                       const struct smb_filename *smb_fname,
    3972             :                                       TALLOC_CTX *mem_ctx,
    3973             :                                       unsigned int *pnum_streams,
    3974             :                                       struct stream_struct **pstreams)
    3975             : {
    3976        6748 :         struct fruit_config_data *config = NULL;
    3977             :         NTSTATUS status;
    3978             : 
    3979        6748 :         if (S_ISDIR(smb_fname->st.st_ex_mode)) {
    3980        1502 :                 return NT_STATUS_OK;
    3981             :         }
    3982             : 
    3983        5246 :         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
    3984             :                                 return NT_STATUS_INTERNAL_ERROR);
    3985             : 
    3986        5246 :         switch (config->rsrc) {
    3987        1206 :         case FRUIT_RSRC_STREAM:
    3988        1206 :                 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
    3989             :                                                       mem_ctx, pnum_streams,
    3990             :                                                       pstreams);
    3991        1206 :                 break;
    3992             : 
    3993           0 :         case FRUIT_RSRC_XATTR:
    3994           0 :                 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
    3995             :                                                      mem_ctx, pnum_streams,
    3996             :                                                      pstreams);
    3997           0 :                 break;
    3998             : 
    3999        4040 :         case FRUIT_RSRC_ADFILE:
    4000        4040 :                 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
    4001             :                                                        mem_ctx, pnum_streams,
    4002             :                                                        pstreams);
    4003        4040 :                 break;
    4004             : 
    4005           0 :         default:
    4006           0 :                 return NT_STATUS_INTERNAL_ERROR;
    4007             :         }
    4008             : 
    4009        5246 :         return status;
    4010             : }
    4011             : 
    4012        6748 : static void fruit_filter_empty_streams(unsigned int *pnum_streams,
    4013             :                                        struct stream_struct **pstreams)
    4014             : {
    4015        6748 :         unsigned num_streams = *pnum_streams;
    4016        6748 :         struct stream_struct *streams = *pstreams;
    4017        6748 :         unsigned i = 0;
    4018             : 
    4019        6748 :         if (!global_fruit_config.nego_aapl) {
    4020        5000 :                 return;
    4021             :         }
    4022             : 
    4023        3680 :         while (i < num_streams) {
    4024        1932 :                 struct smb_filename smb_fname = (struct smb_filename) {
    4025        1932 :                         .stream_name = streams[i].name,
    4026             :                 };
    4027             : 
    4028        1932 :                 if (is_ntfs_default_stream_smb_fname(&smb_fname)
    4029         470 :                     || streams[i].size > 0)
    4030             :                 {
    4031        1758 :                         i++;
    4032        1758 :                         continue;
    4033             :                 }
    4034             : 
    4035         174 :                 streams[i] = streams[num_streams - 1];
    4036         174 :                 num_streams--;
    4037             :         }
    4038             : 
    4039        1748 :         *pnum_streams = num_streams;
    4040             : }
    4041             : 
    4042        6748 : static NTSTATUS fruit_fstreaminfo(vfs_handle_struct *handle,
    4043             :                                  struct files_struct *fsp,
    4044             :                                  TALLOC_CTX *mem_ctx,
    4045             :                                  unsigned int *pnum_streams,
    4046             :                                  struct stream_struct **pstreams)
    4047             : {
    4048        6748 :         struct fruit_config_data *config = NULL;
    4049        6748 :         const struct smb_filename *smb_fname = NULL;
    4050             :         NTSTATUS status;
    4051             : 
    4052        6748 :         smb_fname = fsp->fsp_name;
    4053             : 
    4054        6748 :         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
    4055             :                                 return NT_STATUS_UNSUCCESSFUL);
    4056             : 
    4057        6748 :         DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
    4058             : 
    4059        6748 :         status = SMB_VFS_NEXT_FSTREAMINFO(handle, fsp, mem_ctx,
    4060             :                                          pnum_streams, pstreams);
    4061        6748 :         if (!NT_STATUS_IS_OK(status)) {
    4062           0 :                 return status;
    4063             :         }
    4064             : 
    4065        6748 :         fruit_filter_empty_streams(pnum_streams, pstreams);
    4066             : 
    4067        6748 :         status = fruit_streaminfo_meta(handle, fsp, smb_fname,
    4068             :                                        mem_ctx, pnum_streams, pstreams);
    4069        6748 :         if (!NT_STATUS_IS_OK(status)) {
    4070           0 :                 return status;
    4071             :         }
    4072             : 
    4073        6748 :         status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
    4074             :                                        mem_ctx, pnum_streams, pstreams);
    4075        6748 :         if (!NT_STATUS_IS_OK(status)) {
    4076           0 :                 return status;
    4077             :         }
    4078             : 
    4079        6748 :         return NT_STATUS_OK;
    4080             : }
    4081             : 
    4082        1856 : static int fruit_fntimes(vfs_handle_struct *handle,
    4083             :                          files_struct *fsp,
    4084             :                          struct smb_file_time *ft)
    4085             : {
    4086        1856 :         int rc = 0;
    4087        1856 :         struct adouble *ad = NULL;
    4088        1856 :         struct fruit_config_data *config = NULL;
    4089             : 
    4090        1856 :         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
    4091             :                                 return -1);
    4092             : 
    4093        2336 :         if ((config->meta != FRUIT_META_NETATALK) ||
    4094         480 :             is_omit_timespec(&ft->create_time))
    4095             :         {
    4096        1856 :                 return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
    4097             :         }
    4098             : 
    4099           0 :         DBG_DEBUG("set btime for %s to %s", fsp_str_dbg(fsp),
    4100             :                   time_to_asc(convert_timespec_to_time_t(ft->create_time)));
    4101             : 
    4102           0 :         ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
    4103           0 :         if (ad == NULL) {
    4104           0 :                 goto exit;
    4105             :         }
    4106             : 
    4107           0 :         ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
    4108             :                    convert_time_t_to_uint32_t(ft->create_time.tv_sec));
    4109             : 
    4110           0 :         rc = ad_fset(handle, ad, fsp);
    4111             : 
    4112           0 : exit:
    4113             : 
    4114           0 :         TALLOC_FREE(ad);
    4115           0 :         if (rc != 0) {
    4116           0 :                 DBG_WARNING("%s\n", fsp_str_dbg(fsp));
    4117           0 :                 return -1;
    4118             :         }
    4119           0 :         return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
    4120             : }
    4121             : 
    4122           0 : static int fruit_fallocate(struct vfs_handle_struct *handle,
    4123             :                            struct files_struct *fsp,
    4124             :                            uint32_t mode,
    4125             :                            off_t offset,
    4126             :                            off_t len)
    4127             : {
    4128           0 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    4129             : 
    4130           0 :         if (fio == NULL) {
    4131           0 :                 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
    4132             :         }
    4133             : 
    4134             :         /* Let the pwrite code path handle it. */
    4135           0 :         errno = ENOSYS;
    4136           0 :         return -1;
    4137             : }
    4138             : 
    4139           0 : static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
    4140             :                                       struct files_struct *fsp,
    4141             :                                       off_t offset)
    4142             : {
    4143             : #ifdef HAVE_ATTROPEN
    4144             :         return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
    4145             : #endif
    4146           0 :         return 0;
    4147             : }
    4148             : 
    4149          30 : static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
    4150             :                                         struct files_struct *fsp,
    4151             :                                         off_t offset)
    4152             : {
    4153          30 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    4154             :         int rc;
    4155          30 :         struct adouble *ad = NULL;
    4156             :         off_t ad_off;
    4157             : 
    4158          30 :         if (fio == NULL || fio->ad_fsp == NULL) {
    4159           0 :                 DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
    4160           0 :                 errno = EBADF;
    4161           0 :                 return -1;
    4162             :         }
    4163             : 
    4164          30 :         ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
    4165          30 :         if (ad == NULL) {
    4166           0 :                 DBG_ERR("ad_fget [%s] failed [%s]\n",
    4167             :                         fsp_str_dbg(fio->ad_fsp), strerror(errno));
    4168           0 :                 return -1;
    4169             :         }
    4170             : 
    4171          30 :         ad_off = ad_getentryoff(ad, ADEID_RFORK);
    4172             : 
    4173          30 :         rc = SMB_VFS_NEXT_FTRUNCATE(handle, fio->ad_fsp, offset + ad_off);
    4174          30 :         if (rc != 0) {
    4175           0 :                 TALLOC_FREE(ad);
    4176           0 :                 return -1;
    4177             :         }
    4178             : 
    4179          30 :         ad_setentrylen(ad, ADEID_RFORK, offset);
    4180             : 
    4181          30 :         rc = ad_fset(handle, ad, fio->ad_fsp);
    4182          30 :         if (rc != 0) {
    4183           0 :                 DBG_ERR("ad_fset [%s] failed [%s]\n",
    4184             :                         fsp_str_dbg(fio->ad_fsp), strerror(errno));
    4185           0 :                 TALLOC_FREE(ad);
    4186           0 :                 return -1;
    4187             :         }
    4188             : 
    4189          30 :         TALLOC_FREE(ad);
    4190          30 :         return 0;
    4191             : }
    4192             : 
    4193          10 : static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
    4194             :                                        struct files_struct *fsp,
    4195             :                                        off_t offset)
    4196             : {
    4197          10 :         return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
    4198             : }
    4199             : 
    4200          40 : static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
    4201             :                                 struct files_struct *fsp,
    4202             :                                 off_t offset)
    4203             : {
    4204          40 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    4205             :         int ret;
    4206             : 
    4207          40 :         if (fio == NULL) {
    4208           0 :                 DBG_ERR("Failed to fetch fsp extension\n");
    4209           0 :                 return -1;
    4210             :         }
    4211             : 
    4212          40 :         switch (fio->config->rsrc) {
    4213           0 :         case FRUIT_RSRC_XATTR:
    4214           0 :                 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
    4215           0 :                 break;
    4216             : 
    4217          30 :         case FRUIT_RSRC_ADFILE:
    4218          30 :                 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
    4219          30 :                 break;
    4220             : 
    4221          10 :         case FRUIT_RSRC_STREAM:
    4222          10 :                 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
    4223          10 :                 break;
    4224             : 
    4225           0 :         default:
    4226           0 :                 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
    4227           0 :                 return -1;
    4228             :         }
    4229             : 
    4230             : 
    4231          40 :         return ret;
    4232             : }
    4233             : 
    4234          40 : static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
    4235             :                                 struct files_struct *fsp,
    4236             :                                 off_t offset)
    4237             : {
    4238          40 :         if (offset > 60) {
    4239           8 :                 DBG_WARNING("ftruncate %s to %jd\n",
    4240             :                             fsp_str_dbg(fsp), (intmax_t)offset);
    4241             :                 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED  */
    4242           8 :                 errno = EOVERFLOW;
    4243           8 :                 return -1;
    4244             :         }
    4245             : 
    4246             :         /* OS X returns success but does nothing  */
    4247          32 :         DBG_INFO("ignoring ftruncate %s to %jd\n",
    4248             :                  fsp_str_dbg(fsp), (intmax_t)offset);
    4249          32 :         return 0;
    4250             : }
    4251             : 
    4252         178 : static int fruit_ftruncate(struct vfs_handle_struct *handle,
    4253             :                            struct files_struct *fsp,
    4254             :                            off_t offset)
    4255             : {
    4256         178 :         struct fio *fio = fruit_get_complete_fio(handle, fsp);
    4257             :         int ret;
    4258             : 
    4259         178 :         DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
    4260             :                   (intmax_t)offset);
    4261             : 
    4262         178 :         if (fio == NULL) {
    4263          98 :                 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
    4264             :         }
    4265             : 
    4266          80 :         if (fio->type == ADOUBLE_META) {
    4267          40 :                 ret = fruit_ftruncate_meta(handle, fsp, offset);
    4268             :         } else {
    4269          40 :                 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
    4270             :         }
    4271             : 
    4272          80 :         DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
    4273          80 :         return ret;
    4274             : }
    4275             : 
    4276       10617 : static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
    4277             :                                   struct smb_request *req,
    4278             :                                   struct files_struct *dirfsp,
    4279             :                                   struct smb_filename *smb_fname,
    4280             :                                   uint32_t access_mask,
    4281             :                                   uint32_t share_access,
    4282             :                                   uint32_t create_disposition,
    4283             :                                   uint32_t create_options,
    4284             :                                   uint32_t file_attributes,
    4285             :                                   uint32_t oplock_request,
    4286             :                                   const struct smb2_lease *lease,
    4287             :                                   uint64_t allocation_size,
    4288             :                                   uint32_t private_flags,
    4289             :                                   struct security_descriptor *sd,
    4290             :                                   struct ea_list *ea_list,
    4291             :                                   files_struct **result,
    4292             :                                   int *pinfo,
    4293             :                                   const struct smb2_create_blobs *in_context_blobs,
    4294             :                                   struct smb2_create_blobs *out_context_blobs)
    4295             : {
    4296             :         NTSTATUS status;
    4297       10617 :         struct fruit_config_data *config = NULL;
    4298       10617 :         files_struct *fsp = NULL;
    4299       10617 :         bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
    4300             :         int ret;
    4301             : 
    4302       10617 :         status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
    4303       10617 :         if (!NT_STATUS_IS_OK(status)) {
    4304           0 :                 goto fail;
    4305             :         }
    4306             : 
    4307       10617 :         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
    4308             :                                 return NT_STATUS_UNSUCCESSFUL);
    4309             : 
    4310       10617 :         if (is_apple_stream(smb_fname->stream_name) &&
    4311        3630 :             !internal_open &&
    4312        3500 :             config->convert_adouble)
    4313             :         {
    4314        3500 :                 uint32_t conv_flags  = 0;
    4315             : 
    4316        3500 :                 if (config->wipe_intentionally_left_blank_rfork) {
    4317         870 :                         conv_flags |= AD_CONV_WIPE_BLANK;
    4318             :                 }
    4319        3500 :                 if (config->delete_empty_adfiles) {
    4320         866 :                         conv_flags |= AD_CONV_DELETE;
    4321             :                 }
    4322             : 
    4323        3500 :                 ret = ad_convert(handle,
    4324             :                                  smb_fname,
    4325             :                                  macos_string_replace_map,
    4326             :                                  conv_flags);
    4327        3500 :                 if (ret != 0) {
    4328           6 :                         DBG_ERR("ad_convert(\"%s\") failed\n",
    4329             :                                 smb_fname_str_dbg(smb_fname));
    4330             :                 }
    4331             :         }
    4332             : 
    4333       10617 :         status = SMB_VFS_NEXT_CREATE_FILE(
    4334             :                 handle, req, dirfsp, smb_fname,
    4335             :                 access_mask, share_access,
    4336             :                 create_disposition, create_options,
    4337             :                 file_attributes, oplock_request,
    4338             :                 lease,
    4339             :                 allocation_size, private_flags,
    4340             :                 sd, ea_list, result,
    4341             :                 pinfo, in_context_blobs, out_context_blobs);
    4342       10617 :         if (!NT_STATUS_IS_OK(status)) {
    4343        1760 :                 return status;
    4344             :         }
    4345             : 
    4346        8857 :         fsp = *result;
    4347             : 
    4348        8857 :         if (global_fruit_config.nego_aapl) {
    4349        2248 :                 if (config->posix_rename && fsp->fsp_flags.is_directory) {
    4350             :                         /*
    4351             :                          * Enable POSIX directory rename behaviour
    4352             :                          */
    4353         398 :                         fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
    4354             :                 }
    4355             :         }
    4356             : 
    4357             :         /*
    4358             :          * If this is a plain open for existing files, opening an 0
    4359             :          * byte size resource fork MUST fail with
    4360             :          * NT_STATUS_OBJECT_NAME_NOT_FOUND.
    4361             :          *
    4362             :          * Cf the vfs_fruit torture tests in test_rfork_create().
    4363             :          */
    4364        8857 :         if (global_fruit_config.nego_aapl &&
    4365        1478 :             create_disposition == FILE_OPEN &&
    4366        2504 :             smb_fname->st.st_ex_size == 0 &&
    4367        1026 :             is_named_stream(smb_fname))
    4368             :         {
    4369         290 :                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
    4370         290 :                 goto fail;
    4371             :         }
    4372             : 
    4373        8567 :         if (is_named_stream(smb_fname) || fsp->fsp_flags.is_directory) {
    4374        5319 :                 return status;
    4375             :         }
    4376             : 
    4377        3248 :         if ((config->locking == FRUIT_LOCKING_NETATALK) &&
    4378        1276 :             (fsp->op != NULL) &&
    4379        1272 :             !fsp->fsp_flags.is_pathref)
    4380             :         {
    4381         737 :                 status = fruit_check_access(
    4382             :                         handle, *result,
    4383             :                         access_mask,
    4384             :                         share_access);
    4385         737 :                 if (!NT_STATUS_IS_OK(status)) {
    4386           2 :                         goto fail;
    4387             :                 }
    4388             :         }
    4389             : 
    4390        3246 :         return status;
    4391             : 
    4392         292 : fail:
    4393         292 :         DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
    4394             : 
    4395         292 :         if (fsp) {
    4396         292 :                 close_file_free(req, &fsp, ERROR_CLOSE);
    4397         292 :                 *result = NULL;
    4398             :         }
    4399             : 
    4400         292 :         return status;
    4401             : }
    4402             : 
    4403        1335 : static NTSTATUS fruit_freaddir_attr(struct vfs_handle_struct *handle,
    4404             :                                     struct files_struct *fsp,
    4405             :                                     TALLOC_CTX *mem_ctx,
    4406             :                                     struct readdir_attr_data **pattr_data)
    4407             : {
    4408        1335 :         struct fruit_config_data *config = NULL;
    4409             :         struct readdir_attr_data *attr_data;
    4410        1335 :         uint32_t conv_flags  = 0;
    4411             :         NTSTATUS status;
    4412             :         int ret;
    4413             : 
    4414        1335 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    4415             :                                 struct fruit_config_data,
    4416             :                                 return NT_STATUS_UNSUCCESSFUL);
    4417             : 
    4418        1335 :         if (!global_fruit_config.nego_aapl) {
    4419        1031 :                 return SMB_VFS_NEXT_FREADDIR_ATTR(handle,
    4420             :                                                   fsp,
    4421             :                                                   mem_ctx,
    4422             :                                                   pattr_data);
    4423             :         }
    4424             : 
    4425         304 :         DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
    4426             : 
    4427         304 :         if (config->convert_adouble) {
    4428         304 :                 if (config->wipe_intentionally_left_blank_rfork) {
    4429          92 :                         conv_flags |= AD_CONV_WIPE_BLANK;
    4430             :                 }
    4431         304 :                 if (config->delete_empty_adfiles) {
    4432          76 :                         conv_flags |= AD_CONV_DELETE;
    4433             :                 }
    4434             : 
    4435         304 :                 ret = ad_convert(handle,
    4436         304 :                                  fsp->fsp_name,
    4437             :                                  macos_string_replace_map,
    4438             :                                  conv_flags);
    4439         304 :                 if (ret != 0) {
    4440           0 :                         DBG_ERR("ad_convert(\"%s\") failed\n",
    4441             :                                 fsp_str_dbg(fsp));
    4442             :                 }
    4443             :         }
    4444             : 
    4445         304 :         *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
    4446         304 :         if (*pattr_data == NULL) {
    4447           0 :                 return NT_STATUS_NO_MEMORY;
    4448             :         }
    4449         304 :         attr_data = *pattr_data;
    4450         304 :         attr_data->type = RDATTR_AAPL;
    4451             : 
    4452             :         /*
    4453             :          * Mac metadata: compressed FinderInfo, resource fork length
    4454             :          * and creation date
    4455             :          */
    4456         304 :         status = readdir_attr_macmeta(handle, fsp->fsp_name, attr_data);
    4457         304 :         if (!NT_STATUS_IS_OK(status)) {
    4458             :                 /*
    4459             :                  * Error handling is tricky: if we return failure from
    4460             :                  * this function, the corresponding directory entry
    4461             :                  * will to be passed to the client, so we really just
    4462             :                  * want to error out on fatal errors.
    4463             :                  */
    4464           0 :                 if  (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
    4465           0 :                         goto fail;
    4466             :                 }
    4467             :         }
    4468             : 
    4469             :         /*
    4470             :          * UNIX mode
    4471             :          */
    4472         304 :         if (config->unix_info_enabled) {
    4473         304 :                 attr_data->attr_data.aapl.unix_mode =
    4474         304 :                         fsp->fsp_name->st.st_ex_mode;
    4475             :         }
    4476             : 
    4477             :         /*
    4478             :          * max_access
    4479             :          */
    4480         304 :         if (!config->readdir_attr_max_access) {
    4481           0 :                 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
    4482             :         } else {
    4483         304 :                 status = smbd_calculate_access_mask_fsp(fsp->conn->cwd_fsp,
    4484             :                         fsp,
    4485             :                         false,
    4486             :                         SEC_FLAG_MAXIMUM_ALLOWED,
    4487             :                         &attr_data->attr_data.aapl.max_access);
    4488         304 :                 if (!NT_STATUS_IS_OK(status)) {
    4489           0 :                         goto fail;
    4490             :                 }
    4491             :         }
    4492             : 
    4493         304 :         return NT_STATUS_OK;
    4494             : 
    4495           0 : fail:
    4496           0 :         DBG_WARNING("Path [%s], error: %s\n", fsp_str_dbg(fsp),
    4497             :                    nt_errstr(status));
    4498           0 :         TALLOC_FREE(*pattr_data);
    4499           0 :         return status;
    4500             : }
    4501             : 
    4502       17448 : static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
    4503             :                                   files_struct *fsp,
    4504             :                                   uint32_t security_info,
    4505             :                                   TALLOC_CTX *mem_ctx,
    4506             :                                   struct security_descriptor **ppdesc)
    4507             : {
    4508             :         NTSTATUS status;
    4509             :         struct security_ace ace;
    4510             :         struct dom_sid sid;
    4511             :         struct fruit_config_data *config;
    4512             : 
    4513       17448 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    4514             :                                 struct fruit_config_data,
    4515             :                                 return NT_STATUS_UNSUCCESSFUL);
    4516             : 
    4517       17448 :         status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
    4518             :                                           mem_ctx, ppdesc);
    4519       17448 :         if (!NT_STATUS_IS_OK(status)) {
    4520           0 :                 return status;
    4521             :         }
    4522             : 
    4523             :         /*
    4524             :          * Add MS NFS style ACEs with uid, gid and mode
    4525             :          */
    4526       17448 :         if (!global_fruit_config.nego_aapl) {
    4527       12734 :                 return NT_STATUS_OK;
    4528             :         }
    4529        4714 :         if (!config->unix_info_enabled) {
    4530           0 :                 return NT_STATUS_OK;
    4531             :         }
    4532             : 
    4533             :         /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
    4534        4714 :         status = remove_virtual_nfs_aces(*ppdesc);
    4535        4714 :         if (!NT_STATUS_IS_OK(status)) {
    4536           0 :                 DBG_WARNING("failed to remove MS NFS style ACEs\n");
    4537           0 :                 return status;
    4538             :         }
    4539             : 
    4540             :         /* MS NFS style mode */
    4541        4714 :         sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
    4542        4714 :         init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
    4543        4714 :         status = security_descriptor_dacl_add(*ppdesc, &ace);
    4544        4714 :         if (!NT_STATUS_IS_OK(status)) {
    4545           0 :                 DEBUG(1,("failed to add MS NFS style ACE\n"));
    4546           0 :                 return status;
    4547             :         }
    4548             : 
    4549             :         /* MS NFS style uid */
    4550        4714 :         sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
    4551        4714 :         init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
    4552        4714 :         status = security_descriptor_dacl_add(*ppdesc, &ace);
    4553        4714 :         if (!NT_STATUS_IS_OK(status)) {
    4554           0 :                 DEBUG(1,("failed to add MS NFS style ACE\n"));
    4555           0 :                 return status;
    4556             :         }
    4557             : 
    4558             :         /* MS NFS style gid */
    4559        4714 :         sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
    4560        4714 :         init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
    4561        4714 :         status = security_descriptor_dacl_add(*ppdesc, &ace);
    4562        4714 :         if (!NT_STATUS_IS_OK(status)) {
    4563           0 :                 DEBUG(1,("failed to add MS NFS style ACE\n"));
    4564           0 :                 return status;
    4565             :         }
    4566             : 
    4567        4714 :         return NT_STATUS_OK;
    4568             : }
    4569             : 
    4570        1161 : static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
    4571             :                                   files_struct *fsp,
    4572             :                                   uint32_t security_info_sent,
    4573             :                                   const struct security_descriptor *orig_psd)
    4574             : {
    4575             :         NTSTATUS status;
    4576             :         bool do_chmod;
    4577        1161 :         mode_t ms_nfs_mode = 0;
    4578             :         int result;
    4579        1161 :         struct security_descriptor *psd = NULL;
    4580        1161 :         uint32_t orig_num_aces = 0;
    4581             : 
    4582        1161 :         if (orig_psd->dacl != NULL) {
    4583        1161 :                 orig_num_aces = orig_psd->dacl->num_aces;
    4584             :         }
    4585             : 
    4586        1161 :         psd = security_descriptor_copy(talloc_tos(), orig_psd);
    4587        1161 :         if (psd == NULL) {
    4588           0 :                 return NT_STATUS_NO_MEMORY;
    4589             :         }
    4590             : 
    4591        1161 :         DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
    4592             : 
    4593        1161 :         status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
    4594        1161 :         if (!NT_STATUS_IS_OK(status)) {
    4595           0 :                 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
    4596           0 :                 TALLOC_FREE(psd);
    4597           0 :                 return status;
    4598             :         }
    4599             : 
    4600             :         /*
    4601             :          * If only ms_nfs ACE entries were sent, ensure we set the DACL
    4602             :          * sent/present flags correctly now we've removed them.
    4603             :          */
    4604             : 
    4605        1161 :         if (orig_num_aces != 0) {
    4606             :                 /*
    4607             :                  * Are there any ACE's left ?
    4608             :                  */
    4609        1161 :                 if (psd->dacl->num_aces == 0) {
    4610             :                         /* No - clear the DACL sent/present flags. */
    4611           0 :                         security_info_sent &= ~SECINFO_DACL;
    4612           0 :                         psd->type &= ~SEC_DESC_DACL_PRESENT;
    4613             :                 }
    4614             :         }
    4615             : 
    4616        1161 :         status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
    4617        1161 :         if (!NT_STATUS_IS_OK(status)) {
    4618           0 :                 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
    4619           0 :                 TALLOC_FREE(psd);
    4620           0 :                 return status;
    4621             :         }
    4622             : 
    4623        1161 :         if (do_chmod) {
    4624           8 :                 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
    4625           8 :                 if (result != 0) {
    4626           0 :                         DBG_WARNING("%s, result: %d, %04o error %s\n",
    4627             :                                 fsp_str_dbg(fsp),
    4628             :                                 result,
    4629             :                                 (unsigned)ms_nfs_mode,
    4630             :                                 strerror(errno));
    4631           0 :                         status = map_nt_error_from_unix(errno);
    4632           0 :                         TALLOC_FREE(psd);
    4633           0 :                         return status;
    4634             :                 }
    4635             :         }
    4636             : 
    4637        1161 :         TALLOC_FREE(psd);
    4638        1161 :         return NT_STATUS_OK;
    4639             : }
    4640             : 
    4641             : static struct vfs_offload_ctx *fruit_offload_ctx;
    4642             : 
    4643             : struct fruit_offload_read_state {
    4644             :         struct vfs_handle_struct *handle;
    4645             :         struct tevent_context *ev;
    4646             :         files_struct *fsp;
    4647             :         uint32_t fsctl;
    4648             :         uint32_t flags;
    4649             :         uint64_t xferlen;
    4650             :         DATA_BLOB token;
    4651             : };
    4652             : 
    4653             : static void fruit_offload_read_done(struct tevent_req *subreq);
    4654             : 
    4655          40 : static struct tevent_req *fruit_offload_read_send(
    4656             :         TALLOC_CTX *mem_ctx,
    4657             :         struct tevent_context *ev,
    4658             :         struct vfs_handle_struct *handle,
    4659             :         files_struct *fsp,
    4660             :         uint32_t fsctl,
    4661             :         uint32_t ttl,
    4662             :         off_t offset,
    4663             :         size_t to_copy)
    4664             : {
    4665          40 :         struct tevent_req *req = NULL;
    4666          40 :         struct tevent_req *subreq = NULL;
    4667          40 :         struct fruit_offload_read_state *state = NULL;
    4668             : 
    4669          40 :         req = tevent_req_create(mem_ctx, &state,
    4670             :                                 struct fruit_offload_read_state);
    4671          40 :         if (req == NULL) {
    4672           0 :                 return NULL;
    4673             :         }
    4674          40 :         *state = (struct fruit_offload_read_state) {
    4675             :                 .handle = handle,
    4676             :                 .ev = ev,
    4677             :                 .fsp = fsp,
    4678             :                 .fsctl = fsctl,
    4679             :         };
    4680             : 
    4681          40 :         subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
    4682             :                                                 fsctl, ttl, offset, to_copy);
    4683          40 :         if (tevent_req_nomem(subreq, req)) {
    4684           0 :                 return tevent_req_post(req, ev);
    4685             :         }
    4686          40 :         tevent_req_set_callback(subreq, fruit_offload_read_done, req);
    4687          40 :         return req;
    4688             : }
    4689             : 
    4690          40 : static void fruit_offload_read_done(struct tevent_req *subreq)
    4691             : {
    4692          40 :         struct tevent_req *req = tevent_req_callback_data(
    4693             :                 subreq, struct tevent_req);
    4694          40 :         struct fruit_offload_read_state *state = tevent_req_data(
    4695             :                 req, struct fruit_offload_read_state);
    4696             :         NTSTATUS status;
    4697             : 
    4698          40 :         status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
    4699             :                                                 state->handle,
    4700             :                                                 state,
    4701             :                                                 &state->flags,
    4702             :                                                 &state->xferlen,
    4703             :                                                 &state->token);
    4704          40 :         TALLOC_FREE(subreq);
    4705          40 :         if (tevent_req_nterror(req, status)) {
    4706           0 :                 return;
    4707             :         }
    4708             : 
    4709          40 :         if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
    4710           0 :                 tevent_req_done(req);
    4711           0 :                 return;
    4712             :         }
    4713             : 
    4714          40 :         status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
    4715             :                                             &fruit_offload_ctx);
    4716          40 :         if (tevent_req_nterror(req, status)) {
    4717           0 :                 return;
    4718             :         }
    4719             : 
    4720          40 :         status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
    4721          40 :                                                 state->fsp,
    4722          40 :                                                 &state->token);
    4723          40 :         if (tevent_req_nterror(req, status)) {
    4724           0 :                 return;
    4725             :         }
    4726             : 
    4727          40 :         tevent_req_done(req);
    4728          40 :         return;
    4729             : }
    4730             : 
    4731          40 : static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
    4732             :                                         struct vfs_handle_struct *handle,
    4733             :                                         TALLOC_CTX *mem_ctx,
    4734             :                                         uint32_t *flags,
    4735             :                                         uint64_t *xferlen,
    4736             :                                         DATA_BLOB *token)
    4737             : {
    4738          40 :         struct fruit_offload_read_state *state = tevent_req_data(
    4739             :                 req, struct fruit_offload_read_state);
    4740             :         NTSTATUS status;
    4741             : 
    4742          40 :         if (tevent_req_is_nterror(req, &status)) {
    4743           0 :                 tevent_req_received(req);
    4744           0 :                 return status;
    4745             :         }
    4746             : 
    4747          40 :         *flags = state->flags;
    4748          40 :         *xferlen = state->xferlen;
    4749          40 :         token->length = state->token.length;
    4750          40 :         token->data = talloc_move(mem_ctx, &state->token.data);
    4751             : 
    4752          40 :         tevent_req_received(req);
    4753          40 :         return NT_STATUS_OK;
    4754             : }
    4755             : 
    4756             : struct fruit_offload_write_state {
    4757             :         struct vfs_handle_struct *handle;
    4758             :         off_t copied;
    4759             :         struct files_struct *src_fsp;
    4760             :         struct files_struct *dst_fsp;
    4761             :         bool is_copyfile;
    4762             : };
    4763             : 
    4764             : static void fruit_offload_write_done(struct tevent_req *subreq);
    4765          40 : static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
    4766             :                                                 TALLOC_CTX *mem_ctx,
    4767             :                                                 struct tevent_context *ev,
    4768             :                                                 uint32_t fsctl,
    4769             :                                                 DATA_BLOB *token,
    4770             :                                                 off_t transfer_offset,
    4771             :                                                 struct files_struct *dest_fsp,
    4772             :                                                 off_t dest_off,
    4773             :                                                 off_t num)
    4774             : {
    4775             :         struct tevent_req *req, *subreq;
    4776             :         struct fruit_offload_write_state *state;
    4777             :         NTSTATUS status;
    4778             :         struct fruit_config_data *config;
    4779          40 :         off_t src_off = transfer_offset;
    4780          40 :         files_struct *src_fsp = NULL;
    4781          40 :         off_t to_copy = num;
    4782          40 :         bool copyfile_enabled = false;
    4783             : 
    4784          40 :         DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
    4785             :                   (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
    4786             : 
    4787          40 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    4788             :                                 struct fruit_config_data,
    4789             :                                 return NULL);
    4790             : 
    4791          40 :         req = tevent_req_create(mem_ctx, &state,
    4792             :                                 struct fruit_offload_write_state);
    4793          40 :         if (req == NULL) {
    4794           0 :                 return NULL;
    4795             :         }
    4796          40 :         state->handle = handle;
    4797          40 :         state->dst_fsp = dest_fsp;
    4798             : 
    4799          40 :         switch (fsctl) {
    4800          40 :         case FSCTL_SRV_COPYCHUNK:
    4801             :         case FSCTL_SRV_COPYCHUNK_WRITE:
    4802          40 :                 copyfile_enabled = config->copyfile_enabled;
    4803          40 :                 break;
    4804           0 :         default:
    4805           0 :                 break;
    4806             :         }
    4807             : 
    4808             :         /*
    4809             :          * Check if this a OS X copyfile style copychunk request with
    4810             :          * a requested chunk count of 0 that was translated to a
    4811             :          * offload_write_send VFS call overloading the parameters src_off
    4812             :          * = dest_off = num = 0.
    4813             :          */
    4814          40 :         if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
    4815           8 :                 status = vfs_offload_token_db_fetch_fsp(
    4816             :                         fruit_offload_ctx, token, &src_fsp);
    4817           8 :                 if (tevent_req_nterror(req, status)) {
    4818           0 :                         return tevent_req_post(req, ev);
    4819             :                 }
    4820           8 :                 state->src_fsp = src_fsp;
    4821             : 
    4822           8 :                 status = vfs_stat_fsp(src_fsp);
    4823           8 :                 if (tevent_req_nterror(req, status)) {
    4824           0 :                         return tevent_req_post(req, ev);
    4825             :                 }
    4826             : 
    4827           8 :                 to_copy = src_fsp->fsp_name->st.st_ex_size;
    4828           8 :                 state->is_copyfile = true;
    4829             :         }
    4830             : 
    4831          40 :         subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
    4832             :                                               mem_ctx,
    4833             :                                               ev,
    4834             :                                               fsctl,
    4835             :                                               token,
    4836             :                                               transfer_offset,
    4837             :                                               dest_fsp,
    4838             :                                               dest_off,
    4839             :                                               to_copy);
    4840          40 :         if (tevent_req_nomem(subreq, req)) {
    4841           0 :                 return tevent_req_post(req, ev);
    4842             :         }
    4843             : 
    4844          40 :         tevent_req_set_callback(subreq, fruit_offload_write_done, req);
    4845          40 :         return req;
    4846             : }
    4847             : 
    4848          40 : static void fruit_offload_write_done(struct tevent_req *subreq)
    4849             : {
    4850          40 :         struct tevent_req *req = tevent_req_callback_data(
    4851             :                 subreq, struct tevent_req);
    4852          40 :         struct fruit_offload_write_state *state = tevent_req_data(
    4853             :                 req, struct fruit_offload_write_state);
    4854             :         NTSTATUS status;
    4855          40 :         unsigned int num_streams = 0;
    4856          40 :         struct stream_struct *streams = NULL;
    4857             :         unsigned int i;
    4858          40 :         struct smb_filename *src_fname_tmp = NULL;
    4859          40 :         struct smb_filename *dst_fname_tmp = NULL;
    4860             : 
    4861          40 :         status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
    4862             :                                               subreq,
    4863             :                                               &state->copied);
    4864          40 :         TALLOC_FREE(subreq);
    4865          40 :         if (tevent_req_nterror(req, status)) {
    4866          32 :                 return;
    4867             :         }
    4868             : 
    4869          40 :         if (!state->is_copyfile) {
    4870          32 :                 tevent_req_done(req);
    4871          32 :                 return;
    4872             :         }
    4873             : 
    4874             :         /*
    4875             :          * Now copy all remaining streams. We know the share supports
    4876             :          * streams, because we're in vfs_fruit. We don't do this async
    4877             :          * because streams are few and small.
    4878             :          */
    4879           8 :         status = vfs_fstreaminfo(state->src_fsp,
    4880             :                                 req, &num_streams, &streams);
    4881           8 :         if (tevent_req_nterror(req, status)) {
    4882           0 :                 return;
    4883             :         }
    4884             : 
    4885           8 :         if (num_streams == 1) {
    4886             :                 /* There is always one stream, ::$DATA. */
    4887           0 :                 tevent_req_done(req);
    4888           0 :                 return;
    4889             :         }
    4890             : 
    4891          32 :         for (i = 0; i < num_streams; i++) {
    4892          24 :                 DEBUG(10, ("%s: stream: '%s'/%zu\n",
    4893             :                           __func__, streams[i].name, (size_t)streams[i].size));
    4894             : 
    4895          24 :                 src_fname_tmp = synthetic_smb_fname(
    4896             :                         req,
    4897          24 :                         state->src_fsp->fsp_name->base_name,
    4898          24 :                         streams[i].name,
    4899             :                         NULL,
    4900          24 :                         state->src_fsp->fsp_name->twrp,
    4901          24 :                         state->src_fsp->fsp_name->flags);
    4902          24 :                 if (tevent_req_nomem(src_fname_tmp, req)) {
    4903           0 :                         return;
    4904             :                 }
    4905             : 
    4906          24 :                 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
    4907           8 :                         TALLOC_FREE(src_fname_tmp);
    4908           8 :                         continue;
    4909             :                 }
    4910             : 
    4911          16 :                 dst_fname_tmp = synthetic_smb_fname(
    4912             :                         req,
    4913          16 :                         state->dst_fsp->fsp_name->base_name,
    4914          16 :                         streams[i].name,
    4915             :                         NULL,
    4916          16 :                         state->dst_fsp->fsp_name->twrp,
    4917          16 :                         state->dst_fsp->fsp_name->flags);
    4918          16 :                 if (tevent_req_nomem(dst_fname_tmp, req)) {
    4919           0 :                         TALLOC_FREE(src_fname_tmp);
    4920           0 :                         return;
    4921             :                 }
    4922             : 
    4923          16 :                 status = copy_file(req,
    4924          16 :                                    state->handle->conn,
    4925             :                                    src_fname_tmp,
    4926             :                                    dst_fname_tmp,
    4927             :                                    FILE_CREATE);
    4928          16 :                 if (!NT_STATUS_IS_OK(status)) {
    4929           0 :                         DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
    4930             :                                   smb_fname_str_dbg(src_fname_tmp),
    4931             :                                   smb_fname_str_dbg(dst_fname_tmp),
    4932             :                                   nt_errstr(status)));
    4933           0 :                         TALLOC_FREE(src_fname_tmp);
    4934           0 :                         TALLOC_FREE(dst_fname_tmp);
    4935           0 :                         tevent_req_nterror(req, status);
    4936           0 :                         return;
    4937             :                 }
    4938             : 
    4939          16 :                 TALLOC_FREE(src_fname_tmp);
    4940          16 :                 TALLOC_FREE(dst_fname_tmp);
    4941             :         }
    4942             : 
    4943           8 :         TALLOC_FREE(streams);
    4944           8 :         TALLOC_FREE(src_fname_tmp);
    4945           8 :         TALLOC_FREE(dst_fname_tmp);
    4946           8 :         tevent_req_done(req);
    4947             : }
    4948             : 
    4949          40 : static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
    4950             :                                       struct tevent_req *req,
    4951             :                                       off_t *copied)
    4952             : {
    4953          40 :         struct fruit_offload_write_state *state = tevent_req_data(
    4954             :                 req, struct fruit_offload_write_state);
    4955             :         NTSTATUS status;
    4956             : 
    4957          40 :         if (tevent_req_is_nterror(req, &status)) {
    4958           0 :                 DEBUG(1, ("server side copy chunk failed: %s\n",
    4959             :                           nt_errstr(status)));
    4960           0 :                 *copied = 0;
    4961           0 :                 tevent_req_received(req);
    4962           0 :                 return status;
    4963             :         }
    4964             : 
    4965          40 :         *copied = state->copied;
    4966          40 :         tevent_req_received(req);
    4967             : 
    4968          40 :         return NT_STATUS_OK;
    4969             : }
    4970             : 
    4971           2 : static char *fruit_get_bandsize_line(char **lines, int numlines)
    4972             : {
    4973             :         static regex_t re;
    4974             :         static bool re_initialized = false;
    4975             :         int i;
    4976             :         int ret;
    4977             : 
    4978           2 :         if (!re_initialized) {
    4979           2 :                 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
    4980           2 :                 if (ret != 0) {
    4981           0 :                         return NULL;
    4982             :                 }
    4983           2 :                 re_initialized = true;
    4984             :         }
    4985             : 
    4986           4 :         for (i = 0; i < numlines; i++) {
    4987             :                 regmatch_t matches[1];
    4988             : 
    4989           4 :                 ret = regexec(&re, lines[i], 1, matches, 0);
    4990           4 :                 if (ret == 0) {
    4991             :                         /*
    4992             :                          * Check if the match was on the last line, sa we want
    4993             :                          * the subsequent line.
    4994             :                          */
    4995           2 :                         if (i + 1 == numlines) {
    4996           2 :                                 return NULL;
    4997             :                         }
    4998           2 :                         return lines[i + 1];
    4999             :                 }
    5000           2 :                 if (ret != REG_NOMATCH) {
    5001           0 :                         return NULL;
    5002             :                 }
    5003             :         }
    5004             : 
    5005           0 :         return NULL;
    5006             : }
    5007             : 
    5008           2 : static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
    5009             : {
    5010             :         static regex_t re;
    5011             :         static bool re_initialized = false;
    5012             :         regmatch_t matches[2];
    5013             :         uint64_t band_size;
    5014             :         int ret;
    5015             :         bool ok;
    5016             : 
    5017           2 :         if (!re_initialized) {
    5018           2 :                 ret = regcomp(&re,
    5019             :                               "^[[:blank:]]*"
    5020             :                               "<integer>\\([[:digit:]]*\\)</integer>$",
    5021             :                               0);
    5022           2 :                 if (ret != 0) {
    5023           0 :                         return false;
    5024             :                 }
    5025           2 :                 re_initialized = true;
    5026             :         }
    5027             : 
    5028           2 :         ret = regexec(&re, line, 2, matches, 0);
    5029           2 :         if (ret != 0) {
    5030           0 :                 DBG_ERR("regex failed [%s]\n", line);
    5031           0 :                 return false;
    5032             :         }
    5033             : 
    5034           2 :         line[matches[1].rm_eo] = '\0';
    5035             : 
    5036           2 :         ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
    5037           2 :         if (!ok) {
    5038           0 :                 return false;
    5039             :         }
    5040           2 :         *_band_size = (size_t)band_size;
    5041           2 :         return true;
    5042             : }
    5043             : 
    5044             : /*
    5045             :  * This reads and parses an Info.plist from a TM sparsebundle looking for the
    5046             :  * "band-size" key and value.
    5047             :  */
    5048           2 : static bool fruit_get_bandsize(vfs_handle_struct *handle,
    5049             :                                const char *dir,
    5050             :                                size_t *band_size)
    5051             : {
    5052             : #define INFO_PLIST_MAX_SIZE 64*1024
    5053           2 :         char *plist = NULL;
    5054           2 :         struct smb_filename *smb_fname = NULL;
    5055           2 :         files_struct *fsp = NULL;
    5056           2 :         uint8_t *file_data = NULL;
    5057           2 :         char **lines = NULL;
    5058           2 :         char *band_size_line = NULL;
    5059             :         size_t plist_file_size;
    5060             :         ssize_t nread;
    5061             :         int numlines;
    5062             :         int ret;
    5063           2 :         bool ok = false;
    5064             :         NTSTATUS status;
    5065             : 
    5066           2 :         plist = talloc_asprintf(talloc_tos(),
    5067             :                                 "%s/%s/Info.plist",
    5068           2 :                                 handle->conn->connectpath,
    5069             :                                 dir);
    5070           2 :         if (plist == NULL) {
    5071           0 :                 ok = false;
    5072           0 :                 goto out;
    5073             :         }
    5074             : 
    5075           2 :         smb_fname = synthetic_smb_fname(talloc_tos(),
    5076             :                                         plist,
    5077             :                                         NULL,
    5078             :                                         NULL,
    5079             :                                         0,
    5080             :                                         0);
    5081           2 :         if (smb_fname == NULL) {
    5082           0 :                 ok = false;
    5083           0 :                 goto out;
    5084             :         }
    5085             : 
    5086           2 :         ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
    5087           2 :         if (ret != 0) {
    5088           0 :                 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
    5089           0 :                 ok = true;
    5090           0 :                 goto out;
    5091             :         }
    5092             : 
    5093           2 :         plist_file_size = smb_fname->st.st_ex_size;
    5094             : 
    5095           2 :         if (plist_file_size > INFO_PLIST_MAX_SIZE) {
    5096           0 :                 DBG_INFO("%s is too large, ignoring\n", plist);
    5097           0 :                 ok = true;
    5098           0 :                 goto out;
    5099             :         }
    5100             : 
    5101           2 :         status = SMB_VFS_NEXT_CREATE_FILE(
    5102             :                 handle,                         /* conn */
    5103             :                 NULL,                           /* req */
    5104             :                 NULL,                           /* dirfsp */
    5105             :                 smb_fname,                      /* fname */
    5106             :                 FILE_GENERIC_READ,              /* access_mask */
    5107             :                 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
    5108             :                 FILE_OPEN,                      /* create_disposition */
    5109             :                 0,                              /* create_options */
    5110             :                 0,                              /* file_attributes */
    5111             :                 INTERNAL_OPEN_ONLY,             /* oplock_request */
    5112             :                 NULL,                           /* lease */
    5113             :                 0,                              /* allocation_size */
    5114             :                 0,                              /* private_flags */
    5115             :                 NULL,                           /* sd */
    5116             :                 NULL,                           /* ea_list */
    5117             :                 &fsp,                               /* result */
    5118             :                 NULL,                           /* psbuf */
    5119             :                 NULL, NULL);                    /* create context */
    5120           2 :         if (!NT_STATUS_IS_OK(status)) {
    5121           0 :                 DBG_INFO("Opening [%s] failed [%s]\n",
    5122             :                          smb_fname_str_dbg(smb_fname), nt_errstr(status));
    5123           0 :                 ok = false;
    5124           0 :                 goto out;
    5125             :         }
    5126             : 
    5127           2 :         file_data = talloc_zero_array(talloc_tos(),
    5128             :                                       uint8_t,
    5129             :                                       plist_file_size + 1);
    5130           2 :         if (file_data == NULL) {
    5131           0 :                 ok = false;
    5132           0 :                 goto out;
    5133             :         }
    5134             : 
    5135           2 :         nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
    5136           2 :         if (nread != plist_file_size) {
    5137           0 :                 DBG_ERR("Short read on [%s]: %zu/%zd\n",
    5138             :                         fsp_str_dbg(fsp), nread, plist_file_size);
    5139           0 :                 ok = false;
    5140           0 :                 goto out;
    5141             : 
    5142             :         }
    5143             : 
    5144           2 :         status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
    5145           2 :         if (!NT_STATUS_IS_OK(status)) {
    5146           0 :                 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
    5147           0 :                 ok = false;
    5148           0 :                 goto out;
    5149             :         }
    5150             : 
    5151           2 :         lines = file_lines_parse((char *)file_data,
    5152             :                                  plist_file_size,
    5153             :                                  &numlines,
    5154             :                                  talloc_tos());
    5155           2 :         if (lines == NULL) {
    5156           0 :                 ok = false;
    5157           0 :                 goto out;
    5158             :         }
    5159             : 
    5160           2 :         band_size_line = fruit_get_bandsize_line(lines, numlines);
    5161           2 :         if (band_size_line == NULL) {
    5162           0 :                 DBG_ERR("Didn't find band-size key in [%s]\n",
    5163             :                         smb_fname_str_dbg(smb_fname));
    5164           0 :                 ok = false;
    5165           0 :                 goto out;
    5166             :         }
    5167             : 
    5168           2 :         ok = fruit_get_bandsize_from_line(band_size_line, band_size);
    5169           2 :         if (!ok) {
    5170           0 :                 DBG_ERR("fruit_get_bandsize_from_line failed\n");
    5171           0 :                 goto out;
    5172             :         }
    5173             : 
    5174           2 :         DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
    5175             : 
    5176           2 : out:
    5177           2 :         if (fsp != NULL) {
    5178           0 :                 status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
    5179           0 :                 if (!NT_STATUS_IS_OK(status)) {
    5180           0 :                         DBG_ERR("close_file failed: %s\n", nt_errstr(status));
    5181             :                 }
    5182             :         }
    5183           2 :         TALLOC_FREE(plist);
    5184           2 :         TALLOC_FREE(smb_fname);
    5185           2 :         TALLOC_FREE(file_data);
    5186           2 :         TALLOC_FREE(lines);
    5187           2 :         return ok;
    5188             : }
    5189             : 
    5190             : struct fruit_disk_free_state {
    5191             :         off_t total_size;
    5192             : };
    5193             : 
    5194           2 : static bool fruit_get_num_bands(vfs_handle_struct *handle,
    5195             :                                 const char *bundle,
    5196             :                                 size_t *_nbands)
    5197             : {
    5198           2 :         char *path = NULL;
    5199           2 :         struct smb_filename *bands_dir = NULL;
    5200           2 :         struct smb_Dir *dir_hnd = NULL;
    5201           2 :         const char *dname = NULL;
    5202           2 :         char *talloced = NULL;
    5203             :         size_t nbands;
    5204             :         NTSTATUS status;
    5205             : 
    5206           2 :         path = talloc_asprintf(talloc_tos(),
    5207             :                                "%s/%s/bands",
    5208           2 :                                handle->conn->connectpath,
    5209             :                                bundle);
    5210           2 :         if (path == NULL) {
    5211           0 :                 return false;
    5212             :         }
    5213             : 
    5214           2 :         bands_dir = synthetic_smb_fname(talloc_tos(),
    5215             :                                         path,
    5216             :                                         NULL,
    5217             :                                         NULL,
    5218             :                                         0,
    5219             :                                         0);
    5220           2 :         TALLOC_FREE(path);
    5221           2 :         if (bands_dir == NULL) {
    5222           0 :                 return false;
    5223             :         }
    5224             : 
    5225           2 :         status = OpenDir(talloc_tos(),
    5226           2 :                          handle->conn,
    5227             :                          bands_dir,
    5228             :                          NULL,
    5229             :                          0,
    5230             :                          &dir_hnd);
    5231           2 :         if (!NT_STATUS_IS_OK(status)) {
    5232           0 :                 TALLOC_FREE(bands_dir);
    5233           0 :                 errno = map_errno_from_nt_status(status);
    5234           0 :                 return false;
    5235             :         }
    5236             : 
    5237           2 :         nbands = 0;
    5238             : 
    5239          10 :         while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
    5240           8 :                 if (ISDOT(dname) || ISDOTDOT(dname)) {
    5241           4 :                         continue;
    5242             :                 }
    5243           4 :                 nbands++;
    5244             :         }
    5245           2 :         TALLOC_FREE(dir_hnd);
    5246             : 
    5247           2 :         DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
    5248             : 
    5249           2 :         TALLOC_FREE(bands_dir);
    5250             : 
    5251           2 :         *_nbands = nbands;
    5252           2 :         return true;
    5253             : }
    5254             : 
    5255         190 : static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
    5256             :                                    struct fruit_disk_free_state *state,
    5257             :                                    const char *name)
    5258             : {
    5259             :         bool ok;
    5260         190 :         char *p = NULL;
    5261         190 :         size_t sparsebundle_strlen = strlen("sparsebundle");
    5262         190 :         size_t bandsize = 0;
    5263             :         size_t nbands;
    5264             :         off_t tm_size;
    5265             : 
    5266         190 :         p = strstr(name, "sparsebundle");
    5267         190 :         if (p == NULL) {
    5268         188 :                 return true;
    5269             :         }
    5270             : 
    5271           2 :         if (p[sparsebundle_strlen] != '\0') {
    5272           0 :                 return true;
    5273             :         }
    5274             : 
    5275           2 :         DBG_DEBUG("Processing sparsebundle [%s]\n", name);
    5276             : 
    5277           2 :         ok = fruit_get_bandsize(handle, name, &bandsize);
    5278           2 :         if (!ok) {
    5279             :                 /*
    5280             :                  * Beware of race conditions: this may be an uninitialized
    5281             :                  * Info.plist that a client is just creating. We don't want let
    5282             :                  * this to trigger complete failure.
    5283             :                  */
    5284           0 :                 DBG_ERR("Processing sparsebundle [%s] failed\n", name);
    5285           0 :                 return true;
    5286             :         }
    5287             : 
    5288           2 :         ok = fruit_get_num_bands(handle, name, &nbands);
    5289           2 :         if (!ok) {
    5290             :                 /*
    5291             :                  * Beware of race conditions: this may be a backup sparsebundle
    5292             :                  * in an early stage lacking a bands subdirectory. We don't want
    5293             :                  * let this to trigger complete failure.
    5294             :                  */
    5295           0 :                 DBG_ERR("Processing sparsebundle [%s] failed\n", name);
    5296           0 :                 return true;
    5297             :         }
    5298             : 
    5299             :         /*
    5300             :          * Arithmetic on 32-bit systems may cause overflow, depending on
    5301             :          * size_t precision. First we check its unlikely, then we
    5302             :          * force the precision into target off_t, then we check that
    5303             :          * the total did not overflow either.
    5304             :          */
    5305           2 :         if (bandsize > SIZE_MAX/nbands) {
    5306           0 :                 DBG_ERR("tmsize potential overflow: bandsize [%zu] nbands [%zu]\n",
    5307             :                         bandsize, nbands);
    5308           0 :                 return false;
    5309             :         }
    5310           2 :         tm_size = (off_t)bandsize * (off_t)nbands;
    5311             : 
    5312           2 :         if (state->total_size + tm_size < state->total_size) {
    5313           0 :                 DBG_ERR("tm total size overflow: bandsize [%zu] nbands [%zu]\n",
    5314             :                         bandsize, nbands);
    5315           0 :                 return false;
    5316             :         }
    5317             : 
    5318           2 :         state->total_size += tm_size;
    5319             : 
    5320           2 :         DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
    5321             :                   name, (intmax_t)tm_size, (intmax_t)state->total_size);
    5322             : 
    5323           2 :         return true;
    5324             : }
    5325             : 
    5326             : /**
    5327             :  * Calculate used size of a TimeMachine volume
    5328             :  *
    5329             :  * This assumes that the volume is used only for TimeMachine.
    5330             :  *
    5331             :  * - readdir(basedir of share), then
    5332             :  * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
    5333             :  * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
    5334             :  * - count band files in "\1.sparsebundle/bands/"
    5335             :  * - calculate used size of all bands: band_count * band_size
    5336             :  **/
    5337           2 : static uint64_t fruit_disk_free(vfs_handle_struct *handle,
    5338             :                                 const struct smb_filename *smb_fname,
    5339             :                                 uint64_t *_bsize,
    5340             :                                 uint64_t *_dfree,
    5341             :                                 uint64_t *_dsize)
    5342             : {
    5343           2 :         struct fruit_config_data *config = NULL;
    5344           2 :         struct fruit_disk_free_state state = {0};
    5345           2 :         struct smb_Dir *dir_hnd = NULL;
    5346           2 :         const char *dname = NULL;
    5347           2 :         char *talloced = NULL;
    5348             :         uint64_t dfree;
    5349             :         uint64_t dsize;
    5350             :         bool ok;
    5351             :         NTSTATUS status;
    5352             : 
    5353           2 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    5354             :                                 struct fruit_config_data,
    5355             :                                 return UINT64_MAX);
    5356             : 
    5357           2 :         if (!config->time_machine ||
    5358           2 :             config->time_machine_max_size == 0)
    5359             :         {
    5360           0 :                 return SMB_VFS_NEXT_DISK_FREE(handle,
    5361             :                                               smb_fname,
    5362             :                                               _bsize,
    5363             :                                               _dfree,
    5364             :                                               _dsize);
    5365             :         }
    5366             : 
    5367           2 :         status = OpenDir(talloc_tos(),
    5368           2 :                          handle->conn,
    5369             :                          smb_fname,
    5370             :                          NULL,
    5371             :                          0,
    5372             :                          &dir_hnd);
    5373           2 :         if (!NT_STATUS_IS_OK(status)) {
    5374           0 :                 errno = map_errno_from_nt_status(status);
    5375           0 :                 return UINT64_MAX;
    5376             :         }
    5377             : 
    5378         192 :         while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
    5379         190 :                 ok = fruit_tmsize_do_dirent(handle, &state, dname);
    5380         190 :                 if (!ok) {
    5381           0 :                         TALLOC_FREE(talloced);
    5382           0 :                         TALLOC_FREE(dir_hnd);
    5383           0 :                         return UINT64_MAX;
    5384             :                 }
    5385         190 :                 TALLOC_FREE(talloced);
    5386             :         }
    5387             : 
    5388           2 :         TALLOC_FREE(dir_hnd);
    5389             : 
    5390           2 :         dsize = config->time_machine_max_size / 512;
    5391           2 :         dfree = dsize - (state.total_size / 512);
    5392           2 :         if (dfree > dsize) {
    5393           0 :                 dfree = 0;
    5394             :         }
    5395             : 
    5396           2 :         *_bsize = 512;
    5397           2 :         *_dsize = dsize;
    5398           2 :         *_dfree = dfree;
    5399           2 :         return dfree / 2;
    5400             : }
    5401             : 
    5402        3829 : static uint64_t fruit_fs_file_id(struct vfs_handle_struct *handle,
    5403             :                                  const SMB_STRUCT_STAT *psbuf)
    5404             : {
    5405        3829 :         struct fruit_config_data *config = NULL;
    5406             : 
    5407        3829 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
    5408             :                                 struct fruit_config_data,
    5409             :                                 return 0);
    5410             : 
    5411        3829 :         if (global_fruit_config.nego_aapl &&
    5412         870 :             config->aapl_zero_file_id)
    5413             :         {
    5414         870 :                 return 0;
    5415             :         }
    5416             : 
    5417        2959 :         return SMB_VFS_NEXT_FS_FILE_ID(handle, psbuf);
    5418             : }
    5419             : 
    5420             : static struct vfs_fn_pointers vfs_fruit_fns = {
    5421             :         .connect_fn = fruit_connect,
    5422             :         .disk_free_fn = fruit_disk_free,
    5423             : 
    5424             :         /* File operations */
    5425             :         .fchmod_fn = fruit_fchmod,
    5426             :         .unlinkat_fn = fruit_unlinkat,
    5427             :         .renameat_fn = fruit_renameat,
    5428             :         .openat_fn = fruit_openat,
    5429             :         .close_fn = fruit_close,
    5430             :         .pread_fn = fruit_pread,
    5431             :         .pwrite_fn = fruit_pwrite,
    5432             :         .pread_send_fn = fruit_pread_send,
    5433             :         .pread_recv_fn = fruit_pread_recv,
    5434             :         .pwrite_send_fn = fruit_pwrite_send,
    5435             :         .pwrite_recv_fn = fruit_pwrite_recv,
    5436             :         .fsync_send_fn = fruit_fsync_send,
    5437             :         .fsync_recv_fn = fruit_fsync_recv,
    5438             :         .stat_fn = fruit_stat,
    5439             :         .lstat_fn = fruit_lstat,
    5440             :         .fstat_fn = fruit_fstat,
    5441             :         .fstreaminfo_fn = fruit_fstreaminfo,
    5442             :         .fntimes_fn = fruit_fntimes,
    5443             :         .ftruncate_fn = fruit_ftruncate,
    5444             :         .fallocate_fn = fruit_fallocate,
    5445             :         .create_file_fn = fruit_create_file,
    5446             :         .freaddir_attr_fn = fruit_freaddir_attr,
    5447             :         .offload_read_send_fn = fruit_offload_read_send,
    5448             :         .offload_read_recv_fn = fruit_offload_read_recv,
    5449             :         .offload_write_send_fn = fruit_offload_write_send,
    5450             :         .offload_write_recv_fn = fruit_offload_write_recv,
    5451             :         .fs_file_id_fn = fruit_fs_file_id,
    5452             : 
    5453             :         /* NT ACL operations */
    5454             :         .fget_nt_acl_fn = fruit_fget_nt_acl,
    5455             :         .fset_nt_acl_fn = fruit_fset_nt_acl,
    5456             : };
    5457             : 
    5458             : static_decl_vfs;
    5459         342 : NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
    5460             : {
    5461         342 :         NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
    5462             :                                         &vfs_fruit_fns);
    5463         342 :         if (!NT_STATUS_IS_OK(ret)) {
    5464           0 :                 return ret;
    5465             :         }
    5466             : 
    5467         342 :         vfs_fruit_debug_level = debug_add_class("fruit");
    5468         342 :         if (vfs_fruit_debug_level == -1) {
    5469           0 :                 vfs_fruit_debug_level = DBGC_VFS;
    5470           0 :                 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
    5471             :                           "vfs_fruit_init"));
    5472             :         } else {
    5473         342 :                 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
    5474             :                            "vfs_fruit_init","fruit",vfs_fruit_debug_level));
    5475             :         }
    5476             : 
    5477         342 :         return ret;
    5478             : }

Generated by: LCOV version 1.14