Commit f91748e6 authored by Rusty Russell's avatar Rusty Russell

nfs: initial import.

Another Ronnie module!
parent b581c238
CC=gcc
CFLAGS=-g -O0 -Wall -W -I. "-D_U_=__attribute__((unused))" -D_FILE_OFFSET_BITS=64
LIBS=
LIBNFS_OBJ = libnfs-raw-mount.o libnfs-raw-portmap.o libnfs-raw-nfs.o libnfs-raw-nfsacl.o mount.o nfs.o nfsacl.o portmap.o pdu.o init.o socket.o libnfs.o libnfs-sync.o
all: nfsclient-raw nfsclient-async nfsclient-sync
nfsclient-async: nfsclient-async.c libnfs.a
$(CC) -o $@ nfsclient-async.c libnfs.a $(LIBS)
nfsclient-sync: nfsclient-sync.c libnfs.a
$(CC) -o $@ nfsclient-sync.c libnfs.a $(LIBS)
nfsclient-raw: nfsclient-raw.c libnfs.a
$(CC) -o $@ nfsclient-raw.c libnfs.a $(LIBS)
libnfs.a: $(LIBNFS_OBJ)
@echo Creating library $@
ar r libnfs.a $(LIBNFS_OBJ)
ranlib libnfs.a
libnfs-raw-mount.h: mount.x
@echo Generating $@
rpcgen -h mount.x > libnfs-raw-mount.h
libnfs-raw-mount.c: mount.x
@echo Generating $@
rpcgen -c mount.x | sed -e "s/#include \"mount.h\"/#include \"libnfs-raw-mount.h\"/" > libnfs-raw-mount.c
libnfs-raw-mount.o: libnfs-raw-mount.c libnfs-raw-mount.h
@echo Compiling $@
gcc -g -c libnfs-raw-mount.c -o $@
libnfs-raw-nfs.h: nfs.x
@echo Generating $@
rpcgen -h nfs.x > libnfs-raw-nfs.h
libnfs-raw-nfs.c: nfs.x
@echo Generating $@
rpcgen -c nfs.x | sed -e "s/#include \"nfs.h\"/#include \"libnfs-raw-nfs.h\"/" > libnfs-raw-nfs.c
libnfs-raw-nfs.o: libnfs-raw-nfs.c libnfs-raw-nfs.h
@echo Compiling $@
gcc -g -c libnfs-raw-nfs.c -o $@
libnfs-raw-nfsacl.h: nfsacl.x
@echo Generating $@
rpcgen -h nfsacl.x > libnfs-raw-nfsacl.h
libnfs-raw-nfsacl.c: nfsacl.x
@echo Generating $@
rpcgen -c nfsacl.x | sed -e "s/#include \"nfsacl.h\"/#include \"libnfs-raw-nfsacl.h\"/" > libnfs-raw-nfsacl.c
libnfs-raw-nfsacl.o: libnfs-raw-nfsacl.c libnfs-raw-nfsacl.h
@echo Compiling $@
gcc -g -c libnfs-raw-nfsacl.c -o $@
libnfs-raw-portmap.h: portmap.x
@echo Generating $@
rpcgen -h portmap.x > libnfs-raw-portmap.h
libnfs-raw-portmap.c: portmap.x
@echo Generating $@
rpcgen -c portmap.x | sed -e "s/#include \"portmap.h\"/#include \"libnfs-raw-portmap.h\"/" > libnfs-raw-portmap.c
libnfs-raw-portmap.o: libnfs-raw-portmap.c libnfs-raw-portmap.h
@echo Compiling $@
gcc -g -c libnfs-raw-portmap.c -o $@
clean:
rm -f *.o
rm -f *.a
rm -f libnfs-raw-mount.h libnfs-raw-mount.c
rm -f libnfs-raw-nfs.h libnfs-raw-nfs.c
rm -f libnfs-raw-nfsacl.h libnfs-raw-nfsacl.c
rm -f libnfs-raw-portmap.h libnfs-raw-portmap.c
rm -f nfsclient-raw nfsclient-async nfsclient-sync
/*
Unix SMB/CIFS implementation.
some simple double linked list macros
Copyright (C) Andrew Tridgell 1998-2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* To use these macros you must have a structure containing a next and
prev pointer */
#ifndef _DLINKLIST_H
#define _DLINKLIST_H
/*
February 2010 - changed list format to have a prev pointer from the
list head. This makes DLIST_ADD_END() O(1) even though we only have
one list pointer.
The scheme is as follows:
1) with no entries in the list:
list_head == NULL
2) with 1 entry in the list:
list_head->next == NULL
list_head->prev == list_head
3) with 2 entries in the list:
list_head->next == element2
list_head->prev == element2
element2->prev == list_head
element2->next == NULL
4) with N entries in the list:
list_head->next == element2
list_head->prev == elementN
elementN->prev == element{N-1}
elementN->next == NULL
This allows us to find the tail of the list by using
list_head->prev, which means we can add to the end of the list in
O(1) time
Note that the 'type' arguments below are no longer needed, but
are kept for now to prevent an incompatible argument change
*/
/*
add an element at the front of a list
*/
#define DLIST_ADD(list, p) \
do { \
if (!(list)) { \
(p)->prev = (list) = (p); \
(p)->next = NULL; \
} else { \
(p)->prev = (list)->prev; \
(list)->prev = (p); \
(p)->next = (list); \
(list) = (p); \
} \
} while (0)
/*
remove an element from a list
Note that the element doesn't have to be in the list. If it
isn't then this is a no-op
*/
#define DLIST_REMOVE(list, p) \
do { \
if ((p) == (list)) { \
if ((p)->next) (p)->next->prev = (p)->prev; \
(list) = (p)->next; \
} else if ((list) && (p) == (list)->prev) { \
(p)->prev->next = NULL; \
(list)->prev = (p)->prev; \
} else { \
if ((p)->prev) (p)->prev->next = (p)->next; \
if ((p)->next) (p)->next->prev = (p)->prev; \
} \
if ((p) != (list)) (p)->next = (p)->prev = NULL; \
} while (0)
/*
find the head of the list given any element in it.
Note that this costs O(N), so you should avoid this macro
if at all possible!
*/
#define DLIST_HEAD(p, result_head) \
do { \
(result_head) = (p); \
while (DLIST_PREV(result_head)) (result_head) = (result_head)->prev; \
} while(0)
/* return the last element in the list */
#define DLIST_TAIL(list) ((list)?(list)->prev:NULL)
/* return the previous element in the list. */
#define DLIST_PREV(p) (((p)->prev && (p)->prev->next != NULL)?(p)->prev:NULL)
/* insert 'p' after the given element 'el' in a list. If el is NULL then
this is the same as a DLIST_ADD() */
#define DLIST_ADD_AFTER(list, p, el) \
do { \
if (!(list) || !(el)) { \
DLIST_ADD(list, p); \
} else { \
(p)->prev = (el); \
(p)->next = (el)->next; \
(el)->next = (p); \
if ((p)->next) (p)->next->prev = (p); \
if ((list)->prev == (el)) (list)->prev = (p); \
}\
} while (0)
/*
add to the end of a list.
Note that 'type' is ignored
*/
#define DLIST_ADD_END(list, p, type) \
do { \
if (!(list)) { \
DLIST_ADD(list, p); \
} else { \
DLIST_ADD_AFTER(list, p, (list)->prev); \
} \
} while (0)
/* promote an element to the from of a list */
#define DLIST_PROMOTE(list, p) \
do { \
DLIST_REMOVE(list, p); \
DLIST_ADD(list, p); \
} while (0)
/*
demote an element to the end of a list.
Note that 'type' is ignored
*/
#define DLIST_DEMOTE(list, p, type) \
do { \
DLIST_REMOVE(list, p); \
DLIST_ADD_END(list, p, NULL); \
} while (0)
/*
concatenate two lists - putting all elements of the 2nd list at the
end of the first list.
Note that 'type' is ignored
*/
#define DLIST_CONCATENATE(list1, list2, type) \
do { \
if (!(list1)) { \
(list1) = (list2); \
} else { \
(list1)->prev->next = (list2); \
if (list2) { \
void *_tmplist = (void *)(list1)->prev; \
(list1)->prev = (list2)->prev; \
(list2)->prev = _tmplist; \
} \
} \
} while (0)
#endif /* _DLINKLIST_H */
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <rpc/xdr.h>
#include "dlinklist.h"
#include "libnfs.h"
#include "libnfs-raw.h"
#include "libnfs-private.h"
struct rpc_context *rpc_init_context(void)
{
struct rpc_context *rpc;
rpc = malloc(sizeof(struct rpc_context));
if (rpc == NULL) {
printf("Failed to allocate rpc context\n");
return NULL;
}
bzero(rpc, sizeof(struct rpc_context));
rpc->encodebuflen = 65536;
rpc->encodebuf = malloc(rpc->encodebuflen);
if (rpc->encodebuf == NULL) {
printf("Failed to allocate a buffer for rpc encoding\n");
free(rpc);
return NULL;
}
rpc->auth = authunix_create_default();
if (rpc->auth == NULL) {
printf("failed to create authunix\n");
free(rpc->encodebuf);
free(rpc);
return NULL;
}
rpc->xid = 1;
rpc->fd = -1;
return rpc;
}
void rpc_set_auth(struct rpc_context *rpc, struct AUTH *auth)
{
if (rpc->auth != NULL) {
auth_destroy(rpc->auth);
}
rpc->auth = auth;
}
void rpc_set_error(struct rpc_context *rpc, char *error_string, ...)
{
va_list ap;
char *str;
if (rpc->error_string != NULL) {
free(rpc->error_string);
}
va_start(ap, error_string);
vasprintf(&str, error_string, ap);
rpc->error_string = str;
va_end(ap);
}
char *rpc_get_error(struct rpc_context *rpc)
{
return rpc->error_string;
}
void rpc_error_all_pdus(struct rpc_context *rpc, char *error)
{
struct rpc_pdu *pdu;
while((pdu = rpc->outqueue) != NULL) {
pdu->cb(rpc, RPC_STATUS_ERROR, error, pdu->private_data);
DLIST_REMOVE(rpc->outqueue, pdu);
rpc_free_pdu(rpc, pdu);
}
while((pdu = rpc->waitpdu) != NULL) {
pdu->cb(rpc, RPC_STATUS_ERROR, error, pdu->private_data);
DLIST_REMOVE(rpc->waitpdu, pdu);
rpc_free_pdu(rpc, pdu);
}
}
void rpc_destroy_context(struct rpc_context *rpc)
{
struct rpc_pdu *pdu;
while((pdu = rpc->outqueue) != NULL) {
pdu->cb(rpc, RPC_STATUS_CANCEL, NULL, pdu->private_data);
DLIST_REMOVE(rpc->outqueue, pdu);
rpc_free_pdu(rpc, pdu);
}
while((pdu = rpc->waitpdu) != NULL) {
pdu->cb(rpc, RPC_STATUS_CANCEL, NULL, pdu->private_data);
DLIST_REMOVE(rpc->waitpdu, pdu);
rpc_free_pdu(rpc, pdu);
}
auth_destroy(rpc->auth);
rpc->auth =NULL;
if (rpc->fd != -1) {
close(rpc->fd);
}
if (rpc->encodebuf != NULL) {
free(rpc->encodebuf);
rpc->encodebuf = NULL;
}
if (rpc->error_string != NULL) {
free(rpc->error_string);
rpc->error_string = NULL;
}
free(rpc);
}
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <rpc/auth.h>
struct rpc_context {
int fd;
int is_connected;
char *error_string;
rpc_cb connect_cb;
void *connect_data;
AUTH *auth;
unsigned long xid;
/* buffer used for encoding RPC PDU */
char *encodebuf;
int encodebuflen;
struct rpc_pdu *outqueue;
struct rpc_pdu *waitpdu;
int insize;
int inpos;
char *inbuf;
};
struct rpc_pdu {
struct rpc_pdu *prev, *next;
unsigned long xid;
XDR xdr;
int written;
struct rpc_data outdata;
rpc_cb cb;
void *private_data;
/* function to decode the xdr reply data and buffer to decode into */
xdrproc_t xdr_decode_fn;
caddr_t xdr_decode_buf;
int xdr_decode_bufsize;
};
struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int version, int procedure, rpc_cb cb, void *private_data, xdrproc_t xdr_decode_fn, int xdr_bufsize);
void rpc_free_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu);
int rpc_queue_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu);
int rpc_get_pdu_size(char *buf);
int rpc_process_pdu(struct rpc_context *rpc, char *buf, int size);
void rpc_error_all_pdus(struct rpc_context *rpc, char *error);
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* This is the lowlevel interface to access NFS resources.
* Through this interface you have access to the full gamut of nfs and nfs related
* protocol as well as the XDR encoded/decoded structures.
*/
#include <stdint.h>
struct rpc_data {
int size;
unsigned char *data;
};
struct rpc_context;
struct rpc_context *rpc_init_context(void);
void rpc_destroy_context(struct rpc_context *rpc);
struct AUTH;
void rpc_set_auth(struct rpc_context *rpc, struct AUTH *auth);
int rpc_get_fd(struct rpc_context *rpc);
int rpc_which_events(struct rpc_context *rpc);
int rpc_service(struct rpc_context *rpc, int revents);
char *rpc_get_error(struct rpc_context *rpc);
#define RPC_STATUS_SUCCESS 0
#define RPC_STATUS_ERROR 1
#define RPC_STATUS_CANCEL 2
typedef void (*rpc_cb)(struct rpc_context *rpc, int status, void *data, void *private_data);
/*
* Async connection to the tcp port at server:port.
* Function returns
* 0 : The connection was initiated. Once the connection establish finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the connection. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : The tcp connection was successfully established.
* data is NULL.
* RPC_STATUS_ERROR : The connection failed to establish.
* data is the erro string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* : data is NULL.
*/
int rpc_connect_async(struct rpc_context *rpc, const char *server, int port, int use_privileged_port, rpc_cb cb, void *private_data);
/*
* When disconnecting a connection in flight. All commands in flight will be called with the callback
* and status RPC_STATUS_ERROR. Data will be the error string for the disconnection.
*/
int rpc_disconnect(struct rpc_context *rpc, char *error);
void rpc_set_error(struct rpc_context *rpc, char *error_string, ...);
/*
* PORTMAP FUNCTIONS
*/
/*
* Call PORTMAPPER/NULL
* Function returns
* 0 : The connection was initiated. Once the connection establish finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the connection. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the portmapper daemon.
* data is NULL.
* RPC_STATUS_ERROR : An error occured when trying to contact the portmapper.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_pmap_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
/*
* Call PORTMAPPER/GETPORT.
* Function returns
* 0 : The connection was initiated. Once the connection establish finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the connection. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the portmapper daemon.
* data is a (uint32_t *), containing the port returned.
* RPC_STATUS_ERROR : An error occured when trying to contact the portmapper.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_pmap_getport_async(struct rpc_context *rpc, int program, int version, rpc_cb cb, void *private_data);
/*
* MOUNT FUNCTIONS
*/
char *mountstat3_to_str(int stat);
int mountstat3_to_errno(int error);
/*
* Call MOUNT/NULL
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the mount daemon.
* data is NULL.
* RPC_STATUS_ERROR : An error occured when trying to contact the mount daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_mount_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
/*
* Call MOUNT/MNT
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the mount daemon.
* data is mountres3 *.
* RPC_STATUS_ERROR : An error occured when trying to contact the mount daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_mount_mnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data);
/*
* Call MOUNT/DUMP
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the mount daemon.
* data is a mountlist.
* RPC_STATUS_ERROR : An error occured when trying to contact the mount daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_mount_dump_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
/*
* Call MOUNT/UMNT
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the mount daemon.
* data NULL.
* RPC_STATUS_ERROR : An error occured when trying to contact the mount daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_mount_umnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data);
/*
* Call MOUNT/UMNTALL
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the mount daemon.
* data NULL.
* RPC_STATUS_ERROR : An error occured when trying to contact the mount daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_mount_umntall_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
/*
* Call MOUNT/EXPORT
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the mount daemon.
* data is an exports.
* RPC_STATUS_ERROR : An error occured when trying to contact the mount daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_mount_export_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
/*
* NFS FUNCTIONS
*/
struct nfs_fh3;
char *nfsstat3_to_str(int error);
int nfsstat3_to_errno(int error);
/*
* Call NFS/NULL
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is NULL.
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
/*
* Call NFS/GETATTR
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is GETATTR3res
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_getattr_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data);
/*
* Call NFS/LOOKUP
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is LOOKUP3res
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_lookup_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *name, void *private_data);
/*
* Call NFS/ACCESS
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is ACCESS3res
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_access_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, int access, void *private_data);
/*
* Call NFS/READ
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is ACCESS3res
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_read_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, off_t offset, size_t count, void *private_data);
/*
* Call NFS/WRITE
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is WRITE3res *
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_write_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *buf, off_t offset, size_t count, int stable_how, void *private_data);
/*
* Call NFS/COMMIT
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is COMMIT3res *
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_commit_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data);
/*
* Call NFS/SETATTR
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is SETATTR3res *
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
struct SETATTR3args;
int rpc_nfs_setattr_async(struct rpc_context *rpc, rpc_cb cb, struct SETATTR3args *args, void *private_data);
/*
* Call NFS/MKDIR
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is MKDIR3res *
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_mkdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *dir, void *private_data);
/*
* Call NFS/RMDIR
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is RMDIR3res *
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_rmdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *dir, void *private_data);
/*
* Call NFS/CREATE
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is CREATE3res *
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_create_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *name, int mode, void *private_data);
/*
* Call NFS/REMOVE
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is REMOVE3res *
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_remove_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *name, void *private_data);
/*
* Call NFS/REMOVE
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is READDIR3res *
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_readdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, uint64_t cookie, char *cookieverf, int count, void *private_data);
/*
* Call NFS/FSSTAT
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is FSSTAT3res
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_fsstat_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data);
/*
* Call NFS/READLINK
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is READLINK3res *
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_readlink_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data);
/*
* Call NFS/SYMLINK
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is SYMLINK3res *
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_symlink_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *newname, char *oldpath, void *private_data);
/*
* Call NFS/RENAME
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is RENAME3res *
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_rename_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *olddir, char *oldname, struct nfs_fh3 *newdir, char *newname, void *private_data);
/*
* Call NFS/LINK
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is LINK3res *
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfs_link_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *file, struct nfs_fh3 *newdir, char *newname, void *private_data);
/*
* NFSACL FUNCTIONS
*/
/*
* Call NFSACL/NULL
* Function returns
* 0 : The call was initiated. The callback will be invoked when the call completes.
* <0 : An error occured when trying to set up the call. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
* data is NULL.
* RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon.
* data is the error string.
* RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
* data is NULL.
*/
int rpc_nfsacl_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* High level api to nfs filesystems
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include "libnfs.h"
#include "libnfs-raw.h"
#include "libnfs-raw-mount.h"
#include "libnfs-raw-nfs.h"
struct sync_cb_data {
int is_finished;
int status;
off_t offset;
void *return_data;
int return_int;
};
static void wait_for_reply(struct nfs_context *nfs, struct sync_cb_data *cb_data)
{
struct pollfd pfd;
for (;;) {
if (cb_data->is_finished) {
break;
}
pfd.fd = nfs_get_fd(nfs);
pfd.events = nfs_which_events(nfs);
if (poll(&pfd, 1, -1) < 0) {
printf("Poll failed");
cb_data->status = -EIO;
break;
}
if (nfs_service(nfs, pfd.revents) < 0) {
printf("nfs_service failed\n");
cb_data->status = -EIO;
break;
}
}
}
/*
* connect to the server and mount the export
*/
static void mount_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("mount/mnt call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_mount_sync(struct nfs_context *nfs, const char *server, const char *export)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_mount_async(nfs, server, export, mount_cb, &cb_data) != 0) {
printf("nfs_mount_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* stat()
*/
static void stat_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("stat call failed with \"%s\"\n", (char *)data);
return;
}
memcpy(cb_data->return_data, data, sizeof(struct stat));
}
int nfs_stat_sync(struct nfs_context *nfs, const char *path, struct stat *st)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
cb_data.return_data = st;
if (nfs_stat_async(nfs, path, stat_cb, &cb_data) != 0) {
printf("nfs_stat_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* open()
*/
static void open_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
struct nfsfh *fh, **nfsfh;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("open call failed with \"%s\"\n", (char *)data);
return;
}
fh = data;
nfsfh = cb_data->return_data;
*nfsfh = fh;
}
int nfs_open_sync(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
cb_data.return_data = nfsfh;
if (nfs_open_async(nfs, path, mode, open_cb, &cb_data) != 0) {
printf("nfs_open_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* pread()
*/
static void pread_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
char *buffer;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("pread call failed with \"%s\"\n", (char *)data);
return;
}
buffer = cb_data->return_data;
memcpy(buffer, (char *)data, status);
}
int nfs_pread_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buffer)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
cb_data.return_data = buffer;
if (nfs_pread_async(nfs, nfsfh, offset, count, pread_cb, &cb_data) != 0) {
printf("nfs_pread_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* read()
*/
int nfs_read_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buffer)
{
return nfs_pread_sync(nfs, nfsfh, nfs_get_current_offset(nfsfh), count, buffer);
}
/*
* close()
*/
static void close_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("close call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_close_sync(struct nfs_context *nfs, struct nfsfh *nfsfh)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_close_async(nfs, nfsfh, close_cb, &cb_data) != 0) {
printf("nfs_close_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* fstat()
*/
int nfs_fstat_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *st)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
cb_data.return_data = st;
if (nfs_fstat_async(nfs, nfsfh, stat_cb, &cb_data) != 0) {
printf("nfs_fstat_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* pwrite()
*/
static void pwrite_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("pwrite call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_pwrite_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_pwrite_async(nfs, nfsfh, offset, count, buf, pwrite_cb, &cb_data) != 0) {
printf("nfs_pwrite_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* write()
*/
int nfs_write_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf)
{
return nfs_pwrite_sync(nfs, nfsfh, nfs_get_current_offset(nfsfh), count, buf);
}
/*
* fsync()
*/
static void fsync_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("fsync call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_fsync_sync(struct nfs_context *nfs, struct nfsfh *nfsfh)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_fsync_async(nfs, nfsfh, fsync_cb, &cb_data) != 0) {
printf("nfs_fsync_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* ftruncate()
*/
static void ftruncate_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("ftruncate call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_ftruncate_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t length)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_ftruncate_async(nfs, nfsfh, length, ftruncate_cb, &cb_data) != 0) {
printf("nfs_ftruncate_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* truncate()
*/
static void truncate_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("truncate call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_truncate_sync(struct nfs_context *nfs, const char *path, off_t length)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_truncate_async(nfs, path, length, truncate_cb, &cb_data) != 0) {
printf("nfs_ftruncate_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* mkdir()
*/
static void mkdir_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("mkdir call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_mkdir_sync(struct nfs_context *nfs, const char *path)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_mkdir_async(nfs, path, mkdir_cb, &cb_data) != 0) {
printf("nfs_mkdir_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* rmdir()
*/
static void rmdir_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("rmdir call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_rmdir_sync(struct nfs_context *nfs, const char *path)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_rmdir_async(nfs, path, rmdir_cb, &cb_data) != 0) {
printf("nfs_rmdir_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* creat()
*/
static void creat_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
struct nfsfh *fh, **nfsfh;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("creat call failed with \"%s\"\n", (char *)data);
return;
}
fh = data;
nfsfh = cb_data->return_data;
*nfsfh = fh;
}
int nfs_creat_sync(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
cb_data.return_data = nfsfh;
if (nfs_creat_async(nfs, path, mode, creat_cb, &cb_data) != 0) {
printf("nfs_creat_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* unlink()
*/
static void unlink_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("unlink call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_unlink_sync(struct nfs_context *nfs, const char *path)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_unlink_async(nfs, path, unlink_cb, &cb_data) != 0) {
printf("nfs_unlink_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* opendir()
*/
static void opendir_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
struct nfsdir *dir, **nfsdir;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("opendir call failed with \"%s\"\n", (char *)data);
return;
}
dir = data;
nfsdir = cb_data->return_data;
*nfsdir = dir;
}
int nfs_opendir_sync(struct nfs_context *nfs, const char *path, struct nfsdir **nfsdir)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
cb_data.return_data = nfsdir;
if (nfs_opendir_async(nfs, path, opendir_cb, &cb_data) != 0) {
printf("nfs_opendir_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* lseek()
*/
static void lseek_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("lseek call failed with \"%s\"\n", (char *)data);
return;
}
if (cb_data->return_data != NULL) {
memcpy(cb_data->return_data, data, sizeof(off_t));
}
}
int nfs_lseek_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, int whence, off_t *current_offset)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
cb_data.return_data = current_offset;
if (nfs_lseek_async(nfs, nfsfh, offset, whence, lseek_cb, &cb_data) != 0) {
printf("nfs_lseek_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* statvfs()
*/
static void statvfs_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("statvfs call failed with \"%s\"\n", (char *)data);
return;
}
memcpy(cb_data->return_data, data, sizeof(struct statvfs));
}
int nfs_statvfs_sync(struct nfs_context *nfs, const char *path, struct statvfs *svfs)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
cb_data.return_data = svfs;
if (nfs_statvfs_async(nfs, path, statvfs_cb, &cb_data) != 0) {
printf("nfs_statvfs_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* readlink()
*/
static void readlink_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("readlink call failed with \"%s\"\n", (char *)data);
return;
}
if (strlen(data) > (size_t)cb_data->return_int) {
printf("Too small buffer for readlink\n");
cb_data->status = -ENAMETOOLONG;
return;
}
memcpy(cb_data->return_data, data, strlen(data)+1);
}
int nfs_readlink_sync(struct nfs_context *nfs, const char *path, char *buf, int bufsize)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
cb_data.return_data = buf;
cb_data.return_int = bufsize;
if (nfs_readlink_async(nfs, path, readlink_cb, &cb_data) != 0) {
printf("nfs_readlink_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* chmod()
*/
static void chmod_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("chmod call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_chmod_sync(struct nfs_context *nfs, const char *path, int mode)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_chmod_async(nfs, path, mode, chmod_cb, &cb_data) != 0) {
printf("nfs_chmod_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* fchmod()
*/
static void fchmod_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("fchmod call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_fchmod_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_fchmod_async(nfs, nfsfh, mode, fchmod_cb, &cb_data) != 0) {
printf("nfs_fchmod_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* chown()
*/
static void chown_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("chown call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_chown_sync(struct nfs_context *nfs, const char *path, int uid, int gid)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_chown_async(nfs, path, uid, gid, chown_cb, &cb_data) != 0) {
printf("nfs_chown_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* fchown()
*/
static void fchown_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("fchown call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_fchown_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int gid)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_fchown_async(nfs, nfsfh, uid, gid, fchown_cb, &cb_data) != 0) {
printf("nfs_fchown_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* utimes()
*/
static void utimes_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("utimes call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_utimes_sync(struct nfs_context *nfs, const char *path, struct timeval *times)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_utimes_async(nfs, path, times, utimes_cb, &cb_data) != 0) {
printf("nfs_utimes_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* utime()
*/
static void utime_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("utime call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_utime_sync(struct nfs_context *nfs, const char *path, struct utimbuf *times)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_utime_async(nfs, path, times, utime_cb, &cb_data) != 0) {
printf("nfs_utimes_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* access()
*/
static void access_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("access call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_access_sync(struct nfs_context *nfs, const char *path, int mode)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_access_async(nfs, path, mode, access_cb, &cb_data) != 0) {
printf("nfs_access_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* symlink()
*/
static void symlink_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("symlink call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_symlink_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_symlink_async(nfs, oldpath, newpath, symlink_cb, &cb_data) != 0) {
printf("nfs_symlink_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* rename()
*/
static void rename_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("rename call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_rename_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_rename_async(nfs, oldpath, newpath, rename_cb, &cb_data) != 0) {
printf("nfs_rename_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
* link()
*/
static void link_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
cb_data->is_finished = 1;
cb_data->status = status;
if (status < 0) {
printf("link call failed with \"%s\"\n", (char *)data);
return;
}
}
int nfs_link_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_link_async(nfs, oldpath, newpath, link_cb, &cb_data) != 0) {
printf("nfs_link_async failed\n");
return -1;
}
wait_for_reply(nfs, &cb_data);
return cb_data.status;
}
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* High level api to nfs filesystems
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <utime.h>
#include <unistd.h>
#include <fcntl.h>
#include "libnfs.h"
#include "libnfs-raw.h"
#include "libnfs-raw-mount.h"
#include "libnfs-raw-nfs.h"
struct nfsfh {
struct nfs_fh3 fh;
int is_sync;
off_t offset;
};
struct nfsdir {
struct nfsdirent *entries;
struct nfsdirent *current;
};
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);
}
struct nfs_context {
struct rpc_context *rpc;
char *server;
char *export;
struct nfs_fh3 rootfh;
int acl_support;
};
struct nfs_cb_data;
typedef int (*continue_func)(struct nfs_context *nfs, struct nfs_cb_data *data);
struct nfs_cb_data {
struct nfs_context *nfs;
struct nfsfh *nfsfh;
char *saved_path, *path;
nfs_cb cb;
void *private_data;
continue_func continue_cb;
void *continue_data;
void (*free_continue_data)(void *);
int continue_int;
struct nfs_fh3 fh;
};
static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh);
void nfs_set_auth(struct nfs_context *nfs, struct AUTH *auth)
{
return rpc_set_auth(nfs->rpc, auth);
}
int nfs_get_fd(struct nfs_context *nfs)
{
return rpc_get_fd(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)
{
return rpc_get_error(nfs->rpc);
};
struct nfs_context *nfs_init_context(void)
{
struct nfs_context *nfs;
nfs = malloc(sizeof(struct nfs_context));
if (nfs == NULL) {
printf("Failed to allocate nfs context\n");
return NULL;
}
nfs->rpc = rpc_init_context();
if (nfs->rpc == NULL) {
printf("Failed to allocate rpc sub-context\n");
free(nfs);
return NULL;
}
return nfs;
}
void nfs_destroy_context(struct nfs_context *nfs)
{
rpc_destroy_context(nfs->rpc);
nfs->rpc = NULL;
if (nfs->server) {
free(nfs->server);
nfs->server = NULL;
}
if (nfs->export) {
free(nfs->export);
nfs->export = NULL;
}
if (nfs->rootfh.data.data_val != NULL) {
free(nfs->rootfh.data.data_val);
nfs->rootfh.data.data_val = NULL;
}
free(nfs);
}
void free_nfs_cb_data(struct nfs_cb_data *data)
{
if (data->saved_path != NULL) {
free(data->saved_path);
data->saved_path = NULL;
}
if (data->continue_data != NULL) {
data->free_continue_data(data->continue_data);
data->continue_data = NULL;
}
if (data->fh.data.data_val != NULL) {
free(data->fh.data.data_val);
data->fh.data.data_val = NULL;
}
free(data);
}
static void nfs_mount_10_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
static void nfs_mount_9_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
nfs->acl_support = 1;
if (status == RPC_STATUS_ERROR) {
nfs->acl_support = 0;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
if (rpc_nfs_getattr_async(rpc, nfs_mount_10_cb, &nfs->rootfh, data) != 0) {
data->cb(-ENOMEM, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
}
static void nfs_mount_8_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
if (rpc_nfsacl_null_async(rpc, nfs_mount_9_cb, data) != 0) {
data->cb(-ENOMEM, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
}
static void nfs_mount_7_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
if (rpc_nfs_null_async(rpc, nfs_mount_8_cb, data) != 0) {
data->cb(-ENOMEM, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
}
static void nfs_mount_6_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
mountres3 *res;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->fhs_status != MNT3_OK) {
rpc_set_error(rpc, "RPC error: Mount failed with error %s(%d) %s(%d)", mountstat3_to_str(res->fhs_status), res->fhs_status, strerror(-mountstat3_to_errno(res->fhs_status)), -mountstat3_to_errno(res->fhs_status));
data->cb(mountstat3_to_errno(res->fhs_status), nfs, rpc_get_error(rpc), data->private_data);
free_nfs_cb_data(data);
return;
}
nfs->rootfh.data.data_len = res->mountres3_u.mountinfo.fhandle.fhandle3_len;
nfs->rootfh.data.data_val = malloc(nfs->rootfh.data.data_len);
if (nfs->rootfh.data.data_val == NULL) {
rpc_set_error(rpc, "Out of memory. Could not allocate memory to store root filehandle");
data->cb(-ENOMEM, nfs, rpc_get_error(rpc), data->private_data);
free_nfs_cb_data(data);
return;
}
memcpy(nfs->rootfh.data.data_val, res->mountres3_u.mountinfo.fhandle.fhandle3_val, nfs->rootfh.data.data_len);
rpc_disconnect(rpc, "normal disconnect");
if (rpc_connect_async(rpc, nfs->server, 2049, 1, nfs_mount_7_cb, data) != 0) {
data->cb(-ENOMEM, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
}
static void nfs_mount_5_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
if (rpc_mount_mnt_async(rpc, nfs_mount_6_cb, nfs->export, data) != 0) {
data->cb(-ENOMEM, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
}
static void nfs_mount_4_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
if (rpc_mount_null_async(rpc, nfs_mount_5_cb, data) != 0) {
data->cb(-ENOMEM, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
}
static void nfs_mount_3_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
uint32_t mount_port;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
mount_port = *(uint32_t *)command_data;
if (mount_port == 0) {
rpc_set_error(rpc, "RPC error. Mount program is not available on %s", nfs->server);
data->cb(-ENOENT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
rpc_disconnect(rpc, "normal disconnect");
if (rpc_connect_async(rpc, nfs->server, mount_port, 1, nfs_mount_4_cb, data) != 0) {
data->cb(-ENOMEM, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
}
static void nfs_mount_2_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
if (rpc_pmap_getport_async(rpc, MOUNT_PROGRAM, MOUNT_V3, nfs_mount_3_cb, private_data) != 0) {
data->cb(-ENOMEM, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
}
static void nfs_mount_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
if (rpc_pmap_null_async(rpc, nfs_mount_2_cb, data) != 0) {
data->cb(-ENOMEM, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
}
/*
* 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)
{
struct nfs_cb_data *data;
data = malloc(sizeof(struct nfs_cb_data));
if (data == NULL) {
rpc_set_error(nfs->rpc, "out of memory");
printf("failed to allocate memory for nfs mount data\n");
return -1;
}
bzero(data, sizeof(struct nfs_cb_data));
nfs->server = strdup(server);
nfs->export = strdup(export);
data->nfs = nfs;
data->cb = cb;
data->private_data = private_data;
if (rpc_connect_async(nfs->rpc, server, 111, 0, nfs_mount_1_cb, data) != 0) {
printf("Failed to start connection\n");
free_nfs_cb_data(data);
return -4;
}
return 0;
}
/*
* Functions to first look up a path, component by component, and then finally call a specific function once
* the filehandle for the final component is found.
*/
static void nfs_lookup_path_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
LOOKUP3res *res;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: Lookup of %s failed with %s(%d)", data->saved_path, 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;
}
if (nfs_lookup_path_async_internal(nfs, data, &res->LOOKUP3res_u.resok.object) != 0) {
rpc_set_error(nfs->rpc, "Failed to create lookup pdu");
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return;
}
}
static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh)
{
char *path, *str;
while (*data->path == '/') {
data->path++;
}
path = data->path;
str = index(path, '/');
if (str != NULL) {
*str = 0;
data->path = str+1;
} else {
while (*data->path != 0) {
data->path++;
}
}
if (*path == 0) {
data->fh.data.data_len = fh->data.data_len;
data->fh.data.data_val = malloc(data->fh.data.data_len);
if (data->fh.data.data_val == NULL) {
rpc_set_error(nfs->rpc, "Out of memory: Failed to allocate fh for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
memcpy(data->fh.data.data_val, fh->data.data_val, data->fh.data.data_len);
data->continue_cb(nfs, data);
return 0;
}
if (rpc_nfs_lookup_async(nfs->rpc, nfs_lookup_path_1_cb, fh, path, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send lookup call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data, continue_func continue_cb, void *continue_data, void (*free_continue_data)(void *), int continue_int)
{
struct nfs_cb_data *data;
if (path[0] != '/') {
rpc_set_error(nfs->rpc, "Pathname is not absulute %s", path);
return -1;
}
data = malloc(sizeof(struct nfs_cb_data));
if (data == NULL) {
rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
printf("failed to allocate memory for nfs cb data\n");
free_nfs_cb_data(data);
return -2;
}
bzero(data, sizeof(struct nfs_cb_data));
data->nfs = nfs;
data->cb = cb;
data->continue_cb = continue_cb;
data->continue_data = continue_data;
data->free_continue_data = free_continue_data;
data->continue_int = continue_int;
data->private_data = private_data;
data->saved_path = strdup(path);
if (data->saved_path == NULL) {
rpc_set_error(nfs->rpc, "out of memory: failed to copy path string");
printf("failed to allocate memory for path string\n");
free_nfs_cb_data(data);
return -2;
}
data->path = data->saved_path;
if (nfs_lookup_path_async_internal(nfs, data, &nfs->rootfh) != 0) {
printf("failed to lookup path\n");
/* return 0 here since the callback will be invoked if there is a failure */
return 0;
}
return 0;
}
/*
* Async stat()
*/
static void nfs_stat_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
GETATTR3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
struct stat st;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: GETATTR of %s failed with %s(%d)", data->saved_path, 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;
}
st.st_dev = -1;
st.st_ino = res->GETATTR3res_u.resok.obj_attributes.fileid;
st.st_mode = res->GETATTR3res_u.resok.obj_attributes.mode;
st.st_nlink = res->GETATTR3res_u.resok.obj_attributes.nlink;
st.st_uid = res->GETATTR3res_u.resok.obj_attributes.uid;
st.st_gid = res->GETATTR3res_u.resok.obj_attributes.gid;
st.st_rdev = 0;
st.st_size = res->GETATTR3res_u.resok.obj_attributes.size;
st.st_blksize = 4096;
st.st_blocks = res->GETATTR3res_u.resok.obj_attributes.size / 4096;
st.st_atime = res->GETATTR3res_u.resok.obj_attributes.atime.seconds;
st.st_mtime = res->GETATTR3res_u.resok.obj_attributes.mtime.seconds;
st.st_ctime = res->GETATTR3res_u.resok.obj_attributes.ctime.seconds;
data->cb(0, nfs, &st, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_stat_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
if (rpc_nfs_getattr_async(nfs->rpc, nfs_stat_1_cb, &data->fh, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send STAT GETATTR call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_stat_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
{
if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_stat_continue_internal, NULL, NULL, 0) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -1;
}
return 0;
}
/*
* Async open()
*/
static void nfs_open_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
ACCESS3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
struct nfsfh *nfsfh;
unsigned int nfsmode = 0;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: ACCESS of %s failed with %s(%d)", data->saved_path, 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;
}
if (data->continue_int & O_WRONLY) {
nfsmode |= ACCESS3_MODIFY;
}
if (data->continue_int & O_RDWR) {
nfsmode |= ACCESS3_READ|ACCESS3_MODIFY;
}
if (!(data->continue_int & (O_WRONLY|O_RDWR))) {
nfsmode |= ACCESS3_READ;
}
if (res->ACCESS3res_u.resok.access != nfsmode) {
rpc_set_error(nfs->rpc, "NFS: ACCESS denied. Required access %c%c%c. Allowed access %c%c%c",
nfsmode&ACCESS3_READ?'r':'-',
nfsmode&ACCESS3_MODIFY?'w':'-',
nfsmode&ACCESS3_EXECUTE?'x':'-',
res->ACCESS3res_u.resok.access&ACCESS3_READ?'r':'-',
res->ACCESS3res_u.resok.access&ACCESS3_MODIFY?'w':'-',
res->ACCESS3res_u.resok.access&ACCESS3_EXECUTE?'x':'-');
data->cb(-EACCES, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return;
}
nfsfh = malloc(sizeof(struct nfsfh));
if (nfsfh == NULL) {
rpc_set_error(nfs->rpc, "NFS: Failed to allocate nfsfh structure");
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return;
}
bzero(nfsfh, sizeof(struct nfsfh));
if (data->continue_int & O_SYNC) {
nfsfh->is_sync = 1;
}
/* steal the filehandle */
nfsfh->fh.data.data_len = data->fh.data.data_len;
nfsfh->fh.data.data_val = data->fh.data.data_val;
data->fh.data.data_val = NULL;
data->cb(0, nfs, nfsfh, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_open_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
int nfsmode = 0;
if (data->continue_int & O_WRONLY) {
nfsmode |= ACCESS3_MODIFY;
}
if (data->continue_int & O_RDWR) {
nfsmode |= ACCESS3_READ|ACCESS3_MODIFY;
}
if (!(data->continue_int & (O_WRONLY|O_RDWR))) {
nfsmode |= ACCESS3_READ;
}
if (rpc_nfs_access_async(nfs->rpc, nfs_open_cb, &data->fh, nfsmode, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send OPEN ACCESS call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_open_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data)
{
if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_open_continue_internal, NULL, NULL, mode) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -2;
}
return 0;
}
/*
* Async pread()
*/
static void nfs_pread_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
READ3res *res;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: Read failed with %s(%d)", 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;
}
data->nfsfh->offset += res->READ3res_u.resok.count;
data->cb(res->READ3res_u.resok.count, nfs, res->READ3res_u.resok.data.data_val, data->private_data);
free_nfs_cb_data(data);
}
int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, nfs_cb cb, void *private_data)
{
struct nfs_cb_data *data;
data = malloc(sizeof(struct nfs_cb_data));
if (data == NULL) {
rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
printf("failed to allocate memory for nfs cb data\n");
free_nfs_cb_data(data);
return -1;
}
bzero(data, sizeof(struct nfs_cb_data));
data->nfs = nfs;
data->cb = cb;
data->private_data = private_data;
data->nfsfh = nfsfh;
nfsfh->offset = offset;
if (rpc_nfs_read_async(nfs->rpc, nfs_pread_cb, &nfsfh->fh, offset, count, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send READ call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
/*
* Async read()
*/
int nfs_read_async(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, nfs_cb cb, void *private_data)
{
return nfs_pread_async(nfs, nfsfh, nfsfh->offset, count, cb, private_data);
}
/*
* Async pwrite()
*/
static void nfs_pwrite_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
WRITE3res *res;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: Write failed with %s(%d)", 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;
}
data->nfsfh->offset += res->WRITE3res_u.resok.count;
data->cb(res->WRITE3res_u.resok.count, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf, nfs_cb cb, void *private_data)
{
struct nfs_cb_data *data;
data = malloc(sizeof(struct nfs_cb_data));
if (data == NULL) {
rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
printf("failed to allocate memory for nfs cb data\n");
free_nfs_cb_data(data);
return -1;
}
bzero(data, sizeof(struct nfs_cb_data));
data->nfs = nfs;
data->cb = cb;
data->private_data = private_data;
data->nfsfh = nfsfh;
nfsfh->offset = offset;
if (rpc_nfs_write_async(nfs->rpc, nfs_pwrite_cb, &nfsfh->fh, buf, offset, count, nfsfh->is_sync?FILE_SYNC:UNSTABLE, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send WRITE call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
/*
* Async write()
*/
int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf, nfs_cb cb, void *private_data)
{
return nfs_pwrite_async(nfs, nfsfh, nfsfh->offset, count, buf, cb, private_data);
}
/*
* close
*/
int nfs_close_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data)
{
if (nfsfh->fh.data.data_val != NULL){
free(nfsfh->fh.data.data_val);
nfsfh->fh.data.data_val = NULL;
}
free(nfsfh);
cb(0, nfs, NULL, private_data);
return 0;
};
/*
* Async fstat()
*/
int nfs_fstat_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data)
{
struct nfs_cb_data *data;
data = malloc(sizeof(struct nfs_cb_data));
if (data == NULL) {
rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
printf("failed to allocate memory for nfs cb data\n");
free_nfs_cb_data(data);
return -1;
}
bzero(data, sizeof(struct nfs_cb_data));
data->nfs = nfs;
data->cb = cb;
data->private_data = private_data;
if (rpc_nfs_getattr_async(nfs->rpc, nfs_stat_1_cb, &nfsfh->fh, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send STAT GETATTR call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
/*
* Async fsync()
*/
static void nfs_fsync_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
COMMIT3res *res;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: Commit failed with %s(%d)", 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;
}
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
int nfs_fsync_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data)
{
struct nfs_cb_data *data;
data = malloc(sizeof(struct nfs_cb_data));
if (data == NULL) {
rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
printf("failed to allocate memory for nfs cb data\n");
free_nfs_cb_data(data);
return -1;
}
bzero(data, sizeof(struct nfs_cb_data));
data->nfs = nfs;
data->cb = cb;
data->private_data = private_data;
if (rpc_nfs_commit_async(nfs->rpc, nfs_fsync_cb, &nfsfh->fh, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send COMMIT call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
/*
* Async ftruncate()
*/
static void nfs_ftruncate_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
SETATTR3res *res;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: Setattr failed with %s(%d)", 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;
}
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
int nfs_ftruncate_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t length, nfs_cb cb, void *private_data)
{
struct nfs_cb_data *data;
SETATTR3args args;
data = malloc(sizeof(struct nfs_cb_data));
if (data == NULL) {
rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
printf("failed to allocate memory for nfs cb data\n");
free_nfs_cb_data(data);
return -1;
}
bzero(data, sizeof(struct nfs_cb_data));
data->nfs = nfs;
data->cb = cb;
data->private_data = private_data;
bzero(&args, sizeof(SETATTR3args));
args.object.data.data_len = nfsfh->fh.data.data_len;
args.object.data.data_val = nfsfh->fh.data.data_val;
args.new_attributes.size.set_it = 1;
args.new_attributes.size.set_size3_u.size = length;
if (rpc_nfs_setattr_async(nfs->rpc, nfs_ftruncate_cb, &args, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
/*
* Async truncate()
*/
static int nfs_truncate_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
off_t offset = data->continue_int;
struct nfsfh nfsfh;
nfsfh.fh.data.data_val = data->fh.data.data_val;
nfsfh.fh.data.data_len = data->fh.data.data_len;
if (nfs_ftruncate_async(nfs, &nfsfh, offset, data->cb, data->private_data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
free_nfs_cb_data(data);
return 0;
}
int nfs_truncate_async(struct nfs_context *nfs, const char *path, off_t length, nfs_cb cb, void *private_data)
{
off_t offset;
offset = length;
if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_truncate_continue_internal, NULL, NULL, offset) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -2;
}
return 0;
}
/*
* Async mkdir()
*/
static void nfs_mkdir_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
MKDIR3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
char *str = data->continue_data;
str = &str[strlen(str) + 1];
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: MKDIR 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;
}
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_mkdir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
char *str = data->continue_data;
str = &str[strlen(str) + 1];
if (rpc_nfs_mkdir_async(nfs->rpc, nfs_mkdir_cb, &data->fh, str, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send MKDIR call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_mkdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
{
char *new_path;
char *ptr;
new_path = strdup(path);
if (new_path == NULL) {
printf("Out of memory, failed to allocate mode buffer for path\n");
return -1;
}
ptr = rindex(new_path, '/');
if (ptr == NULL) {
printf("Invalid path %s\n", path);
return -2;
}
*ptr = 0;
/* new_path now points to the parent directory, and beyond the nul terminateor is the new directory to create */
if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_mkdir_continue_internal, new_path, free, 0) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -3;
}
return 0;
}
/*
* Async rmdir()
*/
static void nfs_rmdir_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
RMDIR3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
char *str = data->continue_data;
str = &str[strlen(str) + 1];
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: RMDIR 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;
}
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_rmdir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
char *str = data->continue_data;
str = &str[strlen(str) + 1];
if (rpc_nfs_rmdir_async(nfs->rpc, nfs_rmdir_cb, &data->fh, str, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send RMDIR call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
{
char *new_path;
char *ptr;
new_path = strdup(path);
if (new_path == NULL) {
printf("Out of memory, failed to allocate mode buffer for path\n");
return -1;
}
ptr = rindex(new_path, '/');
if (ptr == NULL) {
printf("Invalid path %s\n", path);
return -2;
}
*ptr = 0;
/* new_path now points to the parent directory, and beyond the nul terminateor is the new directory to create */
if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_rmdir_continue_internal, new_path, free, 0) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -3;
}
return 0;
}
/*
* Async creat()
*/
static void nfs_create_2_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
LOOKUP3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
struct nfsfh *nfsfh;
char *str = data->continue_data;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
str = &str[strlen(str) + 1];
res = command_data;
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);
return;
}
nfsfh = malloc(sizeof(struct nfsfh));
if (nfsfh == NULL) {
rpc_set_error(nfs->rpc, "NFS: Failed to allocate nfsfh structure");
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return;
}
bzero(nfsfh, sizeof(struct nfsfh));
/* steal the filehandle */
nfsfh->fh.data.data_len = data->fh.data.data_len;
nfsfh->fh.data.data_val = data->fh.data.data_val;
data->fh.data.data_val = NULL;
data->cb(0, nfs, nfsfh, data->private_data);
free_nfs_cb_data(data);
}
static void nfs_creat_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
CREATE3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
char *str = data->continue_data;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
str = &str[strlen(str) + 1];
res = command_data;
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);
return;
}
if (rpc_nfs_lookup_async(nfs->rpc, nfs_create_2_cb, &data->fh, str, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send lookup call for %s/%s", data->saved_path, str);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return;
}
return;
}
static int nfs_creat_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
char *str = data->continue_data;
str = &str[strlen(str) + 1];
if (rpc_nfs_create_async(nfs->rpc, nfs_creat_1_cb, &data->fh, str, data->continue_int, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send CREATE call for %s/%s", data->path, str);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data)
{
char *new_path;
char *ptr;
new_path = strdup(path);
if (new_path == NULL) {
printf("Out of memory, failed to allocate mode buffer for path\n");
return -1;
}
ptr = rindex(new_path, '/');
if (ptr == NULL) {
printf("Invalid path %s\n", path);
return -2;
}
*ptr = 0;
/* new_path now points to the parent directory, and beyond the nul terminateor is the new directory to create */
if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_creat_continue_internal, new_path, free, mode) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -3;
}
return 0;
}
/*
* Async unlink()
*/
static void nfs_unlink_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
REMOVE3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
char *str = data->continue_data;
str = &str[strlen(str) + 1];
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: REMOVE 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;
}
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_unlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
char *str = data->continue_data;
str = &str[strlen(str) + 1];
if (rpc_nfs_remove_async(nfs->rpc, nfs_unlink_cb, &data->fh, str, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send REMOVE call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_unlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
{
char *new_path;
char *ptr;
new_path = strdup(path);
if (new_path == NULL) {
printf("Out of memory, failed to allocate mode buffer for path\n");
return -1;
}
ptr = rindex(new_path, '/');
if (ptr == NULL) {
printf("Invalid path %s\n", path);
return -2;
}
*ptr = 0;
/* new_path now points to the parent directory, and beyond the nul terminateor is the new directory to create */
if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_unlink_continue_internal, new_path, free, 0) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -3;
}
return 0;
}
/*
* Async opendir()
*/
static void nfs_opendir_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
READDIR3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
struct nfsdir *nfsdir = data->continue_data;;
struct entry3 *entry;
uint64_t cookie;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
nfs_free_nfsdir(nfsdir);
data->continue_data = NULL;
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
nfs_free_nfsdir(nfsdir);
data->continue_data = NULL;
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: READDIR of %s failed with %s(%d)", data->saved_path, 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);
nfs_free_nfsdir(nfsdir);
data->continue_data = NULL;
free_nfs_cb_data(data);
return;
}
entry =res->READDIR3res_u.resok.reply.entries;
while (entry != NULL) {
struct nfsdirent *nfsdirent;
nfsdirent = malloc(sizeof(struct nfsdirent));
if (nfsdirent == NULL) {
data->cb(-ENOMEM, nfs, "Failed to allocate dirent", data->private_data);
nfs_free_nfsdir(nfsdir);
data->continue_data = NULL;
free_nfs_cb_data(data);
return;
}
bzero(nfsdirent, sizeof(struct nfsdirent));
nfsdirent->name = strdup(entry->name);
if (nfsdirent->name == NULL) {
data->cb(-ENOMEM, nfs, "Failed to allocate dirent->name", data->private_data);
nfs_free_nfsdir(nfsdir);
data->continue_data = NULL;
free_nfs_cb_data(data);
return;
}
nfsdirent->inode = entry->fileid;
nfsdirent->next = nfsdir->entries;
nfsdir->entries = nfsdirent;
cookie = entry->cookie;
entry = entry->nextentry;
}
if (res->READDIR3res_u.resok.reply.eof == 0) {
if (rpc_nfs_readdir_async(nfs->rpc, nfs_opendir_cb, &data->fh, cookie, res->READDIR3res_u.resok.cookieverf, 20000, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIR call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
nfs_free_nfsdir(nfsdir);
data->continue_data = NULL;
free_nfs_cb_data(data);
return;
}
return;
}
/* steal the dirhandle */
data->continue_data = NULL;
nfsdir->current = nfsdir->entries;
data->cb(0, nfs, nfsdir, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_opendir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
cookieverf3 cv;
bzero(cv, sizeof(cookieverf3));
if (rpc_nfs_readdir_async(nfs->rpc, nfs_opendir_cb, &data->fh, 0, (char *)&cv, 20000, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIR call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_opendir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
{
struct nfsdir *nfsdir;
nfsdir = malloc(sizeof(struct nfsdir));
if (nfsdir == NULL) {
printf("failed to allocate buffer for nfsdir\n");
return -1;
}
bzero(nfsdir, sizeof(struct nfsdir));
if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_opendir_continue_internal, nfsdir, free, 0) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -2;
}
return 0;
}
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;
}
void nfs_closedir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir)
{
nfs_free_nfsdir(nfsdir);
}
/*
* Async lseek()
*/
struct lseek_cb_data {
struct nfs_context *nfs;
struct nfsfh *nfsfh;
off_t offset;
nfs_cb cb;
void *private_data;
};
static void nfs_lseek_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
GETATTR3res *res;
struct lseek_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: GETATTR failed with %s(%d)", 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(data);
return;
}
data->nfsfh->offset = data->offset + res->GETATTR3res_u.resok.obj_attributes.size;
data->cb(0, nfs, &data->nfsfh->offset, data->private_data);
free(data);
}
int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, int whence, nfs_cb cb, void *private_data)
{
struct lseek_cb_data *data;
if (whence == SEEK_SET) {
nfsfh->offset = offset;
cb(0, nfs, &nfsfh->offset, private_data);
return 0;
}
if (whence == SEEK_CUR) {
nfsfh->offset += offset;
cb(0, nfs, &nfsfh->offset, private_data);
return 0;
}
data = malloc(sizeof(struct lseek_cb_data));
if (data == NULL) {
rpc_set_error(nfs->rpc, "Out Of Memory: Failed to malloc lseek cb data");
return -1;
}
data->nfs = nfs;
data->nfsfh = nfsfh;
data->offset = offset;
data->cb = cb;
data->private_data = private_data;
if (rpc_nfs_getattr_async(nfs->rpc, nfs_lseek_1_cb, &nfsfh->fh, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send LSEEK GETATTR call");
free(data);
return -2;
}
return 0;
}
/*
* Async statvfs()
*/
static void nfs_statvfs_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
FSSTAT3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
struct statvfs svfs;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: FSSTAT of %s failed with %s(%d)", data->saved_path, 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;
}
svfs.f_bsize = 4096;
svfs.f_frsize = 4096;
svfs.f_blocks = res->FSSTAT3res_u.resok.tbytes/4096;
svfs.f_bfree = res->FSSTAT3res_u.resok.fbytes/4096;
svfs.f_bavail = res->FSSTAT3res_u.resok.abytes/4096;
svfs.f_files = res->FSSTAT3res_u.resok.tfiles;
svfs.f_ffree = res->FSSTAT3res_u.resok.ffiles;
svfs.f_favail = res->FSSTAT3res_u.resok.afiles;
svfs.f_fsid = 0;
svfs.f_flag = 0;
svfs.f_namemax = 256;
data->cb(0, nfs, &svfs, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_statvfs_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
if (rpc_nfs_fsstat_async(nfs->rpc, nfs_statvfs_1_cb, &data->fh, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send FSSTAT call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_statvfs_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
{
if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_statvfs_continue_internal, NULL, NULL, 0) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -1;
}
return 0;
}
/*
* Async readlink()
*/
static void nfs_readlink_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
READLINK3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: READLINK of %s failed with %s(%d)", data->saved_path, 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;
}
data->cb(0, nfs, res->READLINK3res_u.resok.data, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_readlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
if (rpc_nfs_readlink_async(nfs->rpc, nfs_readlink_1_cb, &data->fh, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send READLINK call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_readlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
{
if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_readlink_continue_internal, NULL, NULL, 0) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -1;
}
return 0;
}
/*
* Async chmod()
*/
static void nfs_chmod_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
SETATTR3res *res;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: SETATTR failed with %s(%d)", 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;
}
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_chmod_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
SETATTR3args args;
bzero(&args, sizeof(SETATTR3args));
args.object.data.data_len = data->fh.data.data_len;
args.object.data.data_val = data->fh.data.data_val;
args.new_attributes.mode.set_it = 1;
args.new_attributes.mode.set_mode3_u.mode = data->continue_int;
if (rpc_nfs_setattr_async(nfs->rpc, nfs_chmod_cb, &args, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_chmod_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data)
{
if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_chmod_continue_internal, NULL, NULL, mode) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -1;
}
return 0;
}
/*
* Async fchmod()
*/
int nfs_fchmod_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode, nfs_cb cb, void *private_data)
{
struct nfs_cb_data *data;
data = malloc(sizeof(struct nfs_cb_data));
if (data == NULL) {
rpc_set_error(nfs->rpc, "out of memory");
printf("failed to allocate memory for nfs mount data\n");
return -1;
}
bzero(data, sizeof(struct nfs_cb_data));
data->nfs = nfs;
data->cb = cb;
data->private_data = private_data;
data->continue_int = mode;
data->fh.data.data_len = nfsfh->fh.data.data_len;
data->fh.data.data_val = malloc(data->fh.data.data_len);
if (data->fh.data.data_val == NULL) {
rpc_set_error(nfs->rpc, "Out of memory: Failed to allocate fh");
free_nfs_cb_data(data);
return -1;
}
memcpy(data->fh.data.data_val, nfsfh->fh.data.data_val, data->fh.data.data_len);
if (nfs_chmod_continue_internal(nfs, data) != 0) {
free_nfs_cb_data(data);
return -1;
}
return 0;
}
/*
* Async chown()
*/
static void nfs_chown_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
SETATTR3res *res;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: SETATTR failed with %s(%d)", 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;
}
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
struct nfs_chown_data {
uid_t uid;
gid_t gid;
};
static int nfs_chown_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
SETATTR3args args;
struct nfs_chown_data *chown_data = data->continue_data;
bzero(&args, sizeof(SETATTR3args));
args.object.data.data_len = data->fh.data.data_len;
args.object.data.data_val = data->fh.data.data_val;
if (chown_data->uid != (uid_t)-1) {
args.new_attributes.uid.set_it = 1;
args.new_attributes.uid.set_uid3_u.uid = chown_data->uid;
}
if (chown_data->gid != (gid_t)-1) {
args.new_attributes.gid.set_it = 1;
args.new_attributes.gid.set_gid3_u.gid = chown_data->gid;
}
if (rpc_nfs_setattr_async(nfs->rpc, nfs_chown_cb, &args, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_chown_async(struct nfs_context *nfs, const char *path, int uid, int gid, nfs_cb cb, void *private_data)
{
struct nfs_chown_data *chown_data;
chown_data = malloc(sizeof(struct nfs_chown_data));
if (chown_data == NULL) {
printf("Failed to allocate memory for chown data structure\n");
return -1;
}
chown_data->uid = uid;
chown_data->gid = gid;
if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_chown_continue_internal, chown_data, free, 0) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -1;
}
return 0;
}
/*
* Async fchown()
*/
int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int gid, nfs_cb cb, void *private_data)
{
struct nfs_cb_data *data;
struct nfs_chown_data *chown_data;
chown_data = malloc(sizeof(struct nfs_chown_data));
if (chown_data == NULL) {
printf("Failed to allocate memory for chown data structure\n");
return -1;
}
chown_data->uid = uid;
chown_data->gid = gid;
data = malloc(sizeof(struct nfs_cb_data));
if (data == NULL) {
rpc_set_error(nfs->rpc, "out of memory");
printf("failed to allocate memory for fchown data\n");
return -1;
}
bzero(data, sizeof(struct nfs_cb_data));
data->nfs = nfs;
data->cb = cb;
data->private_data = private_data;
data->continue_data = chown_data;
data->fh.data.data_len = nfsfh->fh.data.data_len;
data->fh.data.data_val = malloc(data->fh.data.data_len);
if (data->fh.data.data_val == NULL) {
rpc_set_error(nfs->rpc, "Out of memory: Failed to allocate fh");
free_nfs_cb_data(data);
return -1;
}
memcpy(data->fh.data.data_val, nfsfh->fh.data.data_val, data->fh.data.data_len);
if (nfs_chown_continue_internal(nfs, data) != 0) {
free_nfs_cb_data(data);
return -1;
}
return 0;
}
/*
* Async utimes()
*/
static void nfs_utimes_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
SETATTR3res *res;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: SETATTR failed with %s(%d)", 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;
}
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_utimes_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
SETATTR3args args;
struct timeval *utimes_data = data->continue_data;
bzero(&args, sizeof(SETATTR3args));
args.object.data.data_len = data->fh.data.data_len;
args.object.data.data_val = data->fh.data.data_val;
if (utimes_data != NULL) {
args.new_attributes.atime.set_it = SET_TO_CLIENT_TIME;
args.new_attributes.atime.set_atime_u.atime.seconds = utimes_data[0].tv_sec;
args.new_attributes.atime.set_atime_u.atime.nseconds = utimes_data[0].tv_usec * 1000;
args.new_attributes.mtime.set_it = SET_TO_CLIENT_TIME;
args.new_attributes.mtime.set_mtime_u.mtime.seconds = utimes_data[1].tv_sec;
args.new_attributes.mtime.set_mtime_u.mtime.nseconds = utimes_data[1].tv_usec * 1000;
} else {
args.new_attributes.atime.set_it = SET_TO_SERVER_TIME;
args.new_attributes.mtime.set_it = SET_TO_SERVER_TIME;
}
if (rpc_nfs_setattr_async(nfs->rpc, nfs_utimes_cb, &args, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_utimes_async(struct nfs_context *nfs, const char *path, struct timeval *times, nfs_cb cb, void *private_data)
{
struct timeval *new_times = NULL;
if (times != NULL) {
new_times = malloc(sizeof(struct timeval)*2);
if (new_times == NULL) {
printf("Failed to allocate memory for timeval structure\n");
return -1;
}
memcpy(new_times, times, sizeof(struct timeval)*2);
}
if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_utimes_continue_internal, new_times, free, 0) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -1;
}
return 0;
}
/*
* Async utime()
*/
int nfs_utime_async(struct nfs_context *nfs, const char *path, struct utimbuf *times, nfs_cb cb, void *private_data)
{
struct timeval *new_times = NULL;
if (times != NULL) {
new_times = malloc(sizeof(struct timeval)*2);
if (new_times == NULL) {
printf("Failed to allocate memory for timeval structure\n");
return -1;
}
new_times[0].tv_sec = times->actime;
new_times[0].tv_usec = 0;
new_times[1].tv_sec = times->modtime;
new_times[1].tv_usec = 0;
}
if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_utimes_continue_internal, new_times, free, 0) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -1;
}
return 0;
}
/*
* Async access()
*/
static void nfs_access_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
ACCESS3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
unsigned int nfsmode = 0;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: ACCESS of %s failed with %s(%d)", data->saved_path, 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;
}
if (data->continue_int & R_OK) {
nfsmode |= ACCESS3_READ;
}
if (data->continue_int & W_OK) {
nfsmode |= ACCESS3_MODIFY;
}
if (data->continue_int & X_OK) {
nfsmode |= ACCESS3_EXECUTE;
}
if (res->ACCESS3res_u.resok.access != nfsmode) {
rpc_set_error(nfs->rpc, "NFS: ACCESS denied. Required access %c%c%c. Allowed access %c%c%c",
nfsmode&ACCESS3_READ?'r':'-',
nfsmode&ACCESS3_MODIFY?'w':'-',
nfsmode&ACCESS3_EXECUTE?'x':'-',
res->ACCESS3res_u.resok.access&ACCESS3_READ?'r':'-',
res->ACCESS3res_u.resok.access&ACCESS3_MODIFY?'w':'-',
res->ACCESS3res_u.resok.access&ACCESS3_EXECUTE?'x':'-');
data->cb(-EACCES, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return;
}
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_access_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
int nfsmode = 0;
if (data->continue_int & R_OK) {
nfsmode |= ACCESS3_READ;
}
if (data->continue_int & W_OK) {
nfsmode |= ACCESS3_MODIFY;
}
if (data->continue_int & X_OK) {
nfsmode |= ACCESS3_EXECUTE;
}
if (rpc_nfs_access_async(nfs->rpc, nfs_access_cb, &data->fh, nfsmode, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send OPEN ACCESS call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_access_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data)
{
if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_access_continue_internal, NULL, NULL, mode) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -2;
}
return 0;
}
/*
* Async symlink()
*/
struct nfs_symlink_data {
char *oldpath;
char *newpathparent;
char *newpathobject;
};
static void free_nfs_symlink_data(void *mem)
{
struct nfs_symlink_data *data = mem;
if (data->oldpath != NULL) {
free(data->oldpath);
}
if (data->newpathparent != NULL) {
free(data->newpathparent);
}
if (data->newpathobject != NULL) {
free(data->newpathobject);
}
free(data);
}
static void nfs_symlink_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
SYMLINK3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
struct nfs_symlink_data *symlink_data = data->continue_data;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: SYMLINK %s/%s -> %s failed with %s(%d)", symlink_data->newpathparent, symlink_data->newpathobject, symlink_data->oldpath, 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;
}
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_symlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
struct nfs_symlink_data *symlink_data = data->continue_data;
if (rpc_nfs_symlink_async(nfs->rpc, nfs_symlink_cb, &data->fh, symlink_data->newpathobject, symlink_data->oldpath, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send SYMLINK call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
int nfs_symlink_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data)
{
char *ptr;
struct nfs_symlink_data *symlink_data;
symlink_data = malloc(sizeof(struct nfs_symlink_data));
if (symlink_data == NULL) {
printf("Out of memory, failed to allocate buffer for symlink data\n");
return -1;
}
bzero(symlink_data, sizeof(struct nfs_symlink_data));
symlink_data->oldpath = strdup(oldpath);
if (symlink_data->oldpath == NULL) {
printf("Out of memory, failed to allocate buffer for oldpath\n");
free_nfs_symlink_data(symlink_data);
return -2;
}
symlink_data->newpathparent = strdup(newpath);
if (symlink_data->newpathparent == NULL) {
printf("Out of memory, failed to allocate mode buffer for new path\n");
free_nfs_symlink_data(symlink_data);
return -3;
}
ptr = rindex(symlink_data->newpathparent, '/');
if (ptr == NULL) {
printf("Invalid path %s\n", oldpath);
free_nfs_symlink_data(symlink_data);
return -4;
}
*ptr = 0;
ptr++;
symlink_data->newpathobject = strdup(ptr);
if (symlink_data->newpathobject == NULL) {
printf("Out of memory, failed to allocate mode buffer for new path\n");
free_nfs_symlink_data(symlink_data);
return -5;
}
if (nfs_lookuppath_async(nfs, symlink_data->newpathparent, cb, private_data, nfs_symlink_continue_internal, symlink_data, free_nfs_symlink_data, 0) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -6;
}
return 0;
}
/*
* Async rename()
*/
struct nfs_rename_data {
char *oldpath;
char *oldobject;
struct nfs_fh3 olddir;
char *newpath;
char *newobject;
struct nfs_fh3 newdir;
};
static void free_nfs_rename_data(void *mem)
{
struct nfs_rename_data *data = mem;
if (data->oldpath != NULL) {
free(data->oldpath);
}
if (data->olddir.data.data_val != NULL) {
free(data->olddir.data.data_val);
}
if (data->newpath != NULL) {
free(data->newpath);
}
if (data->newdir.data.data_val != NULL) {
free(data->newdir.data.data_val);
}
free(data);
}
static void nfs_rename_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
RENAME3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
struct nfs_rename_data *rename_data = data->continue_data;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: RENAME %s/%s -> %s/%s failed with %s(%d)", rename_data->oldpath, rename_data->oldobject, rename_data->newpath, rename_data->newobject, 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;
}
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_rename_continue_2_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
struct nfs_rename_data *rename_data = data->continue_data;
/* steal the filehandle */
rename_data->newdir.data.data_len = data->fh.data.data_len;
rename_data->newdir.data.data_val = data->fh.data.data_val;
data->fh.data.data_val = NULL;
if (rpc_nfs_rename_async(nfs->rpc, nfs_rename_cb, &rename_data->olddir, rename_data->oldobject, &rename_data->newdir, rename_data->newobject, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send RENAME call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
static int nfs_rename_continue_1_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
struct nfs_rename_data *rename_data = data->continue_data;
/* steal the filehandle */
rename_data->olddir.data.data_len = data->fh.data.data_len;
rename_data->olddir.data.data_val = data->fh.data.data_val;
data->fh.data.data_val = NULL;
if (nfs_lookuppath_async(nfs, rename_data->newpath, data->cb, data->private_data, nfs_rename_continue_2_internal, rename_data, free_nfs_rename_data, 0) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send LOOKUP call for %s", rename_data->newpath);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
data->continue_data = NULL;
free_nfs_cb_data(data);
return 0;
}
int nfs_rename_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data)
{
char *ptr;
struct nfs_rename_data *rename_data;
rename_data = malloc(sizeof(struct nfs_rename_data));
if (rename_data == NULL) {
printf("Out of memory, failed to allocate buffer for rename data\n");
return -1;
}
bzero(rename_data, sizeof(struct nfs_rename_data));
rename_data->oldpath = strdup(oldpath);
if (rename_data->oldpath == NULL) {
printf("Out of memory, failed to allocate buffer for oldpath\n");
free_nfs_rename_data(rename_data);
return -2;
}
ptr = rindex(rename_data->oldpath, '/');
if (ptr == NULL) {
printf("Invalid path %s\n", oldpath);
free_nfs_rename_data(rename_data);
return -3;
}
*ptr = 0;
ptr++;
rename_data->oldobject = ptr;
rename_data->newpath = strdup(newpath);
if (rename_data->newpath == NULL) {
printf("Out of memory, failed to allocate buffer for newpath\n");
free_nfs_rename_data(rename_data);
return -4;
}
ptr = rindex(rename_data->newpath, '/');
if (ptr == NULL) {
printf("Invalid path %s\n", newpath);
free_nfs_rename_data(rename_data);
return -5;
}
*ptr = 0;
ptr++;
rename_data->newobject = ptr;
if (nfs_lookuppath_async(nfs, rename_data->oldpath, cb, private_data, nfs_rename_continue_1_internal, rename_data, free_nfs_rename_data, 0) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -6;
}
return 0;
}
/*
* Async link()
*/
struct nfs_link_data {
char *oldpath;
struct nfs_fh3 oldfh;
char *newpath;
char *newobject;
struct nfs_fh3 newdir;
};
static void free_nfs_link_data(void *mem)
{
struct nfs_link_data *data = mem;
if (data->oldpath != NULL) {
free(data->oldpath);
}
if (data->oldfh.data.data_val != NULL) {
free(data->oldfh.data.data_val);
}
if (data->newpath != NULL) {
free(data->newpath);
}
if (data->newdir.data.data_val != NULL) {
free(data->newdir.data.data_val);
}
free(data);
}
static void nfs_link_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
{
LINK3res *res;
struct nfs_cb_data *data = private_data;
struct nfs_context *nfs = data->nfs;
struct nfs_link_data *link_data = data->continue_data;
if (status == RPC_STATUS_ERROR) {
data->cb(-EFAULT, nfs, command_data, data->private_data);
free_nfs_cb_data(data);
return;
}
if (status == RPC_STATUS_CANCEL) {
data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
free_nfs_cb_data(data);
return;
}
res = command_data;
if (res->status != NFS3_OK) {
rpc_set_error(nfs->rpc, "NFS: LINK %s -> %s/%s failed with %s(%d)", link_data->oldpath, link_data->newpath, link_data->newobject, 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;
}
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
}
static int nfs_link_continue_2_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
struct nfs_link_data *link_data = data->continue_data;
/* steal the filehandle */
link_data->newdir.data.data_len = data->fh.data.data_len;
link_data->newdir.data.data_val = data->fh.data.data_val;
data->fh.data.data_val = NULL;
if (rpc_nfs_link_async(nfs->rpc, nfs_link_cb, &link_data->oldfh, &link_data->newdir, link_data->newobject, data) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send LINK call for %s", data->path);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
return 0;
}
static int nfs_link_continue_1_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
struct nfs_link_data *link_data = data->continue_data;
/* steal the filehandle */
link_data->oldfh.data.data_len = data->fh.data.data_len;
link_data->oldfh.data.data_val = data->fh.data.data_val;
data->fh.data.data_val = NULL;
if (nfs_lookuppath_async(nfs, link_data->newpath, data->cb, data->private_data, nfs_link_continue_2_internal, link_data, free_nfs_link_data, 0) != 0) {
rpc_set_error(nfs->rpc, "RPC error: Failed to send LOOKUP call for %s", link_data->newpath);
data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
free_nfs_cb_data(data);
return -1;
}
data->continue_data = NULL;
free_nfs_cb_data(data);
return 0;
}
int nfs_link_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data)
{
char *ptr;
struct nfs_link_data *link_data;
link_data = malloc(sizeof(struct nfs_link_data));
if (link_data == NULL) {
printf("Out of memory, failed to allocate buffer for link data\n");
return -1;
}
bzero(link_data, sizeof(struct nfs_link_data));
link_data->oldpath = strdup(oldpath);
if (link_data->oldpath == NULL) {
printf("Out of memory, failed to allocate buffer for oldpath\n");
free_nfs_link_data(link_data);
return -2;
}
link_data->newpath = strdup(newpath);
if (link_data->newpath == NULL) {
printf("Out of memory, failed to allocate buffer for newpath\n");
free_nfs_link_data(link_data);
return -4;
}
ptr = rindex(link_data->newpath, '/');
if (ptr == NULL) {
printf("Invalid path %s\n", newpath);
free_nfs_link_data(link_data);
return -5;
}
*ptr = 0;
ptr++;
link_data->newobject = ptr;
if (nfs_lookuppath_async(nfs, link_data->oldpath, cb, private_data, nfs_link_continue_1_internal, link_data, free_nfs_link_data, 0) != 0) {
printf("Out of memory: failed to start parsing the path components\n");
return -6;
}
return 0;
}
//qqq replace later with lseek()
off_t nfs_get_current_offset(struct nfsfh *nfsfh)
{
return nfsfh->offset;
}
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* This is the highlevel interface to access NFS resources using a posix-like interface
*/
#include <stdint.h>
struct nfs_context;
/*
* Used for interfacing the async version of the api into an external eventsystem
*/
int nfs_get_fd(struct nfs_context *nfs);
int nfs_which_events(struct nfs_context *nfs);
int nfs_service(struct nfs_context *nfs, int revents);
/*
* Used if you need different credentials than the default for the current user.
*/
struct AUTH;
void nfs_set_auth(struct nfs_context *nfs, struct AUTH *auth);
/*
* When an operation failed, this function can extract a detailed error string.
*/
char *nfs_get_error(struct nfs_context *nfs);
/*
* Callback for all async nfs functions
*/
typedef void (*nfs_cb)(int err, struct nfs_context *nfs, void *data, void *private_data);
/*
* NFS CONTEXT.
*/
/*
* Create an NFS context.
* Function returns
* NULL : Failed to create a context.
* struct nfs_context * : A pointer to an nfs context.
*/
struct nfs_context *nfs_init_context(void);
/*
* Destroy an nfs context.
*/
void nfs_destroy_context(struct nfs_context *nfs);
/*
* A libnfs filehandle
*/
struct nfsfh;
/*
* MOUNT THE EXPORT
*/
/*
* Async nfs mount.
* This function will try to connect to the server and mount the export.
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL
* -errno : An error occured.
* data is the error string.
*/
int nfs_mount_async(struct nfs_context *nfs, const char *server, const char *export, nfs_cb cb, void *private_data);
/*
* Sync nfs mount.
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_mount_sync(struct nfs_context *nfs, const char *server, const char *export);
/*
* STAT()
*/
/*
* Async stat(<filename>)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is struct stat *
* -errno : An error occured.
* data is the error string.
*/
struct stat;
int nfs_stat_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
/*
* Sync stat(<filename>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_stat_sync(struct nfs_context *nfs, const char *path, struct stat *st);
/*
* FSTAT()
*/
/*
* Async fstat(nfsfh *)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is struct stat *
* -errno : An error occured.
* data is the error string.
*/
int nfs_fstat_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data);
/*
* Sync fstat(nfsfh *)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_fstat_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *st);
/*
* OPEN()
*/
/*
* Async open(<filename>)
*
* mode is a combination of the flags : O_RDOLNY, O_WRONLY, O_RDWR , O_SYNC
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is a struct *nfsfh;
* The nfsfh is close using nfs_close().
* -errno : An error occured.
* data is the error string.
*/
int nfs_open_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data);
/*
* Sync open(<filename>)
* Function returns
* 0 : The operation was successfull. *nfsfh is filled in.
* -errno : The command failed.
*/
int nfs_open_sync(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh);
/*
* CLOSE
*/
/*
* Async close(nfsfh)
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL.
* -errno : An error occured.
* data is the error string.
*/
int nfs_close_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data);
/*
* Sync close(nfsfh)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_close_sync(struct nfs_context *nfs, struct nfsfh *nfsfh);
/*
* PREAD()
*/
/*
* Async pread()
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* >=0 : Success.
* status is numer of bytes read.
* data is a pointer to the returned data.
* -errno : An error occured.
* data is the error string.
*/
int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, nfs_cb cb, void *private_data);
/*
* Sync pread()
* Function returns
* >=0 : numer of bytes read.
* -errno : An error occured.
*/
int nfs_pread_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf);
/*
* READ()
*/
/*
* Async read()
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* >=0 : Success.
* status is numer of bytes read.
* data is a pointer to the returned data.
* -errno : An error occured.
* data is the error string.
*/
int nfs_read_async(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, nfs_cb cb, void *private_data);
/*
* Sync read()
* Function returns
* >=0 : numer of bytes read.
* -errno : An error occured.
*/
int nfs_read_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf);
/*
* PWRITE()
*/
/*
* Async pwrite()
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* >=0 : Success.
* status is numer of bytes written.
* -errno : An error occured.
* data is the error string.
*/
int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf, nfs_cb cb, void *private_data);
/*
* Sync pwrite()
* Function returns
* >=0 : numer of bytes written.
* -errno : An error occured.
*/
int nfs_pwrite_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf);
/*
* WRITE()
*/
/*
* Async write()
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* >=0 : Success.
* status is numer of bytes written.
* -errno : An error occured.
* data is the error string.
*/
int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf, nfs_cb cb, void *private_data);
/*
* Sync write()
* Function returns
* >=0 : numer of bytes written.
* -errno : An error occured.
*/
int nfs_write_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf);
/*
* LSEEK()
*/
/*
* Async lseek()
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* >=0 : Success.
* data is off_t * for the current position.
* -errno : An error occured.
* data is the error string.
*/
int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, int whence, nfs_cb cb, void *private_data);
/*
* Sync lseek()
* Function returns
* >=0 : numer of bytes read.
* -errno : An error occured.
*/
int nfs_lseek_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, int whence, off_t *current_offset);
/*
* FSYNC()
*/
/*
* Async fsync()
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* -errno : An error occured.
* data is the error string.
*/
int nfs_fsync_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data);
/*
* Sync fsync()
* Function returns
* 0 : Success
* -errno : An error occured.
*/
int nfs_fsync_sync(struct nfs_context *nfs, struct nfsfh *nfsfh);
/*
* TRUNCATE()
*/
/*
* Async truncate()
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* -errno : An error occured.
* data is the error string.
*/
int nfs_truncate_async(struct nfs_context *nfs, const char *path, off_t length, nfs_cb cb, void *private_data);
/*
* Sync truncate()
* Function returns
* 0 : Success
* -errno : An error occured.
*/
int nfs_truncate_sync(struct nfs_context *nfs, const char *path, off_t length);
/*
* FTRUNCATE()
*/
/*
* Async ftruncate()
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* -errno : An error occured.
* data is the error string.
*/
int nfs_ftruncate_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t length, nfs_cb cb, void *private_data);
/*
* Sync ftruncate()
* Function returns
* 0 : Success
* -errno : An error occured.
*/
int nfs_ftruncate_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t length);
/*
* MKDIR()
*/
/*
* Async mkdir()
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* -errno : An error occured.
* data is the error string.
*/
int nfs_mkdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
/*
* Sync mkdir()
* Function returns
* 0 : Success
* -errno : An error occured.
*/
int nfs_mkdir_sync(struct nfs_context *nfs, const char *path);
/*
* RMDIR()
*/
/*
* Async rmdir()
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* -errno : An error occured.
* data is the error string.
*/
int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
/*
* Sync rmdir()
* Function returns
* 0 : Success
* -errno : An error occured.
*/
int nfs_rmdir_sync(struct nfs_context *nfs, const char *path);
/*
* CREAT()
*/
/*
* Async creat()
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is a struct *nfsfh;
* -errno : An error occured.
* data is the error string.
*/
int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data);
/*
* Sync creat()
* Function returns
* 0 : Success
* -errno : An error occured.
*/
int nfs_creat_sync(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh);
/*
* UNLINK()
*/
/*
* Async unlink()
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL
* -errno : An error occured.
* data is the error string.
*/
int nfs_unlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
/*
* Sync unlink()
* Function returns
* 0 : Success
* -errno : An error occured.
*/
int nfs_unlink_sync(struct nfs_context *nfs, const char *path);
/*
* OPENDIR()
*/
struct nfsdir;
/*
* Async opendir()
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When struct nfsdir * is returned, this resource is closed/freed by calling nfs_closedir()
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is struct nfsdir *
* -errno : An error occured.
* data is the error string.
*/
int nfs_opendir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
/*
* Sync opendir()
* Function returns
* 0 : Success
* -errno : An error occured.
*/
int nfs_opendir_sync(struct nfs_context *nfs, const char *path, struct nfsdir **nfsdir);
/*
* READDIR()
*/
struct nfsdirent {
struct nfsdirent *next;
char *name;
uint64_t inode;
};
/*
* nfs_readdir() never blocks, so no special sync/async versions are available
*/
struct nfsdirent *nfs_readdir(struct nfs_context *nfs, struct nfsdir *nfsdir);
/*
* READDIR()
*/
/*
* nfs_closedir() never blocks, so no special sync/async versions are available
*/
void nfs_closedir(struct nfs_context *nfs, struct nfsdir *nfsdir);
/*
* STATVFS()
*/
/*
* Async statvfs(<dirname>)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is struct statvfs *
* -errno : An error occured.
* data is the error string.
*/
struct statvfs;
int nfs_statvfs_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
/*
* Sync statvfs(<dirname>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_statvfs_sync(struct nfs_context *nfs, const char *path, struct statvfs *svfs);
/*
* READLINK()
*/
/*
* Async readlink(<name>)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is a char *
* data is only valid during the callback and is automatically freed when the callback returns.
* -errno : An error occured.
* data is the error string.
*/
struct statvfs;
int nfs_readlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
/*
* Sync readlink(<name>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_readlink_sync(struct nfs_context *nfs, const char *path, char *buf, int bufsize);
/*
* CHMOD()
*/
/*
* Async chmod(<name>)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL
* -errno : An error occured.
* data is the error string.
*/
int nfs_chmod_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data);
/*
* Sync chmod(<name>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_chmod_sync(struct nfs_context *nfs, const char *path, int mode);
/*
* FCHMOD()
*/
/*
* Async fchmod(<handle>)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL
* -errno : An error occured.
* data is the error string.
*/
int nfs_fchmod_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode, nfs_cb cb, void *private_data);
/*
* Sync fchmod(<handle>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_fchmod_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode);
/*
* CHOWN()
*/
/*
* Async chown(<name>)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL
* -errno : An error occured.
* data is the error string.
*/
int nfs_chown_async(struct nfs_context *nfs, const char *path, int uid, int gid, nfs_cb cb, void *private_data);
/*
* Sync chown(<name>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_chown_sync(struct nfs_context *nfs, const char *path, int uid, int gid);
/*
* FCHOWN()
*/
/*
* Async fchown(<handle>)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL
* -errno : An error occured.
* data is the error string.
*/
int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int gid, nfs_cb cb, void *private_data);
/*
* Sync fchown(<handle>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_fchown_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int gid);
/*
* UTIMES()
*/
/*
* Async utimes(<path>)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL
* -errno : An error occured.
* data is the error string.
*/
int nfs_utimes_async(struct nfs_context *nfs, const char *path, struct timeval *times, nfs_cb cb, void *private_data);
/*
* Sync utimes(<path>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_utimes_sync(struct nfs_context *nfs, const char *path, struct timeval *times);
/*
* UTIME()
*/
/*
* Async utime(<path>)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL
* -errno : An error occured.
* data is the error string.
*/
struct utimbuf;
int nfs_utime_async(struct nfs_context *nfs, const char *path, struct utimbuf *times, nfs_cb cb, void *private_data);
/*
* Sync utime(<path>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_utime_sync(struct nfs_context *nfs, const char *path, struct utimbuf *times);
/*
* ACCESS()
*/
/*
* Async access(<path>)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL
* -errno : An error occured.
* data is the error string.
*/
int nfs_access_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data);
/*
* Sync access(<path>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_access_sync(struct nfs_context *nfs, const char *path, int mode);
/*
* SYMLINK()
*/
/*
* Async symlink(<path>)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL
* -errno : An error occured.
* data is the error string.
*/
int nfs_symlink_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data);
/*
* Sync symlink(<path>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_symlink_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath);
/*
* RENAME()
*/
/*
* Async rename(<oldpath>, <newpath>)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL
* -errno : An error occured.
* data is the error string.
*/
int nfs_rename_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data);
/*
* Sync rename(<oldpath>, <newpath>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_rename_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath);
/*
* LINK()
*/
/*
* Async link(<oldpath>, <newpath>)
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL
* -errno : An error occured.
* data is the error string.
*/
int nfs_link_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data);
/*
* Sync link(<oldpath>, <newpath>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
int nfs_link_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath);
//qqq replace later with lseek(cur, 0)
off_t nfs_get_current_offset(struct nfsfh *nfsfh);
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <errno.h>
#include <rpc/xdr.h>
#include "libnfs.h"
#include "libnfs-raw.h"
#include "libnfs-private.h"
#include "libnfs-raw-mount.h"
int rpc_mount_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_NULL, cb, private_data, (xdrproc_t)xdr_void, 0);
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for mount/null call");
return -1;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for mount/null call");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
int rpc_mount_mnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_MNT, cb, private_data, (xdrproc_t)xdr_mountres3, sizeof(mountres3));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for mount/mnt call");
return -1;
}
if (xdr_dirpath(&pdu->xdr, &export) == 0) {
rpc_set_error(rpc, "XDR error. Failed to encode mount/mnt call");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for mount/mnt call");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
int rpc_mount_dump_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_DUMP, cb, private_data, (xdrproc_t)xdr_mountlist, sizeof(mountlist));
if (pdu == NULL) {
printf("Failed to allocate pdu for mount/dump\n");
return -1;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
printf("Failed to queue mount/dump pdu\n");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
int rpc_mount_umnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_UMNT, cb, private_data, (xdrproc_t)xdr_void, 0);
if (pdu == NULL) {
printf("Failed to allocate pdu for mount/umnt\n");
return -1;
}
if (xdr_dirpath(&pdu->xdr, &export) == 0) {
printf("failed to encode dirpath for mount/umnt\n");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
printf("Failed to queue mount/umnt pdu\n");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
int rpc_mount_umntall_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_UMNTALL, cb, private_data, (xdrproc_t)xdr_void, 0);
if (pdu == NULL) {
printf("Failed to allocate pdu for mount/umntall\n");
return -1;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
printf("Failed to queue mount/umntall pdu\n");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
int rpc_mount_export_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_EXPORT, cb, private_data, (xdrproc_t)xdr_exports, sizeof(exports));
if (pdu == NULL) {
printf("Failed to allocate pdu for mount/export\n");
return -1;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
printf("Failed to queue mount/export pdu\n");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
char *mountstat3_to_str(int st)
{
enum mountstat3 stat = st;
char *str = "unknown mount stat";
switch (stat) {
case MNT3_OK: str="MNT3_OK"; break;
case MNT3ERR_PERM: str="MNT3ERR_PERM"; break;
case MNT3ERR_NOENT: str="MNT3ERR_NOENT"; break;
case MNT3ERR_IO: str="MNT3ERR_IO"; break;
case MNT3ERR_ACCES: str="MNT3ERR_ACCES"; break;
case MNT3ERR_NOTDIR: str="MNT3ERR_NOTDIR"; break;
case MNT3ERR_INVAL: str="MNT3ERR_INVAL"; break;
case MNT3ERR_NAMETOOLONG: str="MNT3ERR_NAMETOOLONG"; break;
case MNT3ERR_NOTSUPP: str="MNT3ERR_NOTSUPP"; break;
case MNT3ERR_SERVERFAULT: str="MNT3ERR_SERVERFAULT"; break;
}
return str;
}
int mountstat3_to_errno(int st)
{
enum mountstat3 stat = st;
switch (stat) {
case MNT3_OK: return 0; break;
case MNT3ERR_PERM: return -EPERM; break;
case MNT3ERR_NOENT: return -EPERM; break;
case MNT3ERR_IO: return -EIO; break;
case MNT3ERR_ACCES: return -EACCES; break;
case MNT3ERR_NOTDIR: return -ENOTDIR; break;
case MNT3ERR_INVAL: return -EINVAL; break;
case MNT3ERR_NAMETOOLONG: return -E2BIG; break;
case MNT3ERR_NOTSUPP: return -EINVAL; break;
case MNT3ERR_SERVERFAULT: return -EIO; break;
}
return -ERANGE;
}
/* copied from RFC1813 */
const MNTPATHLEN = 1024; /* Maximum bytes in a path name */
const MNTNAMLEN = 255; /* Maximum bytes in a name */
const FHSIZE3 = 64; /* Maximum bytes in a V3 file handle */
typedef opaque fhandle3<FHSIZE3>;
typedef string dirpath<MNTPATHLEN>;
typedef string name<MNTNAMLEN>;
enum mountstat3 {
MNT3_OK = 0, /* no error */
MNT3ERR_PERM = 1, /* Not owner */
MNT3ERR_NOENT = 2, /* No such file or directory */
MNT3ERR_IO = 5, /* I/O error */
MNT3ERR_ACCES = 13, /* Permission denied */
MNT3ERR_NOTDIR = 20, /* Not a directory */
MNT3ERR_INVAL = 22, /* Invalid argument */
MNT3ERR_NAMETOOLONG = 63, /* Filename too long */
MNT3ERR_NOTSUPP = 10004, /* Operation not supported */
MNT3ERR_SERVERFAULT = 10006 /* A failure on the server */
};
typedef struct mountbody *mountlist;
struct mountbody {
name ml_hostname;
dirpath ml_directory;
mountlist ml_next;
};
typedef struct groupnode *groups;
struct groupnode {
name gr_name;
groups gr_next;
};
typedef struct exportnode *exports;
struct exportnode {
dirpath ex_dir;
groups ex_groups;
exports ex_next;
};
struct mountres3_ok {
fhandle3 fhandle;
int auth_flavors<>;
};
union mountres3 switch (mountstat3 fhs_status) {
case MNT3_OK:
mountres3_ok mountinfo;
default:
void;
};
program MOUNT_PROGRAM {
version MOUNT_V3 {
void MOUNT3_NULL(void) = 0;
mountres3 MOUNT3_MNT(dirpath) = 1;
mountlist MOUNT3_DUMP(void) = 2;
void MOUNT3_UMNT(dirpath) = 3;
void MOUNT3_UMNTALL(void) = 4;
exports MOUNT3_EXPORT(void) = 5;
} = 3;
} = 100005;
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <rpc/xdr.h>
#include "libnfs.h"
#include "libnfs-raw.h"
#include "libnfs-private.h"
#include "libnfs-raw-nfs.h"
char *nfsstat3_to_str(int error)
{
switch (error) {
case NFS3_OK: return "NFS3_OK"; break;
case NFS3ERR_PERM: return "NFS3ERR_PERM"; break;
case NFS3ERR_NOENT: return "NFS3ERR_NOENT"; break;
case NFS3ERR_IO: return "NFS3ERR_IO"; break;
case NFS3ERR_NXIO: return "NFS3ERR_NXIO"; break;
case NFS3ERR_ACCES: return "NFS3ERR_ACCES"; break;
case NFS3ERR_EXIST: return "NFS3ERR_EXIST"; break;
case NFS3ERR_XDEV: return "NFS3ERR_XDEV"; break;
case NFS3ERR_NODEV: return "NFS3ERR_NODEV"; break;
case NFS3ERR_NOTDIR: return "NFS3ERR_NOTDIR"; break;
case NFS3ERR_ISDIR: return "NFS3ERR_ISDIR"; break;
case NFS3ERR_INVAL: return "NFS3ERR_INVAL"; break;
case NFS3ERR_FBIG: return "NFS3ERR_FBIG"; break;
case NFS3ERR_NOSPC: return "NFS3ERR_NOSPC"; break;
case NFS3ERR_ROFS: return "NFS3ERR_ROFS"; break;
case NFS3ERR_MLINK: return "NFS3ERR_MLINK"; break;
case NFS3ERR_NAMETOOLONG: return "NFS3ERR_NAMETOOLONG"; break;
case NFS3ERR_NOTEMPTY: return "NFS3ERR_NOTEMPTY"; break;
case NFS3ERR_DQUOT: return "NFS3ERR_DQUOT"; break;
case NFS3ERR_STALE: return "NFS3ERR_STALE"; break;
case NFS3ERR_REMOTE: return "NFS3ERR_REMOTE"; break;
case NFS3ERR_BADHANDLE: return "NFS3ERR_BADHANDLE"; break;
case NFS3ERR_NOT_SYNC: return "NFS3ERR_NOT_SYNC"; break;
case NFS3ERR_BAD_COOKIE: return "NFS3ERR_BAD_COOKIE"; break;
case NFS3ERR_NOTSUPP: return "NFS3ERR_NOTSUPP"; break;
case NFS3ERR_TOOSMALL: return "NFS3ERR_TOOSMALL"; break;
case NFS3ERR_SERVERFAULT: return "NFS3ERR_SERVERFAULT"; break;
case NFS3ERR_BADTYPE: return "NFS3ERR_BADTYPE"; break;
case NFS3ERR_JUKEBOX: return "NFS3ERR_JUKEBOX"; break;
};
return "unknown nfs error";
}
int nfsstat3_to_errno(int error)
{
switch (error) {
case NFS3_OK: return 0; break;
case NFS3ERR_PERM: return -EPERM; break;
case NFS3ERR_NOENT: return -ENOENT; break;
case NFS3ERR_IO: return -EIO; break;
case NFS3ERR_NXIO: return -ENXIO; break;
case NFS3ERR_ACCES: return -EACCES; break;
case NFS3ERR_EXIST: return -EEXIST; break;
case NFS3ERR_XDEV: return -EXDEV; break;
case NFS3ERR_NODEV: return -ENODEV; break;
case NFS3ERR_NOTDIR: return -ENOTDIR; break;
case NFS3ERR_ISDIR: return -EISDIR; break;
case NFS3ERR_INVAL: return -EINVAL; break;
case NFS3ERR_FBIG: return -EFBIG; break;
case NFS3ERR_NOSPC: return -ENOSPC; break;
case NFS3ERR_ROFS: return -EROFS; break;
case NFS3ERR_MLINK: return -EMLINK; break;
case NFS3ERR_NAMETOOLONG: return -ENAMETOOLONG; break;
case NFS3ERR_NOTEMPTY: return -EEXIST; break;
case NFS3ERR_DQUOT: return -ERANGE; break;
case NFS3ERR_STALE: return -EIO; break;
case NFS3ERR_REMOTE: return -EIO; break;
case NFS3ERR_BADHANDLE: return -EIO; break;
case NFS3ERR_NOT_SYNC: return -EIO; break;
case NFS3ERR_BAD_COOKIE: return -EIO; break;
case NFS3ERR_NOTSUPP: return -EINVAL; break;
case NFS3ERR_TOOSMALL: return -EIO; break;
case NFS3ERR_SERVERFAULT: return -EIO; break;
case NFS3ERR_BADTYPE: return -EINVAL; break;
case NFS3ERR_JUKEBOX: return -EAGAIN; break;
};
return -ERANGE;
}
int rpc_nfs_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_NULL, cb, private_data, (xdrproc_t)xdr_void, 0);
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/null call");
return -1;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/null call");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
int rpc_nfs_getattr_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data)
{
struct rpc_pdu *pdu;
GETATTR3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_GETATTR, cb, private_data, (xdrproc_t)xdr_GETATTR3res, sizeof(GETATTR3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/null call");
return -1;
}
args.object.data.data_len = fh->data.data_len;
args.object.data.data_val = fh->data.data_val;
if (xdr_GETATTR3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode GETATTR3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/null call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_lookup_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *name, void *private_data)
{
struct rpc_pdu *pdu;
LOOKUP3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_LOOKUP, cb, private_data, (xdrproc_t)xdr_LOOKUP3res, sizeof(LOOKUP3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/lookup call");
return -1;
}
args.what.dir.data.data_len = fh->data.data_len;
args.what.dir.data.data_val = fh->data.data_val;
args.what.name = name;
if (xdr_LOOKUP3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode LOOKUP3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/lookup call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_access_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, int access, void *private_data)
{
struct rpc_pdu *pdu;
ACCESS3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_ACCESS, cb, private_data, (xdrproc_t)xdr_ACCESS3res, sizeof(ACCESS3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/access call");
return -1;
}
args.object.data.data_len = fh->data.data_len;
args.object.data.data_val = fh->data.data_val;
args.access = access;
if (xdr_ACCESS3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode ACCESS3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/access call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_read_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, off_t offset, size_t count, void *private_data)
{
struct rpc_pdu *pdu;
READ3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_READ, cb, private_data, (xdrproc_t)xdr_READ3res, sizeof(READ3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/read call");
return -1;
}
args.file.data.data_len = fh->data.data_len;
args.file.data.data_val = fh->data.data_val;
args.offset = offset;
args.count = count;
if (xdr_READ3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode READ3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/read call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_write_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *buf, off_t offset, size_t count, int stable_how, void *private_data)
{
struct rpc_pdu *pdu;
WRITE3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_WRITE, cb, private_data, (xdrproc_t)xdr_WRITE3res, sizeof(WRITE3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/write call");
return -1;
}
args.file.data.data_len = fh->data.data_len;
args.file.data.data_val = fh->data.data_val;
args.offset = offset;
args.count = count;
args.stable = stable_how;;
args.data.data_len = count;
args.data.data_val = buf;
if (xdr_WRITE3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode WRITE3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/write call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_commit_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data)
{
struct rpc_pdu *pdu;
COMMIT3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_COMMIT, cb, private_data, (xdrproc_t)xdr_COMMIT3res, sizeof(COMMIT3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/commit call");
return -1;
}
args.file.data.data_len = fh->data.data_len;
args.file.data.data_val = fh->data.data_val;
args.offset = 0;
args.count = 0;
if (xdr_COMMIT3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode WRITE3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/commit call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_setattr_async(struct rpc_context *rpc, rpc_cb cb, SETATTR3args *args, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_SETATTR, cb, private_data, (xdrproc_t)xdr_SETATTR3res, sizeof(SETATTR3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/setattr call");
return -1;
}
if (xdr_SETATTR3args(&pdu->xdr, args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode SETATTR3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/setattr call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_mkdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *dir, void *private_data)
{
struct rpc_pdu *pdu;
MKDIR3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_MKDIR, cb, private_data, (xdrproc_t)xdr_MKDIR3res, sizeof(MKDIR3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/setattr call");
return -1;
}
bzero(&args, sizeof(MKDIR3args));
args.where.dir.data.data_len = fh->data.data_len;
args.where.dir.data.data_val = fh->data.data_val;
args.where.name = dir;
args.attributes.mode.set_it = 1;
args.attributes.mode.set_mode3_u.mode = 0755;
if (xdr_MKDIR3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode MKDIR3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/mkdir call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_rmdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *dir, void *private_data)
{
struct rpc_pdu *pdu;
RMDIR3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_RMDIR, cb, private_data, (xdrproc_t)xdr_RMDIR3res, sizeof(RMDIR3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/rmdir call");
return -1;
}
bzero(&args, sizeof(RMDIR3args));
args.object.dir.data.data_len = fh->data.data_len;
args.object.dir.data.data_val = fh->data.data_val;
args.object.name = dir;
if (xdr_RMDIR3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode RMDIR3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/rmdir call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_create_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *file, int mode, void *private_data)
{
struct rpc_pdu *pdu;
CREATE3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_CREATE, cb, private_data, (xdrproc_t)xdr_CREATE3res, sizeof(CREATE3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/create call");
return -1;
}
bzero(&args, sizeof(CREATE3args));
args.where.dir.data.data_len = fh->data.data_len;
args.where.dir.data.data_val = fh->data.data_val;
args.where.name = file;
args.how.mode = UNCHECKED;
args.how.createhow3_u.obj_attributes.mode.set_it = 1;
args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode = mode;
if (xdr_CREATE3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode CREATE3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/create call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_remove_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *file, void *private_data)
{
struct rpc_pdu *pdu;
REMOVE3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_REMOVE, cb, private_data, (xdrproc_t)xdr_REMOVE3res, sizeof(REMOVE3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/remove call");
return -1;
}
bzero(&args, sizeof(REMOVE3args));
args.object.dir.data.data_len = fh->data.data_len;
args.object.dir.data.data_val = fh->data.data_val;
args.object.name = file;
if (xdr_REMOVE3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode REMOVE3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/remove call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_readdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, uint64_t cookie, char *cookieverf, int count, void *private_data)
{
struct rpc_pdu *pdu;
READDIR3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_READDIR, cb, private_data, (xdrproc_t)xdr_READDIR3res, sizeof(READDIR3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/readdir call");
return -1;
}
bzero(&args, sizeof(READDIR3args));
args.dir.data.data_len = fh->data.data_len;
args.dir.data.data_val = fh->data.data_val;
args.cookie = cookie;
memcpy(&args.cookieverf, cookieverf, sizeof(cookieverf3));
args.count = count;
if (xdr_READDIR3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode READDIR3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/readdir call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_fsstat_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data)
{
struct rpc_pdu *pdu;
FSSTAT3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_FSSTAT, cb, private_data, (xdrproc_t)xdr_FSSTAT3res, sizeof(FSSTAT3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/fsstat call");
return -1;
}
args.fsroot.data.data_len = fh->data.data_len;
args.fsroot.data.data_val = fh->data.data_val;
if (xdr_FSSTAT3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode FSSTAT3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/fsstat call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_readlink_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data)
{
struct rpc_pdu *pdu;
READLINK3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_READLINK, cb, private_data, (xdrproc_t)xdr_READLINK3res, sizeof(READLINK3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/readlink call");
return -1;
}
args.symlink.data.data_len = fh->data.data_len;
args.symlink.data.data_val = fh->data.data_val;
if (xdr_READLINK3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode READLINK3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/readlink call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_symlink_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *newname, char *oldpath, void *private_data)
{
struct rpc_pdu *pdu;
SYMLINK3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_SYMLINK, cb, private_data, (xdrproc_t)xdr_SYMLINK3res, sizeof(SYMLINK3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/symlink call");
return -1;
}
bzero(&args, sizeof(SYMLINK3args));
args.where.dir.data.data_len = fh->data.data_len;
args.where.dir.data.data_val = fh->data.data_val;
args.where.name = newname;
args.symlink.symlink_attributes.mode.set_it = 1;
args.symlink.symlink_attributes.mode.set_mode3_u.mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH;
args.symlink.symlink_data = oldpath;
if (xdr_SYMLINK3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode SYMLINK3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/symlink call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_rename_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *olddir, char *oldname, struct nfs_fh3 *newdir, char *newname, void *private_data)
{
struct rpc_pdu *pdu;
RENAME3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_RENAME, cb, private_data, (xdrproc_t)xdr_RENAME3res, sizeof(RENAME3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/rename call");
return -1;
}
bzero(&args, sizeof(RENAME3args));
args.from.dir.data.data_len = olddir->data.data_len;
args.from.dir.data.data_val = olddir->data.data_val;
args.from.name = oldname;
args.to.dir.data.data_len = newdir->data.data_len;
args.to.dir.data.data_val = newdir->data.data_val;
args.to.name = newname;
if (xdr_RENAME3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode RENAME3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/rename call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
int rpc_nfs_link_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *file, struct nfs_fh3 *newdir, char *newname, void *private_data)
{
struct rpc_pdu *pdu;
LINK3args args;
pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_LINK, cb, private_data, (xdrproc_t)xdr_LINK3res, sizeof(LINK3res));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/link call");
return -1;
}
bzero(&args, sizeof(LINK3args));
args.file.data.data_len = file->data.data_len;
args.file.data.data_val = file->data.data_val;
args.link.dir.data.data_len = newdir->data.data_len;
args.link.dir.data.data_val = newdir->data.data_val;
args.link.name = newname;
if (xdr_LINK3args(&pdu->xdr, &args) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode LINK3args");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/link call");
rpc_free_pdu(rpc, pdu);
return -3;
}
return 0;
}
/* copied from rfc 1813 */
const NFS3_FHSIZE = 64; /* Maximum bytes in a V3 file handle */
const NFS3_WRITEVERFSIZE = 8;
const NFS3_CREATEVERFSIZE = 8;
const NFS3_COOKIEVERFSIZE = 8;
typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE];
typedef unsigned hyper uint64;
typedef uint64 cookie3;
struct nfs_fh3 {
opaque data<NFS3_FHSIZE>;
};
typedef string filename3<>;
struct diropargs3 {
nfs_fh3 dir;
filename3 name;
};
enum ftype3 {
NF3REG = 1,
NF3DIR = 2,
NF3BLK = 3,
NF3CHR = 4,
NF3LNK = 5,
NF3SOCK = 6,
NF3FIFO = 7
};
typedef unsigned long uint32;
typedef long int32;
typedef uint32 mode3;
typedef uint32 uid3;
typedef uint32 gid3;
typedef uint64 size3;
typedef uint64 fileid3;
struct specdata3 {
uint32 specdata1;
uint32 specdata2;
};
struct nfstime3 {
uint32 seconds;
uint32 nseconds;
};
struct fattr3 {
ftype3 type;
mode3 mode;
uint32 nlink;
uid3 uid;
gid3 gid;
size3 size;
size3 used;
specdata3 rdev;
uint64 fsid;
fileid3 fileid;
nfstime3 atime;
nfstime3 mtime;
nfstime3 ctime;
};
union post_op_attr switch (bool attributes_follow) {
case TRUE:
fattr3 attributes;
case FALSE:
void;
};
enum nfsstat3 {
NFS3_OK = 0,
NFS3ERR_PERM = 1,
NFS3ERR_NOENT = 2,
NFS3ERR_IO = 5,
NFS3ERR_NXIO = 6,
NFS3ERR_ACCES = 13,
NFS3ERR_EXIST = 17,
NFS3ERR_XDEV = 18,
NFS3ERR_NODEV = 19,
NFS3ERR_NOTDIR = 20,
NFS3ERR_ISDIR = 21,
NFS3ERR_INVAL = 22,
NFS3ERR_FBIG = 27,
NFS3ERR_NOSPC = 28,
NFS3ERR_ROFS = 30,
NFS3ERR_MLINK = 31,
NFS3ERR_NAMETOOLONG = 63,
NFS3ERR_NOTEMPTY = 66,
NFS3ERR_DQUOT = 69,
NFS3ERR_STALE = 70,
NFS3ERR_REMOTE = 71,
NFS3ERR_BADHANDLE = 10001,
NFS3ERR_NOT_SYNC = 10002,
NFS3ERR_BAD_COOKIE = 10003,
NFS3ERR_NOTSUPP = 10004,
NFS3ERR_TOOSMALL = 10005,
NFS3ERR_SERVERFAULT = 10006,
NFS3ERR_BADTYPE = 10007,
NFS3ERR_JUKEBOX = 10008
};
enum stable_how {
UNSTABLE = 0,
DATA_SYNC = 1,
FILE_SYNC = 2
};
typedef uint64 offset3;
typedef uint32 count3;
struct wcc_attr {
size3 size;
nfstime3 mtime;
nfstime3 ctime;
};
union pre_op_attr switch (bool attributes_follow) {
case TRUE:
wcc_attr attributes;
case FALSE:
void;
};
struct wcc_data {
pre_op_attr before;
post_op_attr after;
};
struct WRITE3args {
nfs_fh3 file;
offset3 offset;
count3 count;
stable_how stable;
opaque data<>;
};
typedef opaque writeverf3[NFS3_WRITEVERFSIZE];
struct WRITE3resok {
wcc_data file_wcc;
count3 count;
stable_how committed;
writeverf3 verf;
};
struct WRITE3resfail {
wcc_data file_wcc;
};
union WRITE3res switch (nfsstat3 status) {
case NFS3_OK:
WRITE3resok resok;
default:
WRITE3resfail resfail;
};
struct LOOKUP3args {
diropargs3 what;
};
struct LOOKUP3resok {
nfs_fh3 object;
post_op_attr obj_attributes;
post_op_attr dir_attributes;
};
struct LOOKUP3resfail {
post_op_attr dir_attributes;
};
union LOOKUP3res switch (nfsstat3 status) {
case NFS3_OK:
LOOKUP3resok resok;
default:
LOOKUP3resfail resfail;
};
struct COMMIT3args {
nfs_fh3 file;
offset3 offset;
count3 count;
};
struct COMMIT3resok {
wcc_data file_wcc;
writeverf3 verf;
};
struct COMMIT3resfail {
wcc_data file_wcc;
};
union COMMIT3res switch (nfsstat3 status) {
case NFS3_OK:
COMMIT3resok resok;
default:
COMMIT3resfail resfail;
};
const ACCESS3_READ = 0x0001;
const ACCESS3_LOOKUP = 0x0002;
const ACCESS3_MODIFY = 0x0004;
const ACCESS3_EXTEND = 0x0008;
const ACCESS3_DELETE = 0x0010;
const ACCESS3_EXECUTE = 0x0020;
struct ACCESS3args {
nfs_fh3 object;
uint32 access;
};
struct ACCESS3resok {
post_op_attr obj_attributes;
uint32 access;
};
struct ACCESS3resfail {
post_op_attr obj_attributes;
};
union ACCESS3res switch (nfsstat3 status) {
case NFS3_OK:
ACCESS3resok resok;
default:
ACCESS3resfail resfail;
};
struct GETATTR3args {
nfs_fh3 object;
};
struct GETATTR3resok {
fattr3 obj_attributes;
};
union GETATTR3res switch (nfsstat3 status) {
case NFS3_OK:
GETATTR3resok resok;
default:
void;
};
enum time_how {
DONT_CHANGE = 0,
SET_TO_SERVER_TIME = 1,
SET_TO_CLIENT_TIME = 2
};
union set_mode3 switch (bool set_it) {
case TRUE:
mode3 mode;
default:
void;
};
union set_uid3 switch (bool set_it) {
case TRUE:
uid3 uid;
default:
void;
};
union set_gid3 switch (bool set_it) {
case TRUE:
gid3 gid;
default:
void;
};
union set_size3 switch (bool set_it) {
case TRUE:
size3 size;
default:
void;
};
union set_atime switch (time_how set_it) {
case SET_TO_CLIENT_TIME:
nfstime3 atime;
default:
void;
};
union set_mtime switch (time_how set_it) {
case SET_TO_CLIENT_TIME:
nfstime3 mtime;
default:
void;
};
struct sattr3 {
set_mode3 mode;
set_uid3 uid;
set_gid3 gid;
set_size3 size;
set_atime atime;
set_mtime mtime;
};
enum createmode3 {
UNCHECKED = 0,
GUARDED = 1,
EXCLUSIVE = 2
};
typedef opaque createverf3[NFS3_CREATEVERFSIZE];
union createhow3 switch (createmode3 mode) {
case UNCHECKED:
case GUARDED:
sattr3 obj_attributes;
case EXCLUSIVE:
createverf3 verf;
};
struct CREATE3args {
diropargs3 where;
createhow3 how;
};
union post_op_fh3 switch (bool handle_follows) {
case TRUE:
nfs_fh3 handle;
case FALSE:
void;
};
struct CREATE3resok {
post_op_fh3 obj;
post_op_attr obj_attributes;
wcc_data dir_wcc;
};
struct CREATE3resfail {
wcc_data dir_wcc;
};
union CREATE3res switch (nfsstat3 status) {
case NFS3_OK:
CREATE3resok resok;
default:
CREATE3resfail resfail;
};
struct REMOVE3args {
diropargs3 object;
};
struct REMOVE3resok {
wcc_data dir_wcc;
};
struct REMOVE3resfail {
wcc_data dir_wcc;
};
union REMOVE3res switch (nfsstat3 status) {
case NFS3_OK:
REMOVE3resok resok;
default:
REMOVE3resfail resfail;
};
struct READ3args {
nfs_fh3 file;
offset3 offset;
count3 count;
};
struct READ3resok {
post_op_attr file_attributes;
count3 count;
bool eof;
opaque data<>;
};
struct READ3resfail {
post_op_attr file_attributes;
};
union READ3res switch (nfsstat3 status) {
case NFS3_OK:
READ3resok resok;
default:
READ3resfail resfail;
};
const FSF3_LINK = 0x0001;
const FSF3_SYMLINK = 0x0002;
const FSF3_HOMOGENEOUS = 0x0008;
const FSF3_CANSETTIME = 0x0010;
struct FSINFO3args {
nfs_fh3 fsroot;
};
struct FSINFO3resok {
post_op_attr obj_attributes;
uint32 rtmax;
uint32 rtpref;
uint32 rtmult;
uint32 wtmax;
uint32 wtpref;
uint32 wtmult;
uint32 dtpref;
size3 maxfilesize;
nfstime3 time_delta;
uint32 properties;
};
struct FSINFO3resfail {
post_op_attr obj_attributes;
};
union FSINFO3res switch (nfsstat3 status) {
case NFS3_OK:
FSINFO3resok resok;
default:
FSINFO3resfail resfail;
};
struct FSSTAT3args {
nfs_fh3 fsroot;
};
struct FSSTAT3resok {
post_op_attr obj_attributes;
size3 tbytes;
size3 fbytes;
size3 abytes;
size3 tfiles;
size3 ffiles;
size3 afiles;
uint32 invarsec;
};
struct FSSTAT3resfail {
post_op_attr obj_attributes;
};
union FSSTAT3res switch (nfsstat3 status) {
case NFS3_OK:
FSSTAT3resok resok;
default:
FSSTAT3resfail resfail;
};
struct PATHCONF3args {
nfs_fh3 object;
};
struct PATHCONF3resok {
post_op_attr obj_attributes;
uint32 linkmax;
uint32 name_max;
bool no_trunc;
bool chown_restricted;
bool case_insensitive;
bool case_preserving;
};
struct PATHCONF3resfail {
post_op_attr obj_attributes;
};
union PATHCONF3res switch (nfsstat3 status) {
case NFS3_OK:
PATHCONF3resok resok;
default:
PATHCONF3resfail resfail;
};
typedef string nfspath3<>;
struct symlinkdata3 {
sattr3 symlink_attributes;
nfspath3 symlink_data;
};
struct SYMLINK3args {
diropargs3 where;
symlinkdata3 symlink;
};
struct SYMLINK3resok {
post_op_fh3 obj;
post_op_attr obj_attributes;
wcc_data dir_wcc;
};
struct SYMLINK3resfail {
wcc_data dir_wcc;
};
union SYMLINK3res switch (nfsstat3 status) {
case NFS3_OK:
SYMLINK3resok resok;
default:
SYMLINK3resfail resfail;
};
struct READLINK3args {
nfs_fh3 symlink;
};
struct READLINK3resok {
post_op_attr symlink_attributes;
nfspath3 data;
};
struct READLINK3resfail {
post_op_attr symlink_attributes;
};
union READLINK3res switch (nfsstat3 status) {
case NFS3_OK:
READLINK3resok resok;
default:
READLINK3resfail resfail;
};
struct devicedata3 {
sattr3 dev_attributes;
specdata3 spec;
};
union mknoddata3 switch (ftype3 type) {
case NF3CHR:
case NF3BLK:
devicedata3 device;
case NF3SOCK:
case NF3FIFO:
sattr3 pipe_attributes;
default:
void;
};
struct MKNOD3args {
diropargs3 where;
mknoddata3 what;
};
struct MKNOD3resok {
post_op_fh3 obj;
post_op_attr obj_attributes;
wcc_data dir_wcc;
};
struct MKNOD3resfail {
wcc_data dir_wcc;
};
union MKNOD3res switch (nfsstat3 status) {
case NFS3_OK:
MKNOD3resok resok;
default:
MKNOD3resfail resfail;
};
struct MKDIR3args {
diropargs3 where;
sattr3 attributes;
};
struct MKDIR3resok {
post_op_fh3 obj;
post_op_attr obj_attributes;
wcc_data dir_wcc;
};
struct MKDIR3resfail {
wcc_data dir_wcc;
};
union MKDIR3res switch (nfsstat3 status) {
case NFS3_OK:
MKDIR3resok resok;
default:
MKDIR3resfail resfail;
};
struct RMDIR3args {
diropargs3 object;
};
struct RMDIR3resok {
wcc_data dir_wcc;
};
struct RMDIR3resfail {
wcc_data dir_wcc;
};
union RMDIR3res switch (nfsstat3 status) {
case NFS3_OK:
RMDIR3resok resok;
default:
RMDIR3resfail resfail;
};
struct RENAME3args {
diropargs3 from;
diropargs3 to;
};
struct RENAME3resok {
wcc_data fromdir_wcc;
wcc_data todir_wcc;
};
struct RENAME3resfail {
wcc_data fromdir_wcc;
wcc_data todir_wcc;
};
union RENAME3res switch (nfsstat3 status) {
case NFS3_OK:
RENAME3resok resok;
default:
RENAME3resfail resfail;
};
struct READDIRPLUS3args {
nfs_fh3 dir;
cookie3 cookie;
cookieverf3 cookieverf;
count3 dircount;
count3 maxcount;
};
struct entryplus3 {
fileid3 fileid;
filename3 name;
cookie3 cookie;
post_op_attr name_attributes;
post_op_fh3 name_handle;
entryplus3 *nextentry;
};
struct dirlistplus3 {
entryplus3 *entries;
bool eof;
};
struct READDIRPLUS3resok {
post_op_attr dir_attributes;
cookieverf3 cookieverf;
dirlistplus3 reply;
};
struct READDIRPLUS3resfail {
post_op_attr dir_attributes;
};
union READDIRPLUS3res switch (nfsstat3 status) {
case NFS3_OK:
READDIRPLUS3resok resok;
default:
READDIRPLUS3resfail resfail;
};
struct READDIR3args {
nfs_fh3 dir;
cookie3 cookie;
cookieverf3 cookieverf;
count3 count;
};
struct entry3 {
fileid3 fileid;
filename3 name;
cookie3 cookie;
entry3 *nextentry;
};
struct dirlist3 {
entry3 *entries;
bool eof;
};
struct READDIR3resok {
post_op_attr dir_attributes;
cookieverf3 cookieverf;
dirlist3 reply;
};
struct READDIR3resfail {
post_op_attr dir_attributes;
};
union READDIR3res switch (nfsstat3 status) {
case NFS3_OK:
READDIR3resok resok;
default:
READDIR3resfail resfail;
};
struct LINK3args {
nfs_fh3 file;
diropargs3 link;
};
struct LINK3resok {
post_op_attr file_attributes;
wcc_data linkdir_wcc;
};
struct LINK3resfail {
post_op_attr file_attributes;
wcc_data linkdir_wcc;
};
union LINK3res switch (nfsstat3 status) {
case NFS3_OK:
LINK3resok resok;
default:
LINK3resfail resfail;
};
union sattrguard3 switch (bool check) {
case TRUE:
nfstime3 obj_ctime;
case FALSE:
void;
};
struct SETATTR3args {
nfs_fh3 object;
sattr3 new_attributes;
sattrguard3 guard;
};
struct SETATTR3resok {
wcc_data obj_wcc;
};
struct SETATTR3resfail {
wcc_data obj_wcc;
};
union SETATTR3res switch (nfsstat3 status) {
case NFS3_OK:
SETATTR3resok resok;
default:
SETATTR3resfail resfail;
};
program NFS_PROGRAM {
version NFS_V3 {
void
NFS3_NULL(void) = 0;
GETATTR3res
NFS3_GETATTR(GETATTR3args) = 1;
SETATTR3res
NFS3_SETATTR(SETATTR3args) = 2;
LOOKUP3res
NFS3_LOOKUP(LOOKUP3args) = 3;
ACCESS3res
NFS3_ACCESS(ACCESS3args) = 4;
READLINK3res
NFS3_READLINK(READLINK3args) = 5;
READ3res
NFS3_READ(READ3args) = 6;
WRITE3res
NFS3_WRITE(WRITE3args) = 7;
CREATE3res
NFS3_CREATE(CREATE3args) = 8;
MKDIR3res
NFS3_MKDIR(MKDIR3args) = 9;
SYMLINK3res
NFS3_SYMLINK(SYMLINK3args) = 10;
/* MKNOD3res NFSPROC3_MKNOD(MKNOD3args) = 11;*/
REMOVE3res
NFS3_REMOVE(REMOVE3args) = 12;
RMDIR3res
NFS3_RMDIR(RMDIR3args) = 13;
RENAME3res
NFS3_RENAME(RENAME3args) = 14;
LINK3res
NFS3_LINK(LINK3args) = 15;
READDIR3res
NFS3_READDIR(READDIR3args) = 16;
READDIRPLUS3res
NFS3_READDIRPLUS(READDIRPLUS3args) = 17;
FSSTAT3res
NFS3_FSSTAT(FSSTAT3args) = 18;
FSINFO3res
NFS3_FSINFO(FSINFO3args) = 19;
PATHCONF3res
NFS3_PATHCONF(PATHCONF3args) = 20;
COMMIT3res
NFS3_COMMIT(COMMIT3args) = 21;
} = 3;
} = 100003;
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <errno.h>
#include <rpc/xdr.h>
#include "libnfs.h"
#include "libnfs-raw.h"
#include "libnfs-private.h"
#include "libnfs-raw-nfsacl.h"
int rpc_nfsacl_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, NFSACL_PROGRAM, NFSACL_V3, NFSACL3_NULL, cb, private_data, (xdrproc_t)xdr_void, 0);
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfsacl/null call");
return -1;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfsacl/null call");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
/* deducted from wireshark traces */
program NFSACL_PROGRAM {
version NFSACL_V3 {
void NFSACL3_NULL(void) = 0;
} = 3;
} = 100227;
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/* Example program using the highlevel async interface.
*/
#define SERVER "10.1.1.27"
#define EXPORT "/VIRTUAL"
#define NFSFILE "/BOOKS/Classics/Dracula.djvu"
#define NFSDIR "/BOOKS/Classics/"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include "libnfs.h"
struct client {
char *server;
char *export;
uint32_t mount_port;
struct nfsfh *nfsfh;
int is_finished;
};
void nfs_opendir_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
{
struct client *client = private_data;
struct nfsdir *nfsdir = data;
struct nfsdirent *nfsdirent;
if (status < 0) {
printf("opendir failed with \"%s\"\n", (char *)data);
exit(10);
}
printf("opendir successful\n");
while((nfsdirent = nfs_readdir(nfs, nfsdir)) != NULL) {
printf("Inode:%d Name:%s\n", (int)nfsdirent->inode, nfsdirent->name);
}
nfs_closedir(nfs, nfsdir);
client->is_finished = 1;
}
void nfs_close_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
{
struct client *client = private_data;
if (status < 0) {
printf("close failed with \"%s\"\n", (char *)data);
exit(10);
}
printf("close successful\n");
printf("call opendir(%s)\n", NFSDIR);
if (nfs_opendir_async(nfs, NFSDIR, nfs_opendir_cb, client) != 0) {
printf("Failed to start async nfs close\n");
exit(10);
}
}
void nfs_fstat_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
{
struct client *client = private_data;
struct stat *st;
if (status < 0) {
printf("fstat call failed with \"%s\"\n", (char *)data);
exit(10);
}
printf("Got reply from server for fstat(%s).\n", NFSFILE);
st = (struct stat *)data;
printf("Mode %04o\n", st->st_mode);
printf("Size %d\n", (int)st->st_size);
printf("Inode %04o\n", (int)st->st_ino);
printf("Close file\n");
if (nfs_close_async(nfs, client->nfsfh, nfs_close_cb, client) != 0) {
printf("Failed to start async nfs close\n");
exit(10);
}
}
void nfs_read_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
{
struct client *client = private_data;
char *read_data;
int i;
if (status < 0) {
printf("read failed with \"%s\"\n", (char *)data);
exit(10);
}
printf("read successful with %d bytes of data\n", status);
read_data = data;
for (i=0;i<16;i++) {
printf("%02x ", read_data[i]&0xff);
}
printf("\n");
printf("Fstat file :%s\n", NFSFILE);
if (nfs_fstat_async(nfs, client->nfsfh, nfs_fstat_cb, client) != 0) {
printf("Failed to start async nfs fstat\n");
exit(10);
}
}
void nfs_open_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
{
struct client *client = private_data;
struct nfsfh *nfsfh;
if (status < 0) {
printf("open call failed with \"%s\"\n", (char *)data);
exit(10);
}
nfsfh = data;
client->nfsfh = nfsfh;
printf("Got reply from server for open(%s). Handle:%p\n", NFSFILE, data);
printf("Read first 16 bytes\n");
if (nfs_pread_async(nfs, nfsfh, 0, 16, nfs_read_cb, client) != 0) {
printf("Failed to start async nfs open\n");
exit(10);
}
}
void nfs_stat_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
{
struct client *client = private_data;
struct stat *st;
if (status < 0) {
printf("stat call failed with \"%s\"\n", (char *)data);
exit(10);
}
printf("Got reply from server for stat(%s).\n", NFSFILE);
st = (struct stat *)data;
printf("Mode %04o\n", st->st_mode);
printf("Size %d\n", (int)st->st_size);
printf("Inode %04o\n", (int)st->st_ino);
printf("Open file for reading :%s\n", NFSFILE);
if (nfs_open_async(nfs, NFSFILE, O_RDONLY, nfs_open_cb, client) != 0) {
printf("Failed to start async nfs open\n");
exit(10);
}
}
void nfs_mount_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
{
struct client *client = private_data;
if (status < 0) {
printf("mount/mnt call failed with \"%s\"\n", (char *)data);
exit(10);
}
printf("Got reply from server for MOUNT/MNT procedure.\n");
printf("Stat file :%s\n", NFSFILE);
if (nfs_stat_async(nfs, NFSFILE, nfs_stat_cb, client) != 0) {
printf("Failed to start async nfs stat\n");
exit(10);
}
}
int main(int argc, char *argv[])
{
struct nfs_context *nfs;
struct pollfd pfd;
int ret;
struct client client;
client.server = SERVER;
client.export = EXPORT;
client.is_finished = 0;
nfs = nfs_init_context();
if (nfs == NULL) {
printf("failed to init context\n");
exit(10);
}
ret = nfs_mount_async(nfs, client.server, client.export, nfs_mount_cb, &client);
if (ret != 0) {
printf("Failed to start async nfs mount\n");
exit(10);
}
for (;;) {
pfd.fd = nfs_get_fd(nfs);
pfd.events = nfs_which_events(nfs);
if (poll(&pfd, 1, -1) < 0) {
printf("Poll failed");
exit(10);
}
if (nfs_service(nfs, pfd.revents) < 0) {
printf("nfs_service failed\n");
break;
}
if (client.is_finished) {
break;
}
}
nfs_destroy_context(nfs);
printf("nfsclient finished\n");
return 0;
}
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/* Example program using the lowlevel raw interface.
* This allow accurate control of the exact commands that are being used.
*/
#define SERVER "10.1.1.27"
#define EXPORT "/VIRTUAL"
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include "libnfs.h"
#include "libnfs-raw.h"
#include "libnfs-raw-mount.h"
struct client {
char *server;
char *export;
uint32_t mount_port;
int is_finished;
};
void mount_mnt_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
{
mountres3 *res;
struct client *client = private_data;
if (status == RPC_STATUS_ERROR) {
printf("mount/mnt call failed with \"%s\"\n", (char *)data);
exit(10);
}
if (status != RPC_STATUS_SUCCESS) {
printf("mount/mnt call to server %s failed, status:%d\n", client->server, status);
exit(10);
}
res = data;
if (res->fhs_status != MNT3_OK) {
printf("RPC error: Mount failed with error %s(%d) %s(%d)", mountstat3_to_str(res->fhs_status), res->fhs_status, strerror(-mountstat3_to_errno(res->fhs_status)), -mountstat3_to_errno(res->fhs_status));
exit(10);
}
printf("Got reply from server for MOUNT/MNT procedure.\n");
client->is_finished = 1;
}
void mount_null_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
{
struct client *client = private_data;
if (status == RPC_STATUS_ERROR) {
printf("mount null call failed with \"%s\"\n", (char *)data);
exit(10);
}
if (status != RPC_STATUS_SUCCESS) {
printf("mount null call to server %s failed, status:%d\n", client->server, status);
exit(10);
}
printf("Got reply from server for MOUNT/NULL procedure.\n");
printf("Send MOUNT/MNT command for %s\n", client->export);
if (rpc_mount_mnt_async(rpc, mount_mnt_cb, client->export, client) != 0) {
printf("Failed to send mnt request\n");
exit(10);
}
}
void mount_connect_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
{
struct client *client = private_data;
if (status != RPC_STATUS_SUCCESS) {
printf("connection to RPC.MOUNTD on server %s failed\n", client->server);
exit(10);
}
printf("Connected to RPC.MOUNTD on %s:%d\n", client->server, client->mount_port);
printf("Send NULL request to check if RPC.MOUNTD is actually running\n");
if (rpc_mount_null_async(rpc, mount_null_cb, client) != 0) {
printf("Failed to send null request\n");
exit(10);
}
}
void pmap_getport_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
{
struct client *client = private_data;
uint32_t port;
if (status == RPC_STATUS_ERROR) {
printf("portmapper getport call failed with \"%s\"\n", (char *)data);
exit(10);
}
if (status != RPC_STATUS_SUCCESS) {
printf("portmapper getport call to server %s failed, status:%d\n", client->server, status);
exit(10);
}
client->mount_port = *(uint32_t *)data;
printf("GETPORT returned Port:%d\n", client->mount_port);
if (client->mount_port == 0) {
printf("RPC.MOUNTD is not available on server : %s\n", client->server, client->mount_port);
exit(10);
}
printf("Disconnect socket from portmap server\n");
if (rpc_disconnect(rpc, "normal disconnect") != 0) {
printf("Failed to disconnect socket to portmapper\n");
exit(10);
}
printf("Connect to RPC.MOUNTD on %s:%d\n", client->server, client->mount_port);
if (rpc_connect_async(rpc, client->server, client->mount_port, 1, mount_connect_cb, client) != 0) {
printf("Failed to start connection\n");
exit(10);
}
}
void pmap_null_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
{
struct client *client = private_data;
if (status == RPC_STATUS_ERROR) {
printf("portmapper null call failed with \"%s\"\n", (char *)data);
exit(10);
}
if (status != RPC_STATUS_SUCCESS) {
printf("portmapper null call to server %s failed, status:%d\n", client->server, status);
exit(10);
}
printf("Got reply from server for PORTMAP/NULL procedure.\n");
printf("Send getport request asking for MOUNT port\n");
if (rpc_pmap_getport_async(rpc, MOUNT_PROGRAM, MOUNT_V3, pmap_getport_cb, client) != 0) {
printf("Failed to send getport request\n");
exit(10);
}
}
void pmap_connect_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
{
struct client *client = private_data;
printf("pmap_connect_cb status:%d.\n", status);
if (status != RPC_STATUS_SUCCESS) {
printf("connection to portmapper on server %s failed\n", client->server);
exit(10);
}
printf("Send NULL request to check if portmapper is actually running\n");
if (rpc_pmap_null_async(rpc, pmap_null_cb, client) != 0) {
printf("Failed to send null request\n");
exit(10);
}
}
int main(int argc, char *argv[])
{
struct rpc_context *rpc;
struct pollfd pfd;
int ret;
struct client client;
rpc = rpc_init_context();
if (rpc == NULL) {
printf("failed to init context\n");
exit(10);
}
client.server = SERVER;
client.export = EXPORT;
client.is_finished = 0;
if (rpc_connect_async(rpc, client.server, 111, 0, pmap_connect_cb, &client) != 0) {
printf("Failed to start connection\n");
exit(10);
}
for (;;) {
pfd.fd = rpc_get_fd(rpc);
pfd.events = rpc_which_events(rpc);
if (poll(&pfd, 1, -1) < 0) {
printf("Poll failed");
exit(10);
}
if (rpc_service(rpc, pfd.revents) < 0) {
printf("rpc_service failed\n");
break;
}
if (client.is_finished) {
break;
}
}
rpc_destroy_context(rpc);
rpc=NULL;
printf("nfsclient finished\n");
return 0;
}
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/* Example program using the highlevel sync interface
*/
#define SERVER "10.1.1.27"
#define EXPORT "/VIRTUAL"
#define NFSFILE "/BOOKS/Classics/Dracula.djvu"
#define NFSFILER "/BOOKS/Classics/Dracula.djvu.renamed"
#define NFSFILEW "/BOOKS/Classics/foo"
#define NFSDIR "/BOOKS/Classics/"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <unistd.h>
#include <fcntl.h>
#include "libnfs.h"
#include <rpc/rpc.h> /* for authunix_create() */
struct client {
char *server;
char *export;
uint32_t mount_port;
int is_finished;
};
int main(int argc, char *argv[])
{
struct nfs_context *nfs;
int i, ret;
struct client client;
struct stat st;
struct nfsfh *nfsfh;
struct nfsdir *nfsdir;
struct nfsdirent *nfsdirent;
client.server = SERVER;
client.export = EXPORT;
client.is_finished = 0;
char buf[16];
off_t offset;
struct statvfs svfs;
nfs = nfs_init_context();
if (nfs == NULL) {
printf("failed to init context\n");
exit(10);
}
ret = nfs_mount_sync(nfs, client.server, client.export);
if (ret != 0) {
printf("Failed to mount nfs share : %s\n", nfs_get_error(nfs));
exit(10);
}
printf("mounted share successfully\n");
exit(10);
ret = nfs_stat_sync(nfs, NFSFILE, &st);
if (ret != 0) {
printf("Failed to stat(%s) %s\n", NFSFILE, nfs_get_error(nfs));
exit(10);
}
printf("Mode %04o\n", st.st_mode);
printf("Size %d\n", (int)st.st_size);
printf("Inode %04o\n", (int)st.st_ino);
ret = nfs_open_sync(nfs, NFSFILE, O_RDONLY, &nfsfh);
if (ret != 0) {
printf("Failed to open(%s) %s\n", NFSFILE, nfs_get_error(nfs));
exit(10);
}
ret = nfs_read_sync(nfs, nfsfh, 16, buf);
if (ret < 0) {
printf("Failed to pread(%s) %s\n", NFSFILE, nfs_get_error(nfs));
exit(10);
}
printf("read %d bytes\n", ret);
for (i=0;i<16;i++) {
printf("%02x ", buf[i]&0xff);
}
printf("\n");
ret = nfs_read_sync(nfs, nfsfh, 16, buf);
if (ret < 0) {
printf("Failed to pread(%s) %s\n", NFSFILE, nfs_get_error(nfs));
exit(10);
}
printf("read %d bytes\n", ret);
for (i=0;i<16;i++) {
printf("%02x ", buf[i]&0xff);
}
printf("\n");
ret = (int)nfs_lseek_sync(nfs, nfsfh, 0, SEEK_CUR, &offset);
if (ret < 0) {
printf("Failed to lseek(%s) %s\n", NFSFILE, nfs_get_error(nfs));
exit(10);
}
printf("File position is %d\n", (int)offset);
printf("seek to end of file\n");
ret = (int)nfs_lseek_sync(nfs, nfsfh, 0, SEEK_END, &offset);
if (ret < 0) {
printf("Failed to lseek(%s) %s\n", NFSFILE, nfs_get_error(nfs));
exit(10);
}
printf("File position is %d\n", (int)offset);
ret = nfs_fstat_sync(nfs, nfsfh, &st);
if (ret != 0) {
printf("Failed to stat(%s) %s\n", NFSFILE, nfs_get_error(nfs));
exit(10);
}
printf("Mode %04o\n", st.st_mode);
printf("Size %d\n", (int)st.st_size);
printf("Inode %04o\n", (int)st.st_ino);
ret = nfs_close_sync(nfs, nfsfh);
if (ret < 0) {
printf("Failed to close(%s)\n", NFSFILE, nfs_get_error(nfs));
exit(10);
}
ret = nfs_opendir_sync(nfs, NFSDIR, &nfsdir);
if (ret != 0) {
printf("Failed to open(%s) %s\n", NFSFILE, nfs_get_error(nfs));
exit(10);
}
while((nfsdirent = nfs_readdir(nfs, nfsdir)) != NULL) {
printf("Inode:%d Name:%s\n", (int)nfsdirent->inode, nfsdirent->name);
}
nfs_closedir(nfs, nfsdir);
ret = nfs_open_sync(nfs, NFSFILEW, O_WRONLY, &nfsfh);
if (ret != 0) {
printf("Failed to open(%s) %s\n", NFSFILEW, nfs_get_error(nfs));
exit(10);
}
ret = nfs_pwrite_sync(nfs, nfsfh, 0, 16, buf);
if (ret < 0) {
printf("Failed to pwrite(%s) %s\n", NFSFILEW, nfs_get_error(nfs));
exit(10);
}
ret = nfs_fsync_sync(nfs, nfsfh);
if (ret < 0) {
printf("Failed to fsync(%s) %s\n", NFSFILEW, nfs_get_error(nfs));
exit(10);
}
ret = nfs_close_sync(nfs, nfsfh);
if (ret < 0) {
printf("Failed to close(%s)\n", NFSFILEW, nfs_get_error(nfs));
exit(10);
}
ret = nfs_statvfs_sync(nfs, NFSDIR, &svfs);
if (ret < 0) {
printf("Failed to statvfs(%s)\n", NFSDIR, nfs_get_error(nfs));
exit(10);
}
printf("files %d/%d/%d\n", (int)svfs.f_files, (int)svfs.f_ffree, (int)svfs.f_favail);
ret = nfs_access_sync(nfs, NFSFILE, R_OK);
if (ret != 0) {
printf("Failed to access(%s) %s\n", NFSFILE, nfs_get_error(nfs));
}
/* become root */
nfs_set_auth(nfs, authunix_create("Ronnies-Laptop", 0, 0, 0, NULL));
ret = nfs_link_sync(nfs, NFSFILE, NFSFILER);
if (ret != 0) {
printf("Failed to link(%s) %s\n", NFSFILE, nfs_get_error(nfs));
}
nfs_destroy_context(nfs);
printf("nfsclient finished\n");
return 0;
}
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <strings.h>
#include <rpc/xdr.h>
#include <rpc/rpc_msg.h>
#include "dlinklist.h"
#include "libnfs.h"
#include "libnfs-raw.h"
#include "libnfs-private.h"
struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int version, int procedure, rpc_cb cb, void *private_data, xdrproc_t xdr_decode_fn, int xdr_decode_bufsize)
{
struct rpc_pdu *pdu;
struct rpc_msg msg;
if (rpc == NULL) {
printf("trying to allocate rpc pdu on NULL context\n");
return NULL;
}
pdu = malloc(sizeof(struct rpc_pdu));
if (pdu == NULL) {
printf("Failed to allocate pdu structure\n");
return NULL;
}
bzero(pdu, sizeof(struct rpc_pdu));
pdu->xid = rpc->xid++;
pdu->cb = cb;
pdu->private_data = private_data;
pdu->xdr_decode_fn = xdr_decode_fn;
pdu->xdr_decode_bufsize = xdr_decode_bufsize;
xdrmem_create(&pdu->xdr, rpc->encodebuf, rpc->encodebuflen, XDR_ENCODE);
xdr_setpos(&pdu->xdr, 4); /* skip past the record marker */
bzero(&msg, sizeof(struct rpc_msg));
msg.rm_xid = pdu->xid;
msg.rm_direction = CALL;
msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
msg.rm_call.cb_prog = program;
msg.rm_call.cb_vers = version;
msg.rm_call.cb_proc = procedure;
msg.rm_call.cb_cred = rpc->auth->ah_cred;
msg.rm_call.cb_verf = rpc->auth->ah_verf;
if (xdr_callmsg(&pdu->xdr, &msg) == 0) {
printf("xdr_callmsg failed\n");
xdr_destroy(&pdu->xdr);
free(pdu);
return NULL;
}
return pdu;
}
void rpc_free_pdu(struct rpc_context *rpc _U_, struct rpc_pdu *pdu)
{
if (pdu->outdata.data != NULL) {
free(pdu->outdata.data);
pdu->outdata.data = NULL;
}
if (pdu->xdr_decode_buf != NULL) {
xdr_free(pdu->xdr_decode_fn, pdu->xdr_decode_buf);
free(pdu->xdr_decode_buf);
pdu->xdr_decode_buf = NULL;
}
xdr_destroy(&pdu->xdr);
free(pdu);
}
int rpc_queue_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu)
{
int size, recordmarker;
size = xdr_getpos(&pdu->xdr);
/* write recordmarker */
xdr_setpos(&pdu->xdr, 0);
recordmarker = (size - 4) | 0x80000000;
xdr_int(&pdu->xdr, &recordmarker);
pdu->outdata.size = size;
pdu->outdata.data = malloc(pdu->outdata.size);
if (pdu->outdata.data == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate buffer for pdu\n");
rpc_free_pdu(rpc, pdu);
return -1;
}
memcpy(pdu->outdata.data, rpc->encodebuf, pdu->outdata.size);
DLIST_ADD_END(rpc->outqueue, pdu, NULL);
return 0;
}
int rpc_get_pdu_size(char *buf)
{
uint32_t size;
size = ntohl(*(uint32_t *)buf);
if ((size & 0x80000000) == 0) {
printf("cant handle oncrpc fragments\n");
return -1;
}
return (size & 0x7fffffff) + 4;
}
static int rpc_process_reply(struct rpc_context *rpc, struct rpc_pdu *pdu, XDR *xdr)
{
struct rpc_msg msg;
bzero(&msg, sizeof(struct rpc_msg));
msg.acpted_rply.ar_verf = _null_auth;
if (pdu->xdr_decode_bufsize > 0) {
pdu->xdr_decode_buf = malloc(pdu->xdr_decode_bufsize);
if (pdu->xdr_decode_buf == NULL) {
printf("xdr_replymsg failed in portmap_getport_reply\n");
pdu->cb(rpc, RPC_STATUS_ERROR, "Failed to allocate buffer for decoding of XDR reply", pdu->private_data);
return 0;
}
bzero(pdu->xdr_decode_buf, pdu->xdr_decode_bufsize);
}
msg.acpted_rply.ar_results.where = pdu->xdr_decode_buf;
msg.acpted_rply.ar_results.proc = pdu->xdr_decode_fn;
if (xdr_replymsg(xdr, &msg) == 0) {
printf("xdr_replymsg failed in portmap_getport_reply\n");
pdu->cb(rpc, RPC_STATUS_ERROR, "Message rejected by server", pdu->private_data);
if (pdu->xdr_decode_buf != NULL) {
free(pdu->xdr_decode_buf);
pdu->xdr_decode_buf = NULL;
}
return 0;
}
if (msg.rm_reply.rp_stat != MSG_ACCEPTED) {
pdu->cb(rpc, RPC_STATUS_ERROR, "RPC Packet not accepted by the server", pdu->private_data);
return 0;
}
switch (msg.rm_reply.rp_acpt.ar_stat) {
case SUCCESS:
pdu->cb(rpc, RPC_STATUS_SUCCESS, pdu->xdr_decode_buf, pdu->private_data);
break;
case PROG_UNAVAIL:
pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: Program not available", pdu->private_data);
break;
case PROG_MISMATCH:
pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: Program version mismatch", pdu->private_data);
break;
case PROC_UNAVAIL:
pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: Procedure not available", pdu->private_data);
break;
case GARBAGE_ARGS:
pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: Garbage arguments", pdu->private_data);
break;
case SYSTEM_ERR:
pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: System Error", pdu->private_data);
break;
default:
pdu->cb(rpc, RPC_STATUS_ERROR, "Unknown rpc response from server", pdu->private_data);
break;
}
return 0;
}
int rpc_process_pdu(struct rpc_context *rpc, char *buf, int size)
{
struct rpc_pdu *pdu;
XDR xdr;
int pos, recordmarker;
unsigned int xid;
bzero(&xdr, sizeof(XDR));
xdrmem_create(&xdr, buf, size, XDR_DECODE);
if (xdr_int(&xdr, &recordmarker) == 0) {
printf("xdr_int reading recordmarker failed\n");
xdr_destroy(&xdr);
return -1;
}
pos = xdr_getpos(&xdr);
if (xdr_int(&xdr, (int *)&xid) == 0) {
printf("xdr_int reading xid failed\n");
xdr_destroy(&xdr);
return -1;
}
xdr_setpos(&xdr, pos);
for (pdu=rpc->waitpdu; pdu; pdu=pdu->next) {
if (pdu->xid != xid) {
continue;
}
DLIST_REMOVE(rpc->waitpdu, pdu);
if (rpc_process_reply(rpc, pdu, &xdr) != 0) {
printf("rpc_procdess_reply failed\n");
}
xdr_destroy(&xdr);
rpc_free_pdu(rpc, pdu);
return 0;
}
printf("No matching pdu found for xid:%d\n", xid);
xdr_destroy(&xdr);
return -1;
}
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <rpc/xdr.h>
#include "libnfs.h"
#include "libnfs-raw.h"
#include "libnfs-private.h"
#include "libnfs-raw-portmap.h"
int rpc_pmap_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, PMAP_PROGRAM, PMAP_V2, PMAP_NULL, cb, private_data, (xdrproc_t)xdr_void, 0);
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for portmap/null call");
return -1;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for portmap/null call");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
int rpc_pmap_getport_async(struct rpc_context *rpc, int program, int version, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
struct mapping m;
pdu = rpc_allocate_pdu(rpc, PMAP_PROGRAM, PMAP_V2, PMAP_GETPORT, cb, private_data, (xdrproc_t)xdr_int, sizeof(uint32_t));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for portmap/getport call");
return -1;
}
m.prog = program;
m.vers = version;
m.prot = IPPROTO_TCP;
m.port = 0;
if (xdr_mapping(&pdu->xdr, &m) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode data for portmap/getport call");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
printf("Failed to queue portmap/getport pdu\n");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
/*
* From RFC1833
*/
const PMAP_PORT = 111; /* portmapper port number */
struct mapping {
unsigned int prog;
unsigned int vers;
unsigned int prot;
unsigned int port;
};
struct call_args {
unsigned int prog;
unsigned int vers;
unsigned int proc;
opaque args<>;
};
program PMAP_PROGRAM {
version PMAP_V2 {
void
PMAP_NULL(void) = 0;
bool
PMAP_SET(mapping) = 1;
bool
PMAP_UNSET(mapping) = 2;
unsigned int
PMAP_GETPORT(mapping) = 3;
} = 2;
} = 100000;
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <rpc/xdr.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include "libnfs.h"
#include "libnfs-raw.h"
#include "libnfs-private.h"
#include "dlinklist.h"
static void set_nonblocking(int fd)
{
unsigned v;
v = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, v | O_NONBLOCK);
}
int rpc_get_fd(struct rpc_context *rpc)
{
return rpc->fd;
}
int rpc_which_events(struct rpc_context *rpc)
{
int events = POLLIN;
if (rpc->is_connected == 0) {
events |= POLLOUT;
}
if (rpc->outqueue) {
events |= POLLOUT;
}
return events;
}
static int rpc_write_to_socket(struct rpc_context *rpc)
{
ssize_t count;
if (rpc == NULL) {
printf("trying to write to socket for NULL context\n");
return -1;
}
if (rpc->fd == -1) {
printf("trying to write but not connected\n");
return -2;
}
while (rpc->outqueue != NULL) {
ssize_t total;
total = rpc->outqueue->outdata.size;
count = write(rpc->fd, rpc->outqueue->outdata.data + rpc->outqueue->written, total - rpc->outqueue->written);
if (count == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("socket would block, return from write to socket\n");
return 0;
}
printf("Error when writing to socket :%s(%d)\n", strerror(errno), errno);
return -3;
}
rpc->outqueue->written += count;
if (rpc->outqueue->written == total) {
struct rpc_pdu *pdu = rpc->outqueue;
DLIST_REMOVE(rpc->outqueue, pdu);
DLIST_ADD_END(rpc->waitpdu, pdu, NULL);
}
}
return 0;
}
static int rpc_read_from_socket(struct rpc_context *rpc)
{
int available;
int size;
unsigned char *buf;
ssize_t count;
if (ioctl(rpc->fd, FIONREAD, &available) != 0) {
rpc_set_error(rpc, "Ioctl FIONREAD returned error : %d. Closing socket.", errno);
return -1;
}
if (available == 0) {
rpc_set_error(rpc, "Socket has been closed");
return -2;
}
size = rpc->insize - rpc->inpos + available;
buf = malloc(size);
if (buf == NULL) {
rpc_set_error(rpc, "Out of memory: failed to allocate %d bytes for input buffer. Closing socket.", size);
return -3;
}
if (rpc->insize > rpc->inpos) {
memcpy(buf, rpc->inbuf + rpc->inpos, rpc->insize - rpc->inpos);
rpc->insize -= rpc->inpos;
rpc->inpos = 0;
}
count = read(rpc->fd, buf + rpc->insize, available);
if (count == -1) {
if (errno == EINTR) {
free(buf);
buf = NULL;
return 0;
}
rpc_set_error(rpc, "Read from socket failed, errno:%d. Closing socket.", errno);
free(buf);
buf = NULL;
return -4;
}
if (rpc->inbuf != NULL) {
free(rpc->inbuf);
}
rpc->inbuf = (char *)buf;
rpc->insize += count;
while (1) {
if (rpc->insize - rpc->inpos < 4) {
return 0;
}
count = rpc_get_pdu_size(rpc->inbuf + rpc->inpos);
if (rpc->insize + rpc->inpos < count) {
return 0;
}
if (rpc_process_pdu(rpc, rpc->inbuf + rpc->inpos, count) != 0) {
rpc_set_error(rpc, "Invalid/garbage pdu received from server. Closing socket");
return -5;
}
rpc->inpos += count;
if (rpc->inpos == rpc->insize) {
free(rpc->inbuf);
rpc->inbuf = NULL;
rpc->insize = 0;
rpc->inpos = 0;
}
}
return 0;
}
int rpc_service(struct rpc_context *rpc, int revents)
{
if (revents & POLLERR) {
printf("rpc_service: POLLERR, socket error\n");
if (rpc->is_connected == 0) {
rpc_set_error(rpc, "Failed to connect to server socket.");
} else {
rpc_set_error(rpc, "Socket closed with POLLERR");
}
rpc->connect_cb(rpc, RPC_STATUS_ERROR, rpc->error_string, rpc->connect_data);
return -1;
}
if (revents & POLLHUP) {
printf("rpc_service: POLLHUP, socket error\n");
rpc_set_error(rpc, "Socket failed with POLLHUP");
rpc->connect_cb(rpc, RPC_STATUS_ERROR, rpc->error_string, rpc->connect_data);
return -2;
}
if (rpc->is_connected == 0 && rpc->fd != -1 && revents&POLLOUT) {
rpc->is_connected = 1;
rpc->connect_cb(rpc, RPC_STATUS_SUCCESS, NULL, rpc->connect_data);
return 0;
}
if (revents & POLLOUT && rpc->outqueue != NULL) {
if (rpc_write_to_socket(rpc) != 0) {
printf("write to socket failed\n");
return -3;
}
}
if (revents & POLLIN) {
if (rpc_read_from_socket(rpc) != 0) {
rpc_disconnect(rpc, rpc_get_error(rpc));
return -4;
}
}
return 0;
}
int rpc_connect_async(struct rpc_context *rpc, const char *server, int port, int use_privileged_port, rpc_cb cb, void *private_data)
{
struct sockaddr_storage s;
struct sockaddr_in *sin = (struct sockaddr_in *)&s;
int socksize;
if (rpc->fd != -1) {
rpc_set_error(rpc, "Trying to connect while already connected");
printf("%s\n", rpc->error_string);
return -1;
}
sin->sin_family = AF_INET;
sin->sin_port = htons(port);
if (inet_pton(AF_INET, server, &sin->sin_addr) != 1) {
rpc_set_error(rpc, "Not a valid server ip address");
printf("%s\n", rpc->error_string);
return -2;
}
switch (s.ss_family) {
case AF_INET:
rpc->fd = socket(AF_INET, SOCK_STREAM, 0);
socksize = sizeof(struct sockaddr_in);
break;
}
if (rpc->fd == -1) {
rpc_set_error(rpc, "Failed to open socket");
printf("%s\n", rpc->error_string);
return -3;
}
/* if we are root, try to find a privileged port to use (512 - 1023) */
if (geteuid() == 0 && use_privileged_port != 0) {
struct sockaddr_storage ls;
int ret, count = 0;
static int local_port = 0;
if (local_port == 0) {
srandom(getpid() ^ time(NULL));
local_port = random()%512 + 512;
}
do {
count ++;
if (local_port >= 1024) {
local_port = 512;
}
switch (s.ss_family) {
case AF_INET:
bzero(&ls, socksize);
((struct sockaddr_in *)&ls)->sin_family = AF_INET;
((struct sockaddr_in *)&ls)->sin_addr.s_addr = INADDR_ANY;
((struct sockaddr_in *)&ls)->sin_port = htons(local_port++);
break;
}
ret = bind(rpc->fd, (struct sockaddr *)&ls, socksize);
} while (ret != 0 && count < 50);
}
rpc->connect_cb = cb;
rpc->connect_data = private_data;
set_nonblocking(rpc->fd);
if (connect(rpc->fd, (struct sockaddr *)&s, socksize) != 0 && errno != EINPROGRESS) {
rpc_set_error(rpc, "connect() to server failed");
printf("%s\n", rpc->error_string);
return -4;
}
return 0;
}
int rpc_disconnect(struct rpc_context *rpc, char *error)
{
if (rpc->fd != -1) {
close(rpc->fd);
}
rpc->fd = -1;
rpc->is_connected = 0;
rpc_error_all_pdus(rpc, error);
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment