LCOV - code coverage report
Current view: top level - source3/modules - vfs_fruit.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 1733 2247 77.1 %
Date: 2021-09-23 10:06:22 Functions: 116 125 92.8 %

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

Generated by: LCOV version 1.13