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

Generated by: LCOV version 1.14