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

Generated by: LCOV version 1.13