add tcp-syncnt URL param to adjust TCP_SYNCNT sockopt
[deb_libnfs.git] / lib / libnfs.c
index d2f60dac7cbc68d716f23212a8487f90ea07ab17..cd15fb01eabe0bcc4fa722f33d322d6ad719ba38 100644 (file)
 
 #ifdef WIN32
 #include "win32_compat.h"
-#else
-#include <strings.h>
+#endif
+
+#ifdef HAVE_UTIME_H
 #include <utime.h>
-#endif /*WIN32*/
+#endif
 
 #ifdef ANDROID
 #define statvfs statfs
 #include <sys/statvfs.h>
 #endif
 
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
 #include <stdio.h>
 #include <stdarg.h>
 #include <stdlib.h>
@@ -59,7 +68,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <netinet/in.h>
 #include "libnfs-zdr.h"
 #include "libnfs.h"
 #include "libnfs-raw.h"
@@ -165,6 +173,148 @@ char *nfs_get_error(struct nfs_context *nfs)
        return rpc_get_error(nfs->rpc);
 };
 
+static struct nfs_url *nfs_parse_url(struct nfs_context *nfs, char *url, int dir, int incomplete)
+{
+       struct nfs_url *urls;
+       char *strp, *flagsp, *strp2;
+
+       if (strncmp(url, "nfs://", 6)) {
+               rpc_set_error(nfs->rpc, "Invalid URL specified");
+               return NULL;
+       }
+
+       urls = malloc(sizeof(struct nfs_url));
+       if (urls == NULL) {
+               rpc_set_error(nfs->rpc, "Out of memory");
+               return NULL;
+       }
+
+       urls->server = strdup(url + 6);
+       if (urls->server == NULL) {
+               nfs_destroy_url(urls);
+               rpc_set_error(nfs->rpc, "Out of memory");
+               return NULL;
+       }
+
+       if (urls->server[0] == '/' || urls->server[0] == '\0' ||
+               urls->server[0] == '?') {
+               if (incomplete) {
+                       flagsp = strchr(urls->server, '?');
+                       goto flags;
+               }
+               nfs_destroy_url(urls);
+               rpc_set_error(nfs->rpc, "Invalid server string");
+               return NULL;
+       }
+
+       strp = strchr(urls->server, '/');
+       if (strp == NULL) {
+               if (incomplete) {
+                       flagsp = strchr(urls->server, '?');
+                       goto flags;
+               }
+               nfs_destroy_url(urls);
+               rpc_set_error(nfs->rpc, "Incomplete or invalid URL specified.");
+               return NULL;
+       }
+
+       urls->path = strdup(strp);
+       if (urls->path == NULL) {
+               nfs_destroy_url(urls);
+               rpc_set_error(nfs->rpc, "Out of memory");
+               return NULL;
+       }
+       *strp = 0;
+
+       if (dir) {
+               flagsp = strchr(urls->path, '?');
+               goto flags;
+       }
+
+       strp = strrchr(urls->path, '/');
+       if (strp == NULL) {
+               if (incomplete) {
+                       flagsp = strchr(urls->path, '?');
+                       goto flags;
+               }
+               nfs_destroy_url(urls);
+               rpc_set_error(nfs->rpc, "Incomplete or invalid URL specified.");
+               return NULL;
+       }
+       urls->file = strdup(strp);
+       if (urls->path == NULL) {
+               nfs_destroy_url(urls);
+               rpc_set_error(nfs->rpc, "Out of memory");
+               return NULL;
+       }
+       *strp = 0;
+       flagsp = strchr(urls->file, '?');
+
+flags:
+       if (flagsp) {
+               *flagsp = 0;
+       }
+
+       if (urls->file && !strlen(urls->file)) {
+               free(urls->file);
+               urls->file = NULL;
+               if (!incomplete) {
+                       nfs_destroy_url(urls);
+                       rpc_set_error(nfs->rpc, "Incomplete or invalid URL specified.");
+                       return NULL;
+               }
+       }
+
+       if (urls->server && strlen(urls->server) <= 1) {
+               free(urls->server);
+               urls->server = NULL;
+       }
+
+       while (flagsp != NULL && *(flagsp+1) != 0) {
+               strp = flagsp + 1;
+               flagsp = strchr(strp, '&');
+               if (flagsp) {
+                       *flagsp = 0;
+               }
+               strp2 = strchr(strp, '=');
+               if (strp2) {
+                       *strp2 = 0;
+                       strp2++;
+                       if (!strncmp(strp, "tcp-syncnt", 10)) {
+                               rpc_set_tcp_syncnt(nfs->rpc, atoi(strp2));
+                       }
+               }
+       }
+
+       return urls;
+}
+
+struct nfs_url *nfs_parse_url_full(struct nfs_context *nfs, const char *url)
+{
+       return nfs_parse_url(nfs, url, 0, 0);
+}
+
+struct nfs_url *nfs_parse_url_dir(struct nfs_context *nfs, const char *url)
+{
+       return nfs_parse_url(nfs, url, 1, 0);
+}
+
+struct nfs_url *nfs_parse_url_incomplete(struct nfs_context *nfs, const char *url)
+{
+       return nfs_parse_url(nfs, url, 0, 1);
+}
+
+
+void nfs_destroy_url(struct nfs_url *url)
+{
+       if (url) {
+               free(url->server);
+               free(url->path);
+               free(url->file);
+       }
+       free(url);
+}
+
 struct nfs_context *nfs_init_context(void)
 {
        struct nfs_context *nfs;
@@ -211,6 +361,155 @@ void nfs_destroy_context(struct nfs_context *nfs)
        free(nfs);
 }
 
+struct rpc_cb_data {
+       char *server;
+       uint32_t program;
+       uint32_t version;
+
+       rpc_cb cb;
+       void *private_data;
+};       
+
+void free_rpc_cb_data(struct rpc_cb_data *data)
+{
+       free(data->server);
+       data->server = NULL;
+       free(data);
+}
+
+static void rpc_connect_program_4_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct rpc_cb_data *data = private_data;
+
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
+       /* Dont want any more callbacks even if the socket is closed */
+       rpc->connect_cb = NULL;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(rpc, status, command_data, data->private_data);
+               free_rpc_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(rpc, status, "Command was cancelled", data->private_data);
+               free_rpc_cb_data(data);
+               return;
+       }
+
+       data->cb(rpc, status, NULL, data->private_data);
+       free_rpc_cb_data(data);
+}
+
+static void rpc_connect_program_3_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct rpc_cb_data *data = private_data;
+       uint32_t rpc_port;
+
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
+       if (status == RPC_STATUS_ERROR) {       
+               data->cb(rpc, status, command_data, data->private_data);
+               free_rpc_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(rpc, status, "Command was cancelled", data->private_data);
+               free_rpc_cb_data(data);
+               return;
+       }
+
+       rpc_port = *(uint32_t *)command_data;
+       if (rpc_port == 0) {
+               rpc_set_error(rpc, "RPC error. Program is not available on %s", data->server);
+               data->cb(rpc, RPC_STATUS_ERROR, rpc_get_error(rpc), data->private_data);
+               free_rpc_cb_data(data);
+               return;
+       }
+
+       rpc_disconnect(rpc, "normal disconnect");
+       if (rpc_connect_async(rpc, data->server, rpc_port, rpc_connect_program_4_cb, data) != 0) {
+               data->cb(rpc, status, command_data, data->private_data);
+               free_rpc_cb_data(data);
+               return;
+       }
+}
+
+static void rpc_connect_program_2_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct rpc_cb_data *data = private_data;
+
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(rpc, status, command_data, data->private_data);
+               free_rpc_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(rpc, status, "Command was cancelled", data->private_data);
+               free_rpc_cb_data(data);
+               return;
+       }
+
+       if (rpc_pmap_getport_async(rpc, data->program, data->version, IPPROTO_TCP, rpc_connect_program_3_cb, private_data) != 0) {
+               data->cb(rpc, status, command_data, data->private_data);
+               free_rpc_cb_data(data);
+               return;
+       }
+}
+
+static void rpc_connect_program_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct rpc_cb_data *data = private_data;
+
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
+       /* Dont want any more callbacks even if the socket is closed */
+       rpc->connect_cb = NULL;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(rpc, status, command_data, data->private_data);
+               free_rpc_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(rpc, status, "Command was cancelled", data->private_data);
+               free_rpc_cb_data(data);
+               return;
+       }
+
+       if (rpc_pmap_null_async(rpc, rpc_connect_program_2_cb, data) != 0) {
+               data->cb(rpc, status, command_data, data->private_data);
+               free_rpc_cb_data(data);
+               return;
+       }
+}
+
+int rpc_connect_program_async(struct rpc_context *rpc, char *server, int program, int version, rpc_cb cb, void *private_data)
+{
+       struct rpc_cb_data *data;
+
+       data = malloc(sizeof(struct rpc_cb_data));
+       if (data == NULL) {
+               return -1;
+       }
+       memset(data, 0, sizeof(struct rpc_cb_data));
+       data->server       = strdup(server);
+       data->program      = program;
+       data->version      = version;
+
+       data->cb           = cb;
+       data->private_data = private_data;
+
+       if (rpc_connect_async(rpc, server, 111, rpc_connect_program_1_cb, data) != 0) {
+               rpc_set_error(rpc, "Failed to start connection");
+               free_rpc_cb_data(data);
+               return -1;
+       }
+       return 0;
+}
+
 void free_nfs_cb_data(struct nfs_cb_data *data)
 {
        if (data->saved_path != NULL) {
@@ -1020,7 +1319,7 @@ int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offse
 
        /* trying to read more than maximum server read size, we has to chop it up into smaller
         * reads and collect into a reassembly buffer.
-        * we send all reads in parallell so that performance is still good.
+        * we send all reads in parallel so that performance is still good.
         */
        data->max_offset = offset;
        data->start_offset = offset;
@@ -1200,7 +1499,7 @@ int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offs
 
        /* trying to write more than maximum server write size, we has to chop it up into smaller
         * chunks.
-        * we send all writes in parallell so that performance is still good.
+        * we send all writes in parallel so that performance is still good.
         */
        data->max_offset = offset;
        data->start_offset = offset;
@@ -1711,7 +2010,7 @@ static void nfs_creat_1_cb(struct rpc_context *rpc, int status, void *command_da
        if (res->status != NFS3_OK) {
                rpc_set_error(nfs->rpc, "NFS: CREATE of %s/%s failed with %s(%d)", data->saved_path, str, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
                data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
-
+               free_nfs_cb_data(data);
                return;
        }
 
@@ -2886,9 +3185,6 @@ int nfs_utime_async(struct nfs_context *nfs, const char *path, struct utimbuf *t
 }
 
 
-
-
-
 /*
  * Async access()
  */
@@ -3441,11 +3737,7 @@ uint64_t nfs_get_readmax(struct nfs_context *nfs)
  */
 uint64_t nfs_get_writemax(struct nfs_context *nfs)
 {
-       /* Some ZDR libraries can not marshall PDUs bigger than this */
-        if (nfs->writemax < 32768) {
-               return nfs->writemax;
-       }
-       return 32768;
+       return nfs->writemax;
 }
 
 void nfs_set_error(struct nfs_context *nfs, char *error_string, ...)