INSTALLCMD = @install@ -c
CC=gcc
-CFLAGS=-g -O0 -fPIC -Wall -W -I. -Iinclude -Imount -Infs -Iportmap "-D_U_=__attribute__((unused))" -D_FILE_OFFSET_BITS=64
+CFLAGS=-g -O0 -fPIC -Wall -W -I. -Iinclude -Imount -Infs -Iportmap -Irquota "-D_U_=__attribute__((unused))" -D_FILE_OFFSET_BITS=64
LIBNFS_A=lib/libnfs.a
LIBNFS_SO_NAME=libnfs.so.1
LIBNFS_OBJS = \
mount/libnfs-raw-mount.o mount/mount.o \
portmap/libnfs-raw-portmap.o portmap/portmap.o \
-nfs/libnfs-raw-nfs.o nfs/nfs.o
+nfs/libnfs-raw-nfs.o nfs/nfs.o \
+rquota/libnfs-raw-rquota.o rquota/rquota.o
EXAMPLES=bin/nfsclient-raw bin/nfsclient-async bin/nfsclient-sync
@echo Compiling $@
$(CC) $(CFLAGS) -c nfs/libnfs-raw-nfs.c -o $@
+rquota/libnfs-raw-rquota.h: rquota/rquota.x
+ @echo Generating $@
+ rpcgen -h rquota/rquota.x > rquota/libnfs-raw-rquota.h
+
+rquota/libnfs-raw-rquota.c: rquota/rquota.x
+ @echo Generating $@
+ rpcgen -c rquota/rquota.x | sed -e "s/#include \"rquota\/rquota.h\"/#include \"libnfs-raw-rquota.h\"/" > rquota/libnfs-raw-rquota.c
+
+rquota/libnfs-raw-rquota.o: rquota/libnfs-raw-rquota.c rquota/libnfs-raw-rquota.h
+ @echo Compiling $@
+ $(CC) $(CFLAGS) -c rquota/libnfs-raw-rquota.c -o $@
+
install: $(LIBNFS_A) $(LIBNFS_SO)
ifeq ("$(LIBDIR)x","x")
$(INSTALLCMD) -m 755 $(LIBNFS_SO) $(libdir)
rm -f mount/*.o mount/libnfs-raw-mount.h mount/libnfs-raw-mount.c
rm -f nfs/*.o nfs/libnfs-raw-nfs.h nfs/libnfs-raw-nfs.c
rm -f portmap/*.o portmap/libnfs-raw-portmap.h portmap/libnfs-raw-portmap.c
+ rm -f rquota/*.o rquota/libnfs-raw-rquota.h rquota/libnfs-raw-rquota.c
rm -f nfsclient-raw nfsclient-async nfsclient-sync
#include "libnfs-raw.h"
#include "libnfs-raw-mount.h"
#include "libnfs-raw-nfs.h"
+#include "libnfs-raw-rquota.h"
struct client {
char *server;
char *export;
uint32_t mount_port;
+ uint32_t rquota_port;
int is_finished;
struct nfs_fh3 rootfh;
};
+void rquota_getquota_cb(struct rpc_context *rpc _U_, int status, void *data, void *private_data)
+{
+ struct client *client = private_data;
+ GETQUOTA1res *res = data;
+
+ if (status == RPC_STATUS_ERROR) {
+ printf("rquota/getquota call failed with \"%s\"\n", (char *)data);
+ exit(10);
+ }
+ if (status != RPC_STATUS_SUCCESS) {
+ printf("rquota/getquota call to server %s failed, status:%d\n", client->server, status);
+ exit(10);
+ }
+
+ printf("rquota responded ok\n");
+ client->is_finished = 1;
+}
+
+void rquota_connect_cb(struct rpc_context *rpc, int status, void *data _U_, void *private_data)
+{
+ struct client *client = private_data;
+
+ if (status != RPC_STATUS_SUCCESS) {
+ printf("connection to RPC.RQUOTAD on server %s failed\n", client->server);
+ exit(10);
+ }
+
+ printf("Connected to RPC.RQUOTAD on %s:%d\n", client->server, client->rquota_port);
+ printf("Send GETQUOTA request for uid 100\n");
+ if (rpc_rquota1_getquota_async(rpc, rquota_getquota_cb, EXPORT, 100, client) != 0) {
+ printf("Failed to send fsinfo request\n");
+ exit(10);
+ }
+}
+
void nfs_fsinfo_cb(struct rpc_context *rpc _U_, int status, void *data, void *private_data)
{
struct client *client = private_data;
printf("Got reply from server for NFS/FSINFO procedure.\n");
printf("Read Max:%d\n", (int)res->FSINFO3res_u.resok.rtmax);
printf("Write Max:%d\n", (int)res->FSINFO3res_u.resok.wtmax);
- client->is_finished = 1;
+
+ printf("Disconnect socket from nfs server\n");
+ if (rpc_disconnect(rpc, "normal disconnect") != 0) {
+ printf("Failed to disconnect socket to nfs\n");
+ exit(10);
+ }
+
+ printf("Connect to RPC.RQUOTAD on %s:%d\n", client->server, client->rquota_port);
+ if (rpc_connect_async(rpc, client->server, client->rquota_port, rquota_connect_cb, client) != 0) {
+ printf("Failed to start connection\n");
+ exit(10);
+ }
}
exit(10);
}
- printf("Connected to RPC.NFSDD on %s:%d\n", client->server, client->mount_port);
+ printf("Connected to RPC.NFSD on %s:%d\n", client->server, client->mount_port);
printf("Send FSINFO request\n");
if (rpc_nfs_fsinfo_async(rpc, nfs_fsinfo_cb, &client->rootfh, client) != 0) {
printf("Failed to send fsinfo request\n");
exit(10);
}
- printf("Connect to RPC.NFSDD on %s:%d\n", client->server, 2049);
+ printf("Connect to RPC.NFSD on %s:%d\n", client->server, 2049);
if (rpc_connect_async(rpc, client->server, 2049, nfs_connect_cb, client) != 0) {
printf("Failed to start connection\n");
exit(10);
}
-void pmap_getport_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
+void pmap_getport2_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
{
struct client *client = private_data;
}
client->mount_port = *(uint32_t *)data;
- printf("GETPORT returned Port:%d\n", client->mount_port);
+ printf("GETPORT returned RPC.MOUNTD is on port:%d\n", client->mount_port);
if (client->mount_port == 0) {
printf("RPC.MOUNTD is not available on server : %s:%d\n", client->server, client->mount_port);
exit(10);
}
}
+void pmap_getport1_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
+{
+ struct client *client = private_data;
+
+ 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->rquota_port = *(uint32_t *)data;
+ printf("GETPORT returned RPC.RQUOTAD on port:%d\n", client->rquota_port);
+ if (client->rquota_port == 0) {
+ printf("RPC.RQUOTAD is not available on server : %s:%d\n", client->server, client->rquota_port);
+ exit(10);
+ }
+
+ printf("Send getport request asking for MOUNT port\n");
+ if (rpc_pmap_getport_async(rpc, MOUNT_PROGRAM, MOUNT_V3, pmap_getport2_cb, client) != 0) {
+ printf("Failed to send getport request\n");
+ exit(10);
+ }
+}
void pmap_null_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
{
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) {
+ if (rpc_pmap_getport_async(rpc, RQUOTA_PROGRAM, RQUOTA_V1, pmap_getport1_cb, client) != 0) {
printf("Failed to send getport request\n");
exit(10);
}
+
+/*
+ * RQUOTA FUNCTIONS
+ */
+char *rquotastat_to_str(int error);
+int rquotastat_to_errno(int error);
+
+/*
+ * Call RQUOTA1/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 rquota daemon.
+ * data is NULL.
+ * RPC_STATUS_ERROR : An error occured when trying to contact the rquota daemon.
+ * data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ * data is NULL.
+ */
+int rpc_rquota1_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
+
+/*
+ * Call RQUOTA1/GETQUOTA
+ * 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 rquota daemon.
+ * data is a RQUOTA1res structure.
+ * RPC_STATUS_ERROR : An error occured when trying to contact the rquota daemon.
+ * data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ * data is NULL.
+ */
+int rpc_rquota1_getquota_async(struct rpc_context *rpc, rpc_cb cb, char *export, int uid, void *private_data);
--- /dev/null
+/*
+ Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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-rquota.h"
+
+
+
+char *rquotastat_to_str(int error)
+{
+ switch (error) {
+ case RQUOTA_OK: return "RQUOTA_OK"; break;
+ case RQUOTA_NOQUOTA: return "RQUOTA_NOQUOTA"; break;
+ case RQUOTA_EPERM: return "RQUOTA_EPERM"; break;
+ };
+ return "unknown rquota error";
+}
+
+int rquotastat_to_errno(int error)
+{
+ switch (error) {
+ case RQUOTA_OK: return 0; break;
+ case RQUOTA_NOQUOTA: return -ENOENT; break;
+ case RQUOTA_EPERM: return -EPERM; break;
+ };
+ return -ENOENT;
+}
+
+
+int rpc_rquota1_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
+{
+ struct rpc_pdu *pdu;
+
+ pdu = rpc_allocate_pdu(rpc, RQUOTA_PROGRAM, RQUOTA_V1, RQUOTA1_NULL, cb, private_data, (xdrproc_t)xdr_void, 0);
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for rquota/null call");
+ return -1;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for rquota/null call");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ return 0;
+}
+
+int rpc_rquota1_getquota_async(struct rpc_context *rpc, rpc_cb cb, char *export, int uid, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ GETQUOTA1args args;
+
+ pdu = rpc_allocate_pdu(rpc, RQUOTA_PROGRAM, RQUOTA_V1, RQUOTA1_GETQUOTA, cb, private_data, (xdrproc_t)xdr_GETQUOTA1res, sizeof(GETQUOTA1res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for rquota/getquota call");
+ return -1;
+ }
+
+ args.export = export;
+ args.uid = uid;
+
+ if (xdr_GETQUOTA1args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode GETQUOTA1args");
+ 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 rquota/getquota call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
--- /dev/null
+/* implementation based on wireshark c-code */
+
+const RQUOTAPATHLEN = 1024; /* Guess this is max. It is max for mount so probably rquota too */
+
+enum rquotastat {
+ RQUOTA_OK = 1,
+ RQUOTA_NOQUOTA = 2,
+ RQUOTA_EPERM = 3
+};
+
+typedef string exportpath<RQUOTAPATHLEN>;
+
+struct GETQUOTA1args {
+ exportpath export;
+ int uid;
+};
+
+struct GETQUOTA1res_ok {
+ int bsize;
+ int active;
+ int bhardlimit;
+ int bsoftlimit;
+ int curblocks;
+ int fhardlimit;
+ int fsoftlimit;
+ int curfiles;
+ int btimeleft;
+ int ftimeleft;
+};
+
+union GETQUOTA1res switch (rquotastat status) {
+ case RQUOTA_OK:
+ GETQUOTA1res_ok quota;
+ default:
+ void;
+};
+
+program RQUOTA_PROGRAM {
+ version RQUOTA_V1 {
+ void
+ RQUOTA1_NULL(void) = 0;
+
+ GETQUOTA1res
+ RQUOTA1_GETQUOTA(GETQUOTA1args) = 1;
+ } = 1;
+} = 100011;
+