libnfs/lib/libnfs.c

2147 lines
56 KiB
C
Executable File

/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
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 <http://www.gnu.org/licenses/>.
*/
/*
* 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 <win32/win32_compat.h>
#endif
#ifdef HAVE_UTIME_H
#include <utime.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_VFS_H
#include <sys/vfs.h>
#endif
#ifdef HAVE_SYS_STATVFS_H
#include <sys/statvfs.h>
#endif
#if defined(__ANDROID__) && !defined(HAVE_SYS_STATVFS_H)
#define statvfs statfs
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#ifdef HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#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;
}