The branch, master has been updated
       via  408c965 s4:torture:vfs_fruit: copyfile
       via  6fd351f vfs:fruit: implement copyfile style copy_chunk
       via  de4304d smb2:ioctl: support for OS X AAPL copyfile style copy_chunk
       via  edf3b61 s3:util: use pread/pwrite in transfer_file
       via  8357dcb smbd/smb2_ioctl: fix error handling
       via  f0d6e4e vfs_fruit: simplify lp_parm_bool check
      from  e50bf6d replace: Replace BSD strtoll by wrapping strtoll instead of 
strtoq

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 408c965aab02484987a9446dcdae447cf6514164
Author: Ralph Boehme <[email protected]>
Date:   Wed Jun 10 15:30:04 2015 +0200

    s4:torture:vfs_fruit: copyfile
    
    Bug: https://bugzilla.samba.org/show_bug.cgi?id=11317
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Stefan Metzmacher <[email protected]>
    
    Autobuild-User(master): Ralph Böhme <[email protected]>
    Autobuild-Date(master): Tue Jun 23 14:37:05 CEST 2015 on sn-devel-104

commit 6fd351f23bcbb20019dd967cb40220d195ce726f
Author: Ralph Boehme <[email protected]>
Date:   Wed Apr 22 22:29:16 2015 +0200

    vfs:fruit: implement copyfile style copy_chunk
    
    Implement Apple's special copy_chunk ioctl that requests a copy of the
    whole file along with all attached metadata.
    
    These copy_chunk requests have a chunk count of 0 that we translate to a
    copy_chunk_send VFS call overloading the parameters src_off = dest_off =
    num = 0.
    
    Bug: https://bugzilla.samba.org/show_bug.cgi?id=11317
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Stefan Metzmacher <[email protected]>

commit de4304d22b7d134c0d75924e37f8289a74c7ed7d
Author: Ralph Boehme <[email protected]>
Date:   Wed Apr 22 22:29:16 2015 +0200

    smb2:ioctl: support for OS X AAPL copyfile style copy_chunk
    
    Apple's special copy_chunk ioctl that requests a copy of the whole file
    along with all attached metadata.
    
    These copy_chunk requests have a chunk count of 0 that we translate to a
    copy_chunk_send VFS call overloading the parameters src_off = dest_off =
    num = 0.
    
    Bug: https://bugzilla.samba.org/show_bug.cgi?id=11317
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Stefan Metzmacher <[email protected]>

commit edf3b6146531bb3f15442bbb0cdad7b03be9650b
Author: Ralph Boehme <[email protected]>
Date:   Mon Apr 27 12:16:16 2015 +0200

    s3:util: use pread/pwrite in transfer_file
    
    read/write aren't overloaded in the streams VFS modules, using
    pread/pwrite instead this makes it possible to use transfer_file() with
    named streams.
    
    Bug: https://bugzilla.samba.org/show_bug.cgi?id=11317
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Stefan Metzmacher <[email protected]>

commit 8357dcb4697fba30591968e60a7fdb51aa29a4bf
Author: Ralph Boehme <[email protected]>
Date:   Tue Jun 9 17:47:31 2015 +0200

    smbd/smb2_ioctl: fix error handling
    
    tevent_req_nterror must be called directly as the last step before
    returning with tevent_req_post.
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Stefan Metzmacher <[email protected]>

commit f0d6e4e3c3e9874f78439347ad7a68a3bbe9285a
Author: Ralph Boehme <[email protected]>
Date:   Mon Jun 15 18:31:23 2015 +0200

    vfs_fruit: simplify lp_parm_bool check
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Stefan Metzmacher <[email protected]>

-----------------------------------------------------------------------

Summary of changes:
 docs-xml/manpages/vfs_fruit.8.xml    |  12 +
 selftest/target/Samba3.pm            |   3 +
 selftest/target/Samba4.pm            |   3 +
 source3/include/transfer_file.h      |   4 +-
 source3/include/vfs.h                |   2 +
 source3/lib/util_transfer_file.c     |  23 +-
 source3/modules/vfs_fruit.c          | 253 ++++++++++++++++++--
 source3/smbd/smb2_ioctl_network_fs.c |  42 +++-
 source3/smbd/vfs.c                   |  10 +-
 source4/torture/vfs/fruit.c          | 450 ++++++++++++++++++++++++++++++++++-
 10 files changed, 760 insertions(+), 42 deletions(-)


Changeset truncated at 500 lines:

diff --git a/docs-xml/manpages/vfs_fruit.8.xml 
b/docs-xml/manpages/vfs_fruit.8.xml
index e407b54..9f77d9b 100644
--- a/docs-xml/manpages/vfs_fruit.8.xml
+++ b/docs-xml/manpages/vfs_fruit.8.xml
@@ -214,6 +214,18 @@
            </listitem>
          </varlistentry>
 
+         <varlistentry>
+           <term>fruit:copyfile = yes | no</term>
+           <listitem>
+             <para>Whether to enable OS X specific copychunk ioctl
+             that requests a copy of a whole file along with all
+             attached metadata.</para>
+             <para>WARNING: the copyfile request is blocking the
+             client while the server does the copy.</para>.
+             <para>The default is <emphasis>no</emphasis>.</para>
+           </listitem>
+         </varlistentry>
+
        </variablelist>
 </refsect1>
 
diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index f7f6632..a380e7a 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -1219,6 +1219,9 @@ sub provision($$$$$$$$)
        change share command = $bindir_abs/smbchangeshare
        delete share command = $bindir_abs/smbdeleteshare
 
+       # fruit:copyfile is a global option
+       fruit:copyfile = yes
+
        # Begin extra options
        $extra_options
        # End extra options
diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index 47ad206..32114c3 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -917,6 +917,9 @@ sub provision($$$$$$$$$$)
        lanman auth = yes
        allow nt4 crypto = yes
 
+       # fruit:copyfile is a global option
+       fruit:copyfile = yes
+
        $extra_smbconf_options
 
 [tmp]
diff --git a/source3/include/transfer_file.h b/source3/include/transfer_file.h
index 546104f..2f1bff4 100644
--- a/source3/include/transfer_file.h
+++ b/source3/include/transfer_file.h
@@ -24,8 +24,8 @@
 ssize_t transfer_file_internal(void *in_file,
                               void *out_file,
                               size_t n,
-                              ssize_t (*read_fn)(void *, void *, size_t),
-                              ssize_t (*write_fn)(void *, const void *, 
size_t));
+                              ssize_t (*pread_fn)(void *, void *, size_t, 
off_t),
+                              ssize_t (*pwrite_fn)(void *, const void *, 
size_t, off_t));
 
 off_t transfer_file(int infd, int outfd, off_t n);
 
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index aa1a880..081030c 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -165,6 +165,7 @@
 /* Bump to version 33 - Samba 4.3 will ship with that. */
 /* Version 33 - change fallocate mode flags param from enum->uint32_t */
 /* Version 33 - Add snapshot create/delete calls */
+/* Version 33 - Add OS X SMB2 AAPL copyfile extension flag to fsp */
 
 #define SMB_VFS_INTERFACE_VERSION 33
 
@@ -257,6 +258,7 @@ typedef struct files_struct {
        bool is_sparse;
        bool backup_intent; /* Handle was successfully opened with backup intent
                                and opener has privilege to do so. */
+       bool aapl_copyfile_supported;
        struct smb_filename *fsp_name;
        uint32_t name_hash;             /* Jenkins hash of full pathname. */
        uint64_t mid;                   /* Mid of the operation that created 
us. */
diff --git a/source3/lib/util_transfer_file.c b/source3/lib/util_transfer_file.c
index d415d7f..91f4f6f 100644
--- a/source3/lib/util_transfer_file.c
+++ b/source3/lib/util_transfer_file.c
@@ -36,8 +36,8 @@
 ssize_t transfer_file_internal(void *in_file,
                               void *out_file,
                               size_t n,
-                              ssize_t (*read_fn)(void *, void *, size_t),
-                              ssize_t (*write_fn)(void *, const void *, 
size_t))
+                              ssize_t (*pread_fn)(void *, void *, size_t, 
off_t),
+                              ssize_t (*pwrite_fn)(void *, const void *, 
size_t, off_t))
 {
        char *buf;
        size_t total = 0;
@@ -45,6 +45,7 @@ ssize_t transfer_file_internal(void *in_file,
        ssize_t write_ret;
        size_t num_to_read_thistime;
        size_t num_written = 0;
+       off_t offset = 0;
 
        if (n == 0) {
                return 0;
@@ -57,7 +58,7 @@ ssize_t transfer_file_internal(void *in_file,
        do {
                num_to_read_thistime = MIN((n - total), TRANSFER_BUF_SIZE);
 
-               read_ret = (*read_fn)(in_file, buf, num_to_read_thistime);
+               read_ret = (*pread_fn)(in_file, buf, num_to_read_thistime, 
offset);
                if (read_ret == -1) {
                        DEBUG(0,("transfer_file_internal: read failure. "
                                 "Error = %s\n", strerror(errno) ));
@@ -71,8 +72,9 @@ ssize_t transfer_file_internal(void *in_file,
                num_written = 0;
 
                while (num_written < read_ret) {
-                       write_ret = (*write_fn)(out_file, buf + num_written,
-                                               read_ret - num_written);
+                       write_ret = (*pwrite_fn)(out_file, buf + num_written,
+                                               read_ret - num_written,
+                                               offset + num_written);
 
                        if (write_ret == -1) {
                                DEBUG(0,("transfer_file_internal: "
@@ -89,28 +91,29 @@ ssize_t transfer_file_internal(void *in_file,
                }
 
                total += (size_t)read_ret;
+               offset += (off_t)read_ret;
        } while (total < n);
 
        SAFE_FREE(buf);
        return (ssize_t)total;
 }
 
-static ssize_t sys_read_fn(void *file, void *buf, size_t len)
+static ssize_t sys_pread_fn(void *file, void *buf, size_t len, off_t offset)
 {
        int *fd = (int *)file;
 
-       return sys_read(*fd, buf, len);
+       return sys_pread(*fd, buf, len, offset);
 }
 
-static ssize_t sys_write_fn(void *file, const void *buf, size_t len)
+static ssize_t sys_pwrite_fn(void *file, const void *buf, size_t len, off_t 
offset)
 {
        int *fd = (int *)file;
 
-       return sys_write(*fd, buf, len);
+       return sys_pwrite(*fd, buf, len, offset);
 }
 
 off_t transfer_file(int infd, int outfd, off_t n)
 {
        return (off_t)transfer_file_internal(&infd, &outfd, (size_t)n,
-                                                sys_read_fn, sys_write_fn);
+                                                sys_pread_fn, sys_pwrite_fn);
 }
diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 2547838..a4272f5 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -30,6 +30,7 @@
 #include "libcli/security/security.h"
 #include "../libcli/smb/smb2_create_ctx.h"
 #include "lib/sys_rw.h"
+#include "lib/util/tevent_ntstatus.h"
 
 /*
  * Enhanced OS X and Netatalk compatibility
@@ -124,8 +125,10 @@ struct fruit_config_data {
        enum fruit_locking locking;
        enum fruit_encoding encoding;
        bool use_aapl;
+       bool use_copyfile;
        bool readdir_attr_enabled;
        bool unix_info_enabled;
+       bool copyfile_enabled;
        bool veto_appledouble;
 
        /*
@@ -1335,33 +1338,27 @@ static int init_fruit_config(vfs_handle_struct *handle)
        }
        config->encoding = (enum fruit_encoding)enumval;
 
-       if (lp_parm_bool(SNUM(handle->conn),
-                        FRUIT_PARAM_TYPE_NAME, "veto_appledouble", true)) {
-               config->veto_appledouble = true;
-       }
+       config->veto_appledouble = lp_parm_bool(
+               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+               "veto_appledouble", true);
 
-       if (lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "aapl", true)) {
-               config->use_aapl = true;
-       }
+       config->use_aapl = lp_parm_bool(
+               -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
 
-       if (lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true)) {
-               config->unix_info_enabled = true;
-       }
+       config->unix_info_enabled = lp_parm_bool(
+               -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
 
-       if (lp_parm_bool(SNUM(handle->conn),
-                        "readdir_attr", "aapl_rsize", true)) {
-               config->readdir_attr_rsize = true;
-       }
+       config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
+                                          "copyfile", false);
 
-       if (lp_parm_bool(SNUM(handle->conn),
-                        "readdir_attr", "aapl_finder_info", true)) {
-               config->readdir_attr_finder_info = true;
-       }
+       config->readdir_attr_rsize = lp_parm_bool(
+               SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
 
-       if (lp_parm_bool(SNUM(handle->conn),
-                        "readdir_attr", "aapl_max_access", true)) {
-               config->readdir_attr_max_access = true;
-       }
+       config->readdir_attr_finder_info = lp_parm_bool(
+               SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
+
+       config->readdir_attr_max_access = lp_parm_bool(
+               SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
 
        SMB_VFS_HANDLE_SET_DATA(handle, config,
                                NULL, struct fruit_config_data,
@@ -1837,6 +1834,11 @@ static NTSTATUS check_aapl(vfs_handle_struct *handle,
                        config->readdir_attr_enabled = true;
                }
 
+               if (config->use_copyfile) {
+                       server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
+                       config->copyfile_enabled = true;
+               }
+
                /*
                 * The client doesn't set the flag, so we can't check
                 * for it and just set it unconditionally
@@ -3252,6 +3254,15 @@ static NTSTATUS fruit_create_file(vfs_handle_struct 
*handle,
                return status;
        }
 
+       if (config->copyfile_enabled) {
+               /*
+                * Set a flag in the fsp. Gets used in copychunk to
+                * check whether the special Apple copyfile semantics
+                * for copychunk should be allowed in a copychunk
+                * request with a count of 0.
+                */
+               (*result)->aapl_copyfile_supported = true;
+       }
        if (is_ntfs_stream_smb_fname(smb_fname)
            || (*result == NULL)
            || ((*result)->is_directory)) {
@@ -3462,6 +3473,202 @@ static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct 
*handle,
        return NT_STATUS_OK;
 }
 
+struct fruit_copy_chunk_state {
+       struct vfs_handle_struct *handle;
+       off_t copied;
+       struct files_struct *src_fsp;
+       struct files_struct *dst_fsp;
+       bool is_copyfile;
+};
+
+static void fruit_copy_chunk_done(struct tevent_req *subreq);
+static struct tevent_req *fruit_copy_chunk_send(struct vfs_handle_struct 
*handle,
+                                               TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct files_struct *src_fsp,
+                                               off_t src_off,
+                                               struct files_struct *dest_fsp,
+                                               off_t dest_off,
+                                               off_t num)
+{
+       struct tevent_req *req, *subreq;
+       struct fruit_copy_chunk_state *fruit_copy_chunk_state;
+       NTSTATUS status;
+       struct fruit_config_data *config;
+       off_t to_copy = num;
+
+       DEBUG(10,("soff: %zd, doff: %zd, len: %zd\n",
+                 src_off, dest_off, num));
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct fruit_config_data,
+                               return NULL);
+
+       req = tevent_req_create(mem_ctx, &fruit_copy_chunk_state,
+                               struct fruit_copy_chunk_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       fruit_copy_chunk_state->handle = handle;
+       fruit_copy_chunk_state->src_fsp = src_fsp;
+       fruit_copy_chunk_state->dst_fsp = dest_fsp;
+
+       /*
+        * Check if this a OS X copyfile style copychunk request with
+        * a requested chunk count of 0 that was translated to a
+        * copy_chunk_send VFS call overloading the parameters src_off
+        * = dest_off = num = 0.
+        */
+       if ((src_off == 0) && (dest_off == 0) && (num == 0) &&
+           src_fsp->aapl_copyfile_supported &&
+           dest_fsp->aapl_copyfile_supported)
+       {
+               status = vfs_stat_fsp(src_fsp);
+               if (tevent_req_nterror(req, status)) {
+                       return tevent_req_post(req, ev);
+               }
+
+               to_copy = src_fsp->fsp_name->st.st_ex_size;
+               fruit_copy_chunk_state->is_copyfile = true;
+       }
+
+       subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle,
+                                             mem_ctx,
+                                             ev,
+                                             src_fsp,
+                                             src_off,
+                                             dest_fsp,
+                                             dest_off,
+                                             to_copy);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       tevent_req_set_callback(subreq, fruit_copy_chunk_done, req);
+       return req;
+}
+
+static void fruit_copy_chunk_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct fruit_copy_chunk_state *state = tevent_req_data(
+               req, struct fruit_copy_chunk_state);
+       NTSTATUS status;
+       unsigned int num_streams = 0;
+       struct stream_struct *streams = NULL;
+       int i;
+       struct smb_filename *src_fname_tmp = NULL;
+       struct smb_filename *dst_fname_tmp = NULL;
+
+       status = SMB_VFS_NEXT_COPY_CHUNK_RECV(state->handle,
+                                             subreq,
+                                             &state->copied);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       if (!state->is_copyfile) {
+               tevent_req_done(req);
+               return;
+       }
+
+       /*
+        * Now copy all reamining streams. We know the share supports
+        * streams, because we're in vfs_fruit. We don't do this async
+        * because streams are few and small.
+        */
+       status = vfs_streaminfo(state->handle->conn, NULL,
+                               state->src_fsp->fsp_name->base_name,
+                               req, &num_streams, &streams);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       if (num_streams == 1) {
+               /* There is always one stream, ::$DATA. */
+               tevent_req_done(req);
+               return;
+       }
+
+       for (i = 0; i < num_streams; i++) {
+               DEBUG(10, ("%s: stream: '%s'/%zd\n",
+                         __func__, streams[i].name, streams[i].size));
+
+               src_fname_tmp = synthetic_smb_fname(
+                       req,
+                       state->src_fsp->fsp_name->base_name,
+                       streams[i].name,
+                       NULL);
+               if (tevent_req_nomem(src_fname_tmp, req)) {
+                       return;
+               }
+
+               if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
+                       TALLOC_FREE(src_fname_tmp);
+                       continue;
+               }
+
+               dst_fname_tmp = synthetic_smb_fname(
+                       req,
+                       state->dst_fsp->fsp_name->base_name,
+                       streams[i].name,
+                       NULL);
+               if (tevent_req_nomem(dst_fname_tmp, req)) {
+                       TALLOC_FREE(src_fname_tmp);
+                       return;
+               }
+
+               status = copy_file(req,
+                                  state->handle->conn,
+                                  src_fname_tmp,
+                                  dst_fname_tmp,
+                                  OPENX_FILE_CREATE_IF_NOT_EXIST,
+                                  0, false);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
+                                 smb_fname_str_dbg(src_fname_tmp),
+                                 smb_fname_str_dbg(dst_fname_tmp),
+                                 nt_errstr(status)));
+                       TALLOC_FREE(src_fname_tmp);
+                       TALLOC_FREE(dst_fname_tmp);
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+
+               TALLOC_FREE(src_fname_tmp);
+               TALLOC_FREE(dst_fname_tmp);
+       }
+
+       TALLOC_FREE(streams);
+       TALLOC_FREE(src_fname_tmp);
+       TALLOC_FREE(dst_fname_tmp);
+       tevent_req_done(req);
+}
+
+static NTSTATUS fruit_copy_chunk_recv(struct vfs_handle_struct *handle,
+                                     struct tevent_req *req,
+                                     off_t *copied)
+{
+       struct fruit_copy_chunk_state *fruit_copy_chunk_state = tevent_req_data(
+               req, struct fruit_copy_chunk_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               DEBUG(1, ("server side copy chunk failed: %s\n",
+                         nt_errstr(status)));
+               *copied = 0;
+               tevent_req_received(req);
+               return status;
+       }
+
+       *copied = fruit_copy_chunk_state->copied;
+       tevent_req_received(req);
+
+       return NT_STATUS_OK;
+}
+
 static struct vfs_fn_pointers vfs_fruit_fns = {
        .connect_fn = fruit_connect,
 
@@ -3483,6 +3690,8 @@ static struct vfs_fn_pointers vfs_fruit_fns = {
        .fallocate_fn = fruit_fallocate,
        .create_file_fn = fruit_create_file,
        .readdir_attr_fn = fruit_readdir_attr,
+       .copy_chunk_send_fn = fruit_copy_chunk_send,
+       .copy_chunk_recv_fn = fruit_copy_chunk_recv,
 
        /* NT ACL operations */
        .fget_nt_acl_fn = fruit_fget_nt_acl,
diff --git a/source3/smbd/smb2_ioctl_network_fs.c 
b/source3/smbd/smb2_ioctl_network_fs.c
index 5e70703..01798ac 100644
--- a/source3/smbd/smb2_ioctl_network_fs.c
+++ b/source3/smbd/smb2_ioctl_network_fs.c
@@ -91,6 +91,7 @@ struct fsctl_srv_copychunk_state {
                COPYCHUNK_OUT_LIMITS,
                COPYCHUNK_OUT_RSP,
        } out_data;
+       bool aapl_copyfile;
 };
 static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq);
 
@@ -225,15 +226,48 @@ static struct tevent_req 
*fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx,
        }
 
        state->status = copychunk_check_limits(&cc_copy);
-       if (tevent_req_nterror(req, state->status)) {
+       if (!NT_STATUS_IS_OK(state->status)) {
                DEBUG(3, ("copy chunk req exceeds limits\n"));
                state->out_data = COPYCHUNK_OUT_LIMITS;
+               tevent_req_nterror(req, state->status);
                return tevent_req_post(req, ev);


-- 
Samba Shared Repository

Reply via email to