/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, see . */ /* * High level api to nfs filesystems */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef AROS #include "aros_compat.h" #endif #ifdef PS2_EE #include "ps2_compat.h" #endif #ifdef PS3_PPU #include "ps3_compat.h" #endif #ifdef WIN32 #include #endif #ifdef HAVE_UTIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_VFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif #if defined(__ANDROID__) && !defined(HAVE_SYS_STATVFS_H) #define statvfs statfs #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_STRINGS_H #include #endif #ifdef HAVE_SYS_SYSMACROS_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include #include #include #include #include #include "slist.h" #include "libnfs.h" #include "libnfs-raw.h" #include "libnfs-raw-mount.h" #include "libnfs-raw-portmap.h" #include "libnfs-private.h" void nfs_free_nfsdir(struct nfsdir *nfsdir) { while (nfsdir->entries) { struct nfsdirent *dirent = nfsdir->entries->next; if (nfsdir->entries->name != NULL) { free(nfsdir->entries->name); } free(nfsdir->entries); nfsdir->entries = dirent; } free(nfsdir->fh.val); free(nfsdir); } void nfs_dircache_add(struct nfs_context *nfs, struct nfsdir *nfsdir) { int i = 0; #ifdef HAVE_MULTITHREADING nfs_mt_mutex_lock(&nfs->rpc->rpc_mutex); #endif LIBNFS_LIST_ADD(&nfs->nfsi->dircache, nfsdir); for (nfsdir = nfs->nfsi->dircache; nfsdir; nfsdir = nfsdir->next, i++) { if (i > MAX_DIR_CACHE) { LIBNFS_LIST_REMOVE(&nfs->nfsi->dircache, nfsdir); nfs_free_nfsdir(nfsdir); break; } } #ifdef HAVE_MULTITHREADING nfs_mt_mutex_unlock(&nfs->rpc->rpc_mutex); #endif } struct nfsdir * nfs_dircache_find(struct nfs_context *nfs, struct nfs_fh *fh) { struct nfsdir *nfsdir; #ifdef HAVE_MULTITHREADING nfs_mt_mutex_lock(&nfs->rpc->rpc_mutex); #endif for (nfsdir = nfs->nfsi->dircache; nfsdir; nfsdir = nfsdir->next) { if (nfsdir->fh.len == fh->len && !memcmp(nfsdir->fh.val, fh->val, fh->len)) { LIBNFS_LIST_REMOVE(&nfs->nfsi->dircache, nfsdir); break; } } #ifdef HAVE_MULTITHREADING nfs_mt_mutex_unlock(&nfs->rpc->rpc_mutex); #endif return nfsdir; } void nfs_dircache_drop(struct nfs_context *nfs, struct nfs_fh *fh) { struct nfsdir *cached; cached = nfs_dircache_find(nfs, fh); if (cached) { nfs_free_nfsdir(cached); } } static uint32_t nfs_pagecache_hash(struct nfs_pagecache *pagecache, uint64_t offset) { return (2654435761UL * (1 + ((uint32_t)(offset) / NFS_BLKSIZE))) & (pagecache->num_entries - 1); } void nfs_pagecache_invalidate(struct nfs_context *nfs, struct nfsfh *nfsfh) { if (nfsfh->pagecache.entries) { RPC_LOG(nfs->rpc, 2, "invalidating pagecache"); memset(nfsfh->pagecache.entries, 0x00, sizeof(struct nfs_pagecache_entry) * nfsfh->pagecache.num_entries); } } void nfs_pagecache_put(struct nfs_pagecache *pagecache, uint64_t offset, const char *buf, size_t len) { time_t ts = pagecache->ttl ? (time_t)(rpc_current_time() / 1000) : 1; if (!pagecache->num_entries) return; while (len > 0) { uint64_t page_offset = offset & ~(NFS_BLKSIZE - 1); uint32_t entry = nfs_pagecache_hash(pagecache, page_offset); struct nfs_pagecache_entry *e = &pagecache->entries[entry]; size_t n = MIN(NFS_BLKSIZE - offset % NFS_BLKSIZE, len); /* we can only write to the cache if we add a full page or * partially update a page that is still valid */ if (n == NFS_BLKSIZE || (e->ts && e->offset == page_offset && (!pagecache->ttl || ts - e->ts <= pagecache->ttl))) { e->ts = ts; e->offset = page_offset; memcpy(e->buf + offset % NFS_BLKSIZE, buf, n); } buf += n; offset += n; len -= n; } } char * nfs_pagecache_get(struct nfs_pagecache *pagecache, uint64_t offset) { uint32_t entry; struct nfs_pagecache_entry *e; entry = nfs_pagecache_hash(pagecache, offset); e = &pagecache->entries[entry]; if (offset != e->offset) { return NULL; } if (!e->ts) { return NULL; } if (pagecache->ttl && (time_t)(rpc_current_time() / 1000) - e->ts > pagecache->ttl) { return NULL; } return e->buf; } void nfs_pagecache_init(struct nfs_context *nfs, struct nfsfh *nfsfh) { /* init page cache */ if (nfs->rpc->pagecache) { nfsfh->pagecache.num_entries = nfs->rpc->pagecache; nfsfh->pagecache.ttl = nfs->rpc->pagecache_ttl; nfsfh->pagecache.entries = malloc(sizeof(struct nfs_pagecache_entry) * nfsfh->pagecache.num_entries); nfs_pagecache_invalidate(nfs, nfsfh); RPC_LOG(nfs->rpc, 2, "init pagecache entries %d pagesize %d\n", nfsfh->pagecache.num_entries, NFS_BLKSIZE); } } void nfs_set_auth(struct nfs_context *nfs, struct AUTH *auth) { rpc_set_auth(nfs->rpc, auth); } int nfs_get_fd(struct nfs_context *nfs) { return rpc_get_fd(nfs->rpc); } int nfs_queue_length(struct nfs_context *nfs) { return rpc_queue_length(nfs->rpc); } int nfs_which_events(struct nfs_context *nfs) { return rpc_which_events(nfs->rpc); } int nfs_service(struct nfs_context *nfs, int revents) { return rpc_service(nfs->rpc, revents); } char * nfs_get_error(struct nfs_context *nfs) { #ifdef HAVE_MULTITHREADING if (nfs && nfs->nfsi->multithreading_enabled) { struct nfs_thread_context *ntc; for(ntc = nfs->nfsi->thread_ctx; ntc; ntc = ntc->next) { if (nfs_mt_get_tid() == ntc->tid) { nfs = &ntc->nfs; break; } } } #endif return nfs->error_string ? nfs->error_string : ""; }; #ifdef HAVE_SO_BINDTODEVICE void nfs_set_interface(struct nfs_context *nfs, const char *ifname) { rpc_set_interface(nfs_get_rpc_context(nfs), ifname); } #endif static int nfs_set_context_args(struct nfs_context *nfs, const char *arg, const char *val) { if (!strcmp(arg, "tcp-syncnt")) { rpc_set_tcp_syncnt(nfs_get_rpc_context(nfs), atoi(val)); } else if (!strcmp(arg, "uid")) { rpc_set_uid(nfs_get_rpc_context(nfs), atoi(val)); } else if (!strcmp(arg, "gid")) { rpc_set_gid(nfs_get_rpc_context(nfs), atoi(val)); } else if (!strcmp(arg, "readahead")) { rpc_set_readahead(nfs_get_rpc_context(nfs), atoi(val)); } else if (!strcmp(arg, "pagecache")) { rpc_set_pagecache(nfs_get_rpc_context(nfs), atoi(val)); } else if (!strcmp(arg, "debug")) { rpc_set_debug(nfs_get_rpc_context(nfs), atoi(val)); } else if (!strcmp(arg, "auto-traverse-mounts")) { nfs->nfsi->auto_traverse_mounts = atoi(val); } else if (!strcmp(arg, "dircache")) { nfs_set_dircache(nfs, atoi(val)); } else if (!strcmp(arg, "autoreconnect")) { nfs_set_autoreconnect(nfs, atoi(val)); #ifdef HAVE_SO_BINDTODEVICE } else if (!strcmp(arg, "if")) { nfs_set_interface(nfs, val); #endif } else if (!strcmp(arg, "version")) { if (nfs_set_version(nfs, atoi(val)) < 0) { nfs_set_error(nfs, "NFS version %d is not supported", atoi(val)); return -1; } } else if (!strcmp(arg, "nfsport")) { nfs->nfsi->nfsport = atoi(val); } else if (!strcmp(arg, "mountport")) { nfs->nfsi->mountport = atoi(val); } return 0; } static int tohex(char ch) { if (ch >= '0' && ch <= '9') { return ch - '0'; } ch &= 0xDF; if (ch >= 'A' && ch <= 'F') { return ch - 'A' + 10; } return -1; } static struct nfs_url * nfs_parse_url(struct nfs_context *nfs, const char *url, int dir, int incomplete) { struct nfs_url *urls; char *strp, *flagsp, *strp2, ch; int tmp; if (strncmp(url, "nfs://", 6)) { nfs_set_error(nfs, "Invalid URL specified"); return NULL; } urls = malloc(sizeof(struct nfs_url)); if (urls == NULL) { nfs_set_error(nfs, "Out of memory"); return NULL; } memset(urls, 0x00, sizeof(struct nfs_url)); urls->server = strdup(url + 6); if (urls->server == NULL) { nfs_destroy_url(urls); nfs_set_error(nfs, "Out of memory"); return NULL; } /* unescape all % hex hex characters */ strp = urls->server; while (strp && *strp) { strp = strchr(strp, '%'); if (strp == NULL) { break; } tmp = tohex(strp[1]); if (tmp < 0) { strp++; continue; } ch = (tmp & 0x0f) << 4; tmp = tohex(strp[2]); if (tmp < 0) { strp++; continue; } ch |= tmp & 0x0f; *strp = ch; strcpy(strp + 1, strp + 3); strp++; } if (urls->server[0] == '/' || urls->server[0] == '\0' || urls->server[0] == '?') { if (incomplete) { flagsp = strchr(urls->server, '?'); goto flags; } nfs_destroy_url(urls); nfs_set_error(nfs, "Invalid server string"); return NULL; } strp = strchr(urls->server, '/'); if (strp == NULL) { if (incomplete) { flagsp = strchr(urls->server, '?'); goto flags; } nfs_destroy_url(urls); nfs_set_error(nfs, "Incomplete or invalid URL specified."); return NULL; } urls->path = strdup(strp); if (urls->path == NULL) { nfs_destroy_url(urls); nfs_set_error(nfs, "Out of memory"); return NULL; } *strp = 0; strp = strchr(urls->server, ':'); if (strp) { *strp++ = 0; nfs->nfsi->nfsport = atoi(strp); } 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); nfs_set_error(nfs, "Incomplete or invalid URL specified."); return NULL; } urls->file = strdup(strp); if (urls->path == NULL) { nfs_destroy_url(urls); nfs_set_error(nfs, "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); nfs_set_error(nfs, "Incomplete or invalid URL " "specified."); return 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++; nfs_set_context_args(nfs, strp, strp2); } } if (urls->server && strlen(urls->server) <= 1) { free(urls->server); urls->server = NULL; } 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); } #define MAX_CLIENT_NAME 64 struct nfs_context * nfs_init_context(void) { struct nfs_context *nfs; struct nfs_context_internal *nfsi; int i; uint64_t v; verifier4 verifier; char client_name[MAX_CLIENT_NAME]; nfsi = malloc(sizeof(struct nfs_context_internal)); if (nfsi == NULL) { return NULL; } memset(nfsi, 0, sizeof(struct nfs_context_internal)); nfs = malloc(sizeof(struct nfs_context)); if (nfs == NULL) { free(nfsi); return NULL; } memset(nfs, 0, sizeof(struct nfs_context)); nfs->nfsi = nfsi; nfs->rpc = rpc_init_context(); if (nfs->rpc == NULL) { free(nfs); return NULL; } nfs->nfsi->cwd = strdup("/"); nfs->nfsi->mask = 022; nfs->nfsi->auto_traverse_mounts = 1; nfs->nfsi->dircache_enabled = 1; /* Default is never give up, never surrender */ nfs->nfsi->auto_reconnect = -1; nfs->nfsi->version = NFS_V3; /* NFSv4 parameters */ /* We need a "random" initial verifier */ v = rpc_current_time() << 32 | getpid(); for (i = 0; i < NFS4_VERIFIER_SIZE; i++) { verifier[i] = v & 0xff; v >>= 8; } nfs4_set_verifier(nfs, verifier); snprintf(client_name, MAX_CLIENT_NAME, "Libnfs pid:%d %d", getpid(), (int)time(NULL)); nfs4_set_client_name(nfs, client_name); #ifdef HAVE_MULTITHREADING nfs_mt_mutex_init(&nfs->nfsi->nfs_mutex); nfs_mt_mutex_init(&nfs->nfsi->nfs4_open_mutex); #endif /* HAVE_MULTITHREADING */ return nfs; } void nfs4_set_client_name(struct nfs_context *nfs, const char *client_name) { nfs->nfsi->client_name = strdup(client_name); } void nfs4_set_verifier(struct nfs_context *nfs, const char *verifier) { memcpy(nfs->nfsi->verifier, verifier, NFS4_VERIFIER_SIZE); } void nfs_destroy_context(struct nfs_context *nfs) { while (nfs->nfsi->nested_mounts) { struct nested_mounts *mnt = nfs->nfsi->nested_mounts; LIBNFS_LIST_REMOVE(&nfs->nfsi->nested_mounts, mnt); free(mnt->path); free(mnt->fh.val); free(mnt); } rpc_destroy_context(nfs->rpc); nfs->rpc = NULL; free(nfs->error_string); nfs->error_string = NULL; free(nfs->nfsi->server); free(nfs->nfsi->export); free(nfs->nfsi->cwd); free(nfs->nfsi->rootfh.val); free(nfs->nfsi->client_name); while (nfs->nfsi->dircache) { struct nfsdir *nfsdir = nfs->nfsi->dircache; LIBNFS_LIST_REMOVE(&nfs->nfsi->dircache, nfsdir); nfs_free_nfsdir(nfsdir); } #ifdef HAVE_MULTITHREADING nfs_mt_mutex_destroy(&nfs->nfsi->nfs4_open_mutex); nfs_mt_mutex_destroy(&nfs->nfsi->nfs_mutex); while (nfs->nfsi->thread_ctx) { struct nfs_thread_context *tmp = nfs->nfsi->thread_ctx->next; free(nfs->nfsi->thread_ctx->nfs.error_string); free(nfs->nfsi->thread_ctx); nfs->nfsi->thread_ctx = tmp; } #endif /* HAVE_MULTITHREADING */ free(nfs->nfsi); 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 int rpc_connect_port_internal(struct rpc_context *rpc, int port, struct rpc_cb_data *data); static void rpc_connect_program_5_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_SUCCESS) { data->cb(rpc, status, command_data, 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_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_SUCCESS) { data->cb(rpc, status, command_data, data->private_data); free_rpc_cb_data(data); return; } if (rpc_null_async(rpc, data->program, data->version, rpc_connect_program_5_cb, data) != 0) { data->cb(rpc, RPC_STATUS_ERROR, command_data, data->private_data); free_rpc_cb_data(data); return; } } 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; struct pmap3_string_result *gar; uint32_t rpc_port = 0; char *ptr; assert(rpc->magic == RPC_CONTEXT_MAGIC); if (status != RPC_STATUS_SUCCESS) { data->cb(rpc, status, command_data, data->private_data); free_rpc_cb_data(data); return; } switch (rpc->s.ss_family) { case AF_INET: rpc_port = *(uint32_t *)(void *)command_data; break; case AF_INET6: /* ouch. portmapper and ipv6 are not great */ gar = command_data; if (gar->addr == NULL) { break; } ptr = strrchr(gar->addr, '.'); if (ptr == NULL) { break; } rpc_port = atoi(ptr + 1); *ptr = 0; ptr = strrchr(gar->addr, '.'); if (ptr == NULL) { break; } rpc_port += 256 * atoi(ptr + 1); break; } 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_port_internal(rpc, rpc_port, data)) { data->cb(rpc, RPC_STATUS_ERROR, 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; struct pmap3_mapping map; assert(rpc->magic == RPC_CONTEXT_MAGIC); if (status != RPC_STATUS_SUCCESS) { data->cb(rpc, status, command_data, data->private_data); free_rpc_cb_data(data); return; } switch (rpc->s.ss_family) { case AF_INET: if (rpc_pmap2_getport_async(rpc, data->program, data->version, IPPROTO_TCP, rpc_connect_program_3_cb, private_data) != 0) { data->cb(rpc, RPC_STATUS_ERROR, command_data, data->private_data); free_rpc_cb_data(data); return; } break; case AF_INET6: map.prog=data->program; map.vers=data->version; map.netid=""; map.addr=""; map.owner=""; if (rpc_pmap3_getaddr_async(rpc, &map, rpc_connect_program_3_cb, private_data) != 0) { data->cb(rpc, RPC_STATUS_ERROR, command_data, data->private_data); free_rpc_cb_data(data); return; } break; } } 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_SUCCESS) { data->cb(rpc, status, command_data, data->private_data); free_rpc_cb_data(data); return; } switch (rpc->s.ss_family) { case AF_INET: if (rpc_pmap2_null_async(rpc, rpc_connect_program_2_cb, data) != 0) { data->cb(rpc, RPC_STATUS_ERROR, command_data, data->private_data); free_rpc_cb_data(data); return; } break; case AF_INET6: if (rpc_pmap3_null_async(rpc, rpc_connect_program_2_cb, data) != 0) { data->cb(rpc, RPC_STATUS_ERROR, command_data, data->private_data); free_rpc_cb_data(data); return; } break; } } static int rpc_connect_port_internal(struct rpc_context *rpc, int port, struct rpc_cb_data *data) { if (rpc_connect_async(rpc, data->server, port, rpc_connect_program_4_cb, data) != 0) { return -1; } return 0; } int rpc_connect_port_async(struct rpc_context *rpc, const char *server, int port, 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_port_internal(rpc, port, data)) { rpc_set_error(rpc, "Failed to start connection. %s", rpc_get_error(rpc)); free_rpc_cb_data(data); return -1; } return 0; } int rpc_connect_program_async(struct rpc_context *rpc, const 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. %s", rpc_get_error(rpc)); free_rpc_cb_data(data); return -1; } return 0; } void free_nfs_cb_data(struct nfs_cb_data *data) { if (data->continue_data != NULL) { assert(data->free_continue_data); data->free_continue_data(data->continue_data); } free(data->saved_path); free(data->fh.val); if (!data->not_my_buffer) { free(data->buffer); } free(data); } void nfs_free_nfsfh(struct nfsfh *nfsfh) { if (nfsfh->fh.val != NULL) { free(nfsfh->fh.val); nfsfh->fh.len = 0; nfsfh->fh.val = NULL; } free(nfsfh->pagecache.entries); free(nfsfh); } /* * Async call for mounting an nfs share and geting the root filehandle */ int nfs_mount_async(struct nfs_context *nfs, const char *server, const char *export, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_mount_async(nfs, server, export, cb, private_data); case NFS_V4: return nfs4_mount_async(nfs, server, export, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } /* * Async call for umounting an nfs share */ int nfs_umount_async(struct nfs_context *nfs, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_umount_async(nfs, cb, private_data); case NFS_V4: /* umount is a no-op in v4 */ (*cb)(0, nfs, NULL, private_data); return 0; default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_normalize_path(struct nfs_context *nfs, char *path) { char *str; size_t len; /* // -> / */ while ((str = strstr(path, "//"))) { while(*str) { *str = *(str + 1); str++; } } /* /./ -> / */ while ((str = strstr(path, "/./"))) { while(*(str + 1)) { *str = *(str + 2); str++; } } /* ^/../ -> error */ if (!strncmp(path, "/../", 4)) { nfs_set_error(nfs, "Absolute path starts with '/../' " "during normalization"); return -1; } /* ^[^/] -> error */ if (path[0] != '/') { nfs_set_error(nfs, "Absolute path does not start with '/'"); return -1; } /* /string/../ -> / */ while ((str = strstr(path, "/../"))) { char *tmp; if (!strncmp(path, "/../", 4)) { nfs_set_error(nfs, "Absolute path starts with '/../' " "during normalization"); return -1; } tmp = str - 1; while (*tmp != '/') { tmp--; } str += 3; while((*(tmp++) = *(str++)) != '\0') ; } /* /$ -> \0 */ len = strlen(path); if (len > 1) { if (path[len - 1] == '/') { path[len - 1] = '\0'; len--; } } if (path[0] == '\0') { nfs_set_error(nfs, "Absolute path became '' " "during normalization"); return -1; } /* /.$ -> \0 */ if (len >= 2) { if (!strcmp(&path[len - 2], "/.")) { path[len - 2] = '\0'; len -= 2; } } /* ^/..$ -> error */ if (!strcmp(path, "/..")) { nfs_set_error(nfs, "Absolute path is '/..' " "during normalization"); return -1; } /* /string/..$ -> / */ if (len >= 3) { if (!strcmp(&path[len - 3], "/..")) { char *tmp = &path[len - 3]; while (*--tmp != '/') ; *tmp = '\0'; } } return 0; } int nfs_stat_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_stat_async(nfs, path, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv4", __FUNCTION__); return -1; } } int nfs_stat64_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_stat64_async(nfs, path, 0, cb, private_data); case NFS_V4: return nfs4_stat64_async(nfs, path, 0, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_lstat64_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_stat64_async(nfs, path, 1, cb, private_data); case NFS_V4: return nfs4_stat64_async(nfs, path, 1, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_open2_async(struct nfs_context *nfs, const char *path, int flags, int mode, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_open_async(nfs, path, flags, mode, cb, private_data); case NFS_V4: return nfs4_open_async(nfs, path, flags, mode, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_open_async(struct nfs_context *nfs, const char *path, int flags, nfs_cb cb, void *private_data) { return nfs_open2_async(nfs, path, flags, 0666 & ~nfs->nfsi->mask, cb, private_data); } int nfs_chdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_chdir_async(nfs, path, cb, private_data); case NFS_V4: return nfs4_chdir_async(nfs, path, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_pread_async_internal(nfs, nfsfh, offset, (size_t)count, cb, private_data, 0); case NFS_V4: return nfs4_pread_async_internal(nfs, nfsfh, offset, (size_t)count, cb, private_data, 0); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_read_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_pread_async_internal(nfs, nfsfh, nfsfh->offset, (size_t)count, cb, private_data, 1); case NFS_V4: return nfs4_pread_async_internal(nfs, nfsfh, nfsfh->offset, (size_t)count, cb, private_data, 1); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, const void *buf, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_pwrite_async_internal(nfs, nfsfh, offset, (size_t)count, buf, cb, private_data, 0); case NFS_V4: return nfs4_pwrite_async_internal(nfs, nfsfh, offset, (size_t)count, buf, cb, private_data, 0); default: nfs_set_error(nfs, "%s does not support NFSv%d.", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, const void *buf, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_write_async(nfs, nfsfh, count, buf, cb, private_data); case NFS_V4: return nfs4_write_async(nfs, nfsfh, count, buf, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_close_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_close_async(nfs, nfsfh, cb, private_data); case NFS_V4: return nfs4_close_async(nfs, nfsfh, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_fstat_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_fstat_async(nfs, nfsfh, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv4", __FUNCTION__); return -1; } } int nfs_fstat64_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_fstat64_async(nfs, nfsfh, cb, private_data); case NFS_V4: return nfs4_fstat64_async(nfs, nfsfh, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_fsync_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_fsync_async(nfs, nfsfh, cb, private_data); case NFS_V4: return nfs4_fsync_async(nfs, nfsfh, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_ftruncate_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t length, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_ftruncate_async(nfs, nfsfh, length, cb, private_data); case NFS_V4: return nfs4_ftruncate_async(nfs, nfsfh, length, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_truncate_async(struct nfs_context *nfs, const char *path, uint64_t length, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_truncate_async(nfs, path, length, cb, private_data); case NFS_V4: return nfs4_truncate_async(nfs, path, length, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_mkdir2_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_mkdir2_async(nfs, path, mode, cb, private_data); case NFS_V4: return nfs4_mkdir2_async(nfs, path, mode, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_mkdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { return nfs_mkdir2_async(nfs, path, 0755, cb, private_data); } int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_rmdir_async(nfs, path, cb, private_data); case NFS_V4: return nfs4_rmdir_async(nfs, path, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_create_async(struct nfs_context *nfs, const char *path, int flags, int mode, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_create_async(nfs, path, flags, mode, cb, private_data); case NFS_V4: return nfs4_create_async(nfs, path, flags, mode, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data) { return nfs_create_async(nfs, path, 0, mode, cb, private_data); } int nfs_unlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_unlink_async(nfs, path, cb, private_data); case NFS_V4: return nfs4_unlink_async(nfs, path, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_mknod_async(struct nfs_context *nfs, const char *path, int mode, int dev, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_mknod_async(nfs, path, mode, dev, cb, private_data); case NFS_V4: return nfs4_mknod_async(nfs, path, mode, dev, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_opendir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_opendir_async(nfs, path, cb, private_data); case NFS_V4: return nfs4_opendir_async(nfs, path, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv4", __FUNCTION__); return -1; } } struct nfsdirent * nfs_readdir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir) { struct nfsdirent *nfsdirent = nfsdir->current; if (nfsdir->current != NULL) { nfsdir->current = nfsdir->current->next; } return nfsdirent; } long nfs_telldir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir) { long i; struct nfsdirent *tmp; for (i = 0, tmp = nfsdir->entries; tmp; i++, tmp = tmp->next) { if (tmp == nfsdir->current) { return i; } } return -1; } void nfs_seekdir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir, long loc) { if (loc < 0) { return; } for (nfsdir->current = nfsdir->entries; nfsdir->current && loc--; nfsdir->current = nfsdir->current->next) { } } void nfs_rewinddir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir) { nfsdir->current = nfsdir->entries; } void nfs_closedir(struct nfs_context *nfs, struct nfsdir *nfsdir) { if (nfs && nfs->nfsi->dircache_enabled) { nfs_dircache_add(nfs, nfsdir); } else { nfs_free_nfsdir(nfsdir); } } void nfs_getcwd(struct nfs_context *nfs, const char **cwd) { if (cwd) { *cwd = nfs->nfsi->cwd; } } int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int64_t offset, int whence, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_lseek_async(nfs, nfsfh, offset, whence, cb, private_data); case NFS_V4: return nfs4_lseek_async(nfs, nfsfh, offset, whence, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_lockf_async(struct nfs_context *nfs, struct nfsfh *nfsfh, enum nfs4_lock_op op, uint64_t count, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V4: return nfs4_lockf_async(nfs, nfsfh, op, count, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_fcntl_async(struct nfs_context *nfs, struct nfsfh *nfsfh, enum nfs4_fcntl_op cmd, void *arg, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V4: return nfs4_fcntl_async(nfs, nfsfh, cmd, arg, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_statvfs_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_statvfs_async(nfs, path, cb, private_data); case NFS_V4: return nfs4_statvfs_async(nfs, path, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_statvfs64_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_statvfs64_async(nfs, path, cb, private_data); case NFS_V4: return nfs4_statvfs64_async(nfs, path, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_readlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_readlink_async(nfs, path, cb, private_data); case NFS_V4: return nfs4_readlink_async(nfs, path, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_chmod_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_chmod_async_internal(nfs, path, 0, mode, cb, private_data); case NFS_V4: return nfs4_chmod_async_internal(nfs, path, 0, mode, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_lchmod_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_chmod_async_internal(nfs, path, 1, mode, cb, private_data); case NFS_V4: return nfs4_chmod_async_internal(nfs, path, 1, mode, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_fchmod_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_fchmod_async(nfs, nfsfh, mode, cb, private_data); case NFS_V4: return nfs4_fchmod_async(nfs, nfsfh, mode, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_chown_async(struct nfs_context *nfs, const char *path, int uid, int gid, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_chown_async_internal(nfs, path, 0, uid, gid, cb, private_data); case NFS_V4: return nfs4_chown_async_internal(nfs, path, 0, uid, gid, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_lchown_async(struct nfs_context *nfs, const char *path, int uid, int gid, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_chown_async_internal(nfs, path, 1, uid, gid, cb, private_data); case NFS_V4: return nfs4_chown_async_internal(nfs, path, 1, uid, gid, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int gid, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_fchown_async(nfs, nfsfh, uid, gid, cb, private_data); case NFS_V4: return nfs4_fchown_async(nfs, nfsfh, uid, gid, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_utimes_async(struct nfs_context *nfs, const char *path, struct timeval *times, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_utimes_async_internal(nfs, path, 0, times, cb, private_data); case NFS_V4: return nfs4_utimes_async_internal(nfs, path, 0, times, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_lutimes_async(struct nfs_context *nfs, const char *path, struct timeval *times, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_utimes_async_internal(nfs, path, 1, times, cb, private_data); case NFS_V4: return nfs4_utimes_async_internal(nfs, path, 1, times, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_utime_async(struct nfs_context *nfs, const char *path, struct utimbuf *times, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_utime_async(nfs, path, times, cb, private_data); case NFS_V4: return nfs4_utime_async(nfs, path, times, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv4", __FUNCTION__); return -1; } } int nfs_access_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_access_async(nfs, path, mode, cb, private_data); case NFS_V4: return nfs4_access_async(nfs, path, mode, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_access2_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_access2_async(nfs, path, cb, private_data); case NFS_V4: return nfs4_access2_async(nfs, path, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv4", __FUNCTION__); return -1; } } int nfs_symlink_async(struct nfs_context *nfs, const char *target, const char *newpath, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_symlink_async(nfs, target, newpath, cb, private_data); case NFS_V4: return nfs4_symlink_async(nfs, target, newpath, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_rename_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_rename_async(nfs, oldpath, newpath, cb, private_data); case NFS_V4: return nfs4_rename_async(nfs, oldpath, newpath, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } int nfs_link_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data) { switch (nfs->nfsi->version) { case NFS_V3: return nfs3_link_async(nfs, oldpath, newpath, cb, private_data); case NFS_V4: return nfs4_link_async(nfs, oldpath, newpath, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->nfsi->version); return -1; } } /* * Get/Set the maximum supported READ size by the server */ uint64_t nfs_get_readmax(struct nfs_context *nfs) { return nfs->nfsi->readmax; } void nfs_set_readmax(struct nfs_context *nfs, uint64_t readmax) { nfs->nfsi->readmax = readmax; } /* * Get/Set the maximum supported WRITE size by the server */ uint64_t nfs_get_writemax(struct nfs_context *nfs) { return nfs->nfsi->writemax; } void nfs_set_writemax(struct nfs_context *nfs, uint64_t writemax) { nfs->nfsi->writemax = writemax; } void nfs_set_tcp_syncnt(struct nfs_context *nfs, int v) { rpc_set_tcp_syncnt(nfs->rpc, v); } void nfs_set_uid(struct nfs_context *nfs, int uid) { rpc_set_uid(nfs->rpc, uid); } void nfs_set_gid(struct nfs_context *nfs, int gid) { rpc_set_gid(nfs->rpc, gid); } void nfs_set_pagecache(struct nfs_context *nfs, uint32_t v) { rpc_set_pagecache(nfs->rpc, v); } void nfs_set_pagecache_ttl(struct nfs_context *nfs, uint32_t v) { rpc_set_pagecache_ttl(nfs->rpc, v); } void nfs_set_readahead(struct nfs_context *nfs, uint32_t v) { rpc_set_readahead(nfs->rpc, v); } void nfs_set_debug(struct nfs_context *nfs, int level) { rpc_set_debug(nfs->rpc, level); } void nfs_set_dircache(struct nfs_context *nfs, int enabled) { nfs->nfsi->dircache_enabled = enabled; } void nfs_set_autoreconnect(struct nfs_context *nfs, int num_retries) { nfs->nfsi->auto_reconnect = num_retries; } int nfs_set_version(struct nfs_context *nfs, int version) { switch (version) { case NFS_V3: case NFS_V4: nfs->nfsi->version = version; break; default: nfs_set_error(nfs, "NFS version %d is not supported", version); return -1; } return 0; } int nfs_get_version(struct nfs_context *nfs) { return nfs->nfsi->version; } void nfs_set_error(struct nfs_context *nfs, char *error_string, ...) { va_list ap; char *str = NULL; #ifdef HAVE_MULTITHREADING /* All thread contexts share the same rpc_context so * use the mutex from the rpc_context. */ nfs_mt_mutex_lock(&nfs->rpc->rpc_mutex); #endif /* HAVE_MULTITHREADING */ va_start(ap, error_string); str = malloc(1024); vsnprintf(str, 1024, error_string, ap); if (nfs->error_string != NULL) { free(nfs->error_string); } nfs->error_string = str; va_end(ap); #ifdef HAVE_MULTITHREADING nfs_mt_mutex_unlock(&nfs->rpc->rpc_mutex); #endif /* HAVE_MULTITHREADING */ } struct mount_cb_data { rpc_cb cb; void *private_data; char *server; }; static void free_mount_cb_data(struct mount_cb_data *data) { if (data->server != NULL) { free(data->server); data->server = NULL; } free(data); } static void mount_export_5_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { struct mount_cb_data *data = private_data; assert(rpc->magic == RPC_CONTEXT_MAGIC); if (status != RPC_STATUS_SUCCESS) { data->cb(rpc, -EFAULT, command_data, data->private_data); free_mount_cb_data(data); return; } data->cb(rpc, 0, command_data, data->private_data); if (rpc_disconnect(rpc, "normal disconnect") != 0) { rpc_set_error(rpc, "Failed to disconnect\n"); } free_mount_cb_data(data); } static void mount_export_4_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { struct mount_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_SUCCESS) { data->cb(rpc, -EFAULT, command_data, data->private_data); free_mount_cb_data(data); return; } if (rpc_mount3_export_async(rpc, mount_export_5_cb, data) != 0) { data->cb(rpc, -ENOMEM, command_data, data->private_data); free_mount_cb_data(data); return; } } int mount_getexports_async(struct rpc_context *rpc, const char *server, rpc_cb cb, void *private_data) { struct mount_cb_data *data; assert(rpc->magic == RPC_CONTEXT_MAGIC); data = malloc(sizeof(struct mount_cb_data)); if (data == NULL) { return -1; } memset(data, 0, sizeof(struct mount_cb_data)); data->cb = cb; data->private_data = private_data; data->server = strdup(server); if (data->server == NULL) { free_mount_cb_data(data); return -1; } if (rpc_connect_program_async(rpc, data->server, MOUNT_PROGRAM, MOUNT_V3, mount_export_4_cb, data) != 0) { rpc_set_error(rpc, "Failed to start connection. %s", rpc_get_error(rpc)); free_mount_cb_data(data); return -1; } return 0; } struct rpc_context * nfs_get_rpc_context(struct nfs_context *nfs) { assert(nfs->rpc->magic == RPC_CONTEXT_MAGIC); return nfs->rpc; } const char * nfs_get_server(struct nfs_context *nfs) { return nfs->nfsi->server; } const char * nfs_get_export(struct nfs_context *nfs) { return nfs->nfsi->export; } const struct nfs_fh * nfs_get_rootfh(struct nfs_context *nfs) { return &nfs->nfsi->rootfh; } struct nfs_fh * nfs_get_fh(struct nfsfh *nfsfh) { return &nfsfh->fh; } uint16_t nfs_umask(struct nfs_context *nfs, uint16_t mask) { uint16_t tmp = nfs->nfsi->mask; nfs->nfsi->mask = mask; return tmp; } /* * Sets timeout for nfs apis */ void nfs_set_timeout(struct nfs_context *nfs,int timeout) { rpc_set_timeout(nfs->rpc,timeout); } /* * Gets timeout for nfs apis */ int nfs_get_timeout(struct nfs_context *nfs) { return rpc_get_timeout(nfs->rpc); } int rpc_null_async(struct rpc_context *rpc, int program, int version, rpc_cb cb, void *private_data) { struct rpc_pdu *pdu; pdu = rpc_allocate_pdu(rpc, program, version, 0, cb, private_data, (zdrproc_t)zdr_void, 0); if (pdu == NULL) { rpc_set_error(rpc, "Out of memory. Failed to allocate pdu " "for NULL call"); return -1; } if (rpc_queue_pdu(rpc, pdu) != 0) { rpc_set_error(rpc, "Out of memory. Failed to queue pdu " "for NULL call"); return -1; } return 0; }