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

Generated by: LCOV version 1.14