Merge tag 'libnfs-1.9.6' of https://github.com/sahlberg/libnfs into upstream upstream upstream/1.9.6
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Mon, 1 Dec 2014 18:01:56 +0000 (19:01 +0100)
committerJérôme Benoit <jerome.benoit@piment-noir.org>
Mon, 1 Dec 2014 18:01:56 +0000 (19:01 +0100)
Tag for 1.9.6

Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
Conflicts:
README
configure.ac
examples/fuse_nfs.c
include/nfsc/libnfs-zdr.h
include/nfsc/libnfs.h
lib/Makefile.am
lib/libnfs-sync.c
lib/libnfs-zdr.c
lib/libnfs.c
lib/socket.c
nfs/Makefile.am
nfs/libnfs-raw-nfs.c
nfs/libnfs-raw-nfs.h
nfs/nfs.c
nfs/nfs.x
nlm/libnfs-raw-nlm.c
nlm/libnfs-raw-nlm.h
nlm/nlm.x
packaging/RPM/libnfs.spec.in

19 files changed:
README
configure.ac
examples/fuse_nfs.c
include/nfsc/libnfs-zdr.h
include/nfsc/libnfs.h
lib/Makefile.am
lib/libnfs-sync.c
lib/libnfs-zdr.c
lib/libnfs.c
lib/socket.c
nfs/Makefile.am
nfs/libnfs-raw-nfs.c
nfs/libnfs-raw-nfs.h
nfs/nfs.c
nfs/nfs.x
nlm/libnfs-raw-nlm.c
nlm/libnfs-raw-nlm.h
nlm/nlm.x
packaging/RPM/libnfs.spec.in

diff --git a/README b/README
index b0655ea7ca37b4e441ea26d29b04b6d91c25986d..701a781cd46e06af65106ea7453ead114825dc29 100644 (file)
--- a/README
+++ b/README
@@ -115,7 +115,8 @@ patches to make it better instead.
 
 RELEASE TARBALLS
 ================
-Release tarballs are available at https://sites.google.com/site/libnfstarballs/li
+Release tarballs are available at
+https://sites.google.com/site/libnfstarballs/li
 
 
 
index b84a8c52db40c017202669beee7ea43beecbf46a..2e64295be8b37d782639bc579ac25a5dbc9f6eaa 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.50)
-AC_INIT([libnfs], [1.9.5], [ronniesahlberg@gmail.com])
+AC_INIT([libnfs], [1.9.6], [ronniesahlberg@gmail.com])
 AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE([foreign])
 AC_CANONICAL_HOST
@@ -72,10 +72,8 @@ case $host in
   *solaris*)
     AC_CHECK_HEADERS([sys/filio.h])
     AC_CHECK_HEADERS([sys/sockio.h])
-    if test x$ENABLE_EXAMPLES = xyes; then
-      AC_CHECK_LIB([socket], [main], , [AC_MSG_ERROR([Can not find required library])])
-      AC_CHECK_LIB([nsl],    [main], , [AC_MSG_ERROR([Can not find required library])])
-    fi
+    AC_CHECK_LIB([socket], [main], , [AC_MSG_ERROR([Can not find required library])])
+    AC_CHECK_LIB([nsl],    [main], , [AC_MSG_ERROR([Can not find required library])])
     ;;
   *)
     ;;
@@ -153,6 +151,11 @@ AC_CHECK_MEMBER([struct sockaddr_storage.ss_family],
 #include <sys/socket.h>
 ])
 
+AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec])
+
+# check where makedev is defined
+AC_HEADER_MAJOR
+
 #output
 AC_CONFIG_FILES([Makefile]
                 [doc/Makefile]
index 261a6aabd28c5b540ab78dfa4b2bc7fa69fa1f41..28449eec4c88c52d7520e5cf4b4b4668ea7d23ac 100644 (file)
@@ -191,7 +191,7 @@ static struct fuse_operations nfs_oper = {
 
 void print_usage(char *name)
 {
-       printf("Usage: %s [-?|--help] [-n|--nfs-share=nfs-url] mountpoint\n",
+       printf("Usage: %s [-?|--help] [-n|--nfs-share=nfs-url] [-m|--mountpoint=mountpoint]\n",
                name);
        exit(0);
 }
@@ -248,11 +248,13 @@ int main(int argc, char *argv[])
 
        if (url == NULL) {
                fprintf(stderr, "-n was not specified.\n");
+               print_usage(argv[0]);
                ret = 10;
                goto finished;
        }
        if (mnt == NULL) {
                fprintf(stderr, "-m was not specified.\n");
+               print_usage(argv[0]);
                ret = 10;
                goto finished;
        }
index 6b2ea9123ce99ab86b17fa3cc839c9e230dc2a13..aa9f1d8d1a8fc6b593bb502e23de84529136b842 100644 (file)
@@ -230,11 +230,11 @@ bool_t libnfs_zdr_u_int(ZDR *zdrs, uint32_t *u);
 #define zdr_int libnfs_zdr_int
 bool_t libnfs_zdr_int(ZDR *zdrs, int32_t *i);
 
-#define zdr_u_quad_t libnfs_zdr_u_quad_t
-bool_t libnfs_zdr_u_quad_t(ZDR *zdrs, uint64_t *u);
+#define zdr_uint64_t libnfs_zdr_uint64_t
+bool_t libnfs_zdr_uint64_t(ZDR *zdrs, uint64_t *u);
 
-#define zdr_quad_t libnfs_zdr_quad_t
-bool_t libnfs_zdr_quad_t(ZDR *zdrs, int64_t *i);
+#define zdr_int64_t libnfs_zdr_int64_t
+bool_t libnfs_zdr_int64_t(ZDR *zdrs, int64_t *i);
 
 #define zdr_enum libnfs_zdr_enum
 bool_t libnfs_zdr_enum(ZDR *zdrs, enum_t *e);
index dfa2185a06b57854be7f3aea6a9df5291065fbde..670723240f06e7911036f6380d421daf8c1431ef 100644 (file)
@@ -270,6 +270,10 @@ struct nfs_stat_64 {
        uint64_t nfs_atime;
        uint64_t nfs_mtime;
        uint64_t nfs_ctime;
+       uint64_t nfs_atime_nsec;
+       uint64_t nfs_mtime_nsec;
+       uint64_t nfs_ctime_nsec;
+       uint64_t nfs_used;
 };
 
 /*
@@ -293,6 +297,35 @@ EXTERN int nfs_stat64_async(struct nfs_context *nfs, const char *path, nfs_cb cb
  */
 EXTERN int nfs_stat64(struct nfs_context *nfs, const char *path, struct nfs_stat_64 *st);
 
+/*
+ * Async stat(<filename>)
+ *
+ * Like stat except if the destination is a symbolic link, it acts on the
+ * symbolic link itself.
+ *
+ * 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 nfs_stat_64 *
+ * -errno : An error occured.
+ *          data is the error string.
+ */
+EXTERN int nfs_lstat64_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
+/*
+ * Sync stat(<filename>)
+ *
+ * Like stat except if the destination is a symbolic link, it acts on the
+ * symbolic link itself.
+ *
+ * Function returns
+ *      0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+EXTERN int nfs_lstat64(struct nfs_context *nfs, const char *path, struct nfs_stat_64 *st);
+
 /*
  * FSTAT()
  */
@@ -308,6 +341,7 @@ EXTERN int nfs_stat64(struct nfs_context *nfs, const char *path, struct nfs_stat
  * -errno : An error occured.
  *          data is the error string.
  */
+/* This function is deprecated. Use nfs_fstat64_async() instead */
 EXTERN int nfs_fstat_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data);
 /*
  * Sync fstat(nfsfh *)
@@ -321,6 +355,35 @@ EXTERN int nfs_fstat(struct nfs_context *nfs, struct nfsfh *nfsfh, struct __stat
 EXTERN int nfs_fstat(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *st);
 #endif
 
+/* nfs_fstat64
+ * 64 bit version of fstat. All fields are always 64bit.
+ * Use these functions instead of nfs_fstat[_async](), especially if you
+ * have weird stat structures.
+ */
+/*
+ * 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.
+ */
+EXTERN int nfs_fstat64_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.
+ */
+EXTERN int nfs_fstat64(struct nfs_context *nfs, struct nfsfh *nfsfh, struct nfs_stat_64 *st);
+
 
 
 /*
@@ -330,7 +393,7 @@ EXTERN int nfs_fstat(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *
  * Async open(<filename>)
  *
  * mode is a combination of the flags :
- * O_RDOLNY, O_WRONLY, O_RDWR , O_SYNC, O_APPEND
+ * O_RDOLNY, O_WRONLY, O_RDWR , O_SYNC, O_APPEND, O_TRUNC
  *
  * Function returns
  *  0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
@@ -341,6 +404,7 @@ EXTERN int nfs_fstat(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *
  * O_RDONLY
  * O_WRONLY
  * O_RDWR
+ * O_SYNC
  * O_TRUNC (Only valid with O_RDWR or O_WRONLY. Ignored otherwise.)
  *
  * When the callback is invoked, status indicates the result:
@@ -682,6 +746,34 @@ EXTERN int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode,
  */
 EXTERN int nfs_creat(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh);
 
+/*
+ * Async create()
+ *
+ * Same as nfs_creat_async but allows passing flags:
+ * O_APPEND
+ * O_SYNC
+ * O_EXCL
+ * O_TRUNC
+ *
+ * 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.
+ */
+EXTERN int nfs_create_async(struct nfs_context *nfs, const char *path, int flags, int mode, nfs_cb cb, void *private_data);
+/*
+ * Sync create()
+ * Function returns
+ *      0 : Success
+ * -errno : An error occured.
+ */
+EXTERN int nfs_create(struct nfs_context *nfs, const char *path, int flags, int mode, struct nfsfh **nfsfh);
+
 
 /*
  * MKNOD()
@@ -786,6 +878,14 @@ struct nfsdirent  {
        uint32_t uid;
        uint32_t gid;
        uint32_t nlink;
+       uint64_t dev;
+       uint64_t rdev;
+       uint64_t blksize;
+       uint64_t blocks;
+       uint64_t used;
+       uint32_t atime_nsec;
+       uint32_t mtime_nsec;
+       uint32_t ctime_nsec;
 };
 /*
  * nfs_readdir() never blocks, so no special sync/async versions are available
@@ -924,6 +1024,34 @@ EXTERN int nfs_chmod_async(struct nfs_context *nfs, const char *path, int mode,
  * -errno : The command failed.
  */
 EXTERN int nfs_chmod(struct nfs_context *nfs, const char *path, int mode);
+/*
+ * Async chmod(<name>)
+ *
+ * Like chmod except if the destination is a symbolic link, it acts on the
+ * symbolic link itself.
+ *
+ * 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.
+ */
+EXTERN int nfs_lchmod_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data);
+/*
+ * Sync chmod(<name>)
+ *
+ * Like chmod except if the destination is a symbolic link, it acts on the
+ * symbolic link itself.
+ *
+ * Function returns
+ *      0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+EXTERN int nfs_lchmod(struct nfs_context *nfs, const char *path, int mode);
 
 
 
@@ -976,6 +1104,34 @@ EXTERN int nfs_chown_async(struct nfs_context *nfs, const char *path, int uid, i
  * -errno : The command failed.
  */
 EXTERN int nfs_chown(struct nfs_context *nfs, const char *path, int uid, int gid);
+/*
+ * Async chown(<name>)
+ *
+ * Like chown except if the destination is a symbolic link, it acts on the
+ * symbolic link itself.
+ *
+ * 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.
+ */
+EXTERN int nfs_lchown_async(struct nfs_context *nfs, const char *path, int uid, int gid, nfs_cb cb, void *private_data);
+/*
+ * Sync chown(<name>)
+ *
+ * Like chown except if the destination is a symbolic link, it acts on the
+ * symbolic link itself.
+ *
+ * Function returns
+ *      0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+EXTERN int nfs_lchown(struct nfs_context *nfs, const char *path, int uid, int gid);
 
 
 
@@ -1029,6 +1185,34 @@ EXTERN int nfs_utimes_async(struct nfs_context *nfs, const char *path, struct ti
  * -errno : The command failed.
  */
 EXTERN int nfs_utimes(struct nfs_context *nfs, const char *path, struct timeval *times);
+/*
+ * Async utimes(<path>)
+ *
+ * Like utimes except if the destination is a symbolic link, it acts on the
+ * symbolic link itself.
+ *
+ * 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.
+ */
+EXTERN int nfs_lutimes_async(struct nfs_context *nfs, const char *path, struct timeval *times, nfs_cb cb, void *private_data);
+/*
+ * Sync utimes(<path>)
+ *
+ * Like utimes except if the destination is a symbolic link, it acts on the
+ * symbolic link itself.
+ *
+ * Function returns
+ *      0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+EXTERN int nfs_lutimes(struct nfs_context *nfs, const char *path, struct timeval *times);
 
 
 /*
@@ -1086,6 +1270,36 @@ EXTERN int nfs_access(struct nfs_context *nfs, const char *path, int mode);
 
 
 
+
+/*
+ * ACCESS2()
+ */
+/*
+ * Async access2(<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 : A mask of R_OK, W_OK and X_OK indicating which permissions are
+ *             available.
+ *             data is NULL
+ * -errno : An error occured.
+ *          data is the error string.
+ */
+EXTERN int nfs_access2_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
+/*
+ * Sync access(<path>)
+ * Function returns
+ *      >= 0 : A mask of R_OK, W_OK and X_OK indicating which permissions are
+ *             available.
+ * -errno : The command failed.
+ */
+EXTERN int nfs_access2(struct nfs_context *nfs, const char *path);
+
+
+
+
 /*
  * SYMLINK()
  */
index 2f12325d6bb22bf51a2f4da599bace7c665f5899..bbc29d25e3c039847802e3a05f3e2eccea0874d8 100644 (file)
@@ -18,9 +18,9 @@ libnfs_la_SOURCES = \
        pdu.c \
        socket.c
 
-SOCURRENT=6
+SOCURRENT=7
 SOREVISION=0
-SOAGE=2
+SOAGE=3
 libnfs_la_LDFLAGS = -version-info $(SOCURRENT):$(SOREVISION):$(SOAGE)
 libnfs_la_LIBADD = \
        ../mount/libmount.la \
index d4a54513f2647df3e3020bea932971223f3d8d70..59911fc867db45e4f4a2470870445f95774e6f29 100644 (file)
 #include <sys/sockio.h>
 #endif
 
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
 #include "libnfs-zdr.h"
 #include "libnfs.h"
 #include "libnfs-raw.h"
@@ -268,6 +272,23 @@ int nfs_stat64(struct nfs_context *nfs, const char *path, struct nfs_stat_64 *st
        return cb_data.status;
 }
 
+int nfs_lstat64(struct nfs_context *nfs, const char *path, struct nfs_stat_64 *st)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+       cb_data.return_data = st;
+
+       if (nfs_lstat64_async(nfs, path, stat64_cb, &cb_data) != 0) {
+               nfs_set_error(nfs, "nfs_lstat64_async failed");
+               return -1;
+       }
+
+       wait_for_nfs_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
 /*
  * open()
  */
@@ -453,6 +474,26 @@ int nfs_fstat(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *st)
        return cb_data.status;
 }
 
+/*
+ * fstat64()
+ */
+int nfs_fstat64(struct nfs_context *nfs, struct nfsfh *nfsfh, struct nfs_stat_64 *st)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+       cb_data.return_data = st;
+
+       if (nfs_fstat64_async(nfs, nfsfh, stat64_cb, &cb_data) != 0) {
+               nfs_set_error(nfs, "nfs_fstat64_async failed");
+               return -1;
+       }
+
+       wait_for_nfs_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
 
 /*
  * pwrite()
@@ -696,15 +737,15 @@ static void creat_cb(int status, struct nfs_context *nfs, void *data, void *priv
        *nfsfh = fh;
 }
 
-int nfs_creat(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh)
+int nfs_create(struct nfs_context *nfs, const char *path, int flags, 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) {
-               nfs_set_error(nfs, "nfs_creat_async failed");
+       if (nfs_create_async(nfs, path, flags, mode, creat_cb, &cb_data) != 0) {
+               nfs_set_error(nfs, "nfs_create_async failed");
                return -1;
        }
 
@@ -713,6 +754,11 @@ int nfs_creat(struct nfs_context *nfs, const char *path, int mode, struct nfsfh
        return cb_data.status;
 }
 
+int nfs_creat(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh)
+{
+       return nfs_create(nfs, path, 0, mode, nfsfh);
+}
+
 /*
  * mknod()
  */
@@ -973,6 +1019,22 @@ int nfs_chmod(struct nfs_context *nfs, const char *path, int mode)
        return cb_data.status;
 }
 
+int nfs_lchmod(struct nfs_context *nfs, const char *path, int mode)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_lchmod_async(nfs, path, mode, chmod_cb, &cb_data) != 0) {
+               nfs_set_error(nfs, "nfs_lchmod_async failed");
+               return -1;
+       }
+
+       wait_for_nfs_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
 
 
 
@@ -1043,6 +1105,25 @@ int nfs_chown(struct nfs_context *nfs, const char *path, int uid, int gid)
        return cb_data.status;
 }
 
+/*
+ * lchown()
+ */
+int nfs_lchown(struct nfs_context *nfs, const char *path, int uid, int gid)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_lchown_async(nfs, path, uid, gid, chown_cb, &cb_data) != 0) {
+               nfs_set_error(nfs, "nfs_lchown_async failed");
+               return -1;
+       }
+
+       wait_for_nfs_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
 /*
  * fchown()
  */
@@ -1109,6 +1190,22 @@ int nfs_utimes(struct nfs_context *nfs, const char *path, struct timeval *times)
        return cb_data.status;
 }
 
+int nfs_lutimes(struct nfs_context *nfs, const char *path, struct timeval *times)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_lutimes_async(nfs, path, times, utimes_cb, &cb_data) != 0) {
+               nfs_set_error(nfs, "nfs_lutimes_async failed");
+               return -1;
+       }
+
+       wait_for_nfs_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
 
 
 /*
@@ -1180,6 +1277,40 @@ int nfs_access(struct nfs_context *nfs, const char *path, int mode)
 
 
 
+/*
+ * access2()
+ */
+static void access2_cb(int status, struct nfs_context *nfs, 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) {
+               nfs_set_error(nfs, "access2 call failed with \"%s\"", (char *)data);
+               return;
+       }
+}
+
+int nfs_access2(struct nfs_context *nfs, const char *path)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_access2_async(nfs, path, access2_cb, &cb_data) != 0) {
+               nfs_set_error(nfs, "nfs_access2_async failed");
+               return -1;
+       }
+
+       wait_for_nfs_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
 /*
  * symlink()
  */
index c32f661fc86cc8170940673e4b10c3d4deebce2d..921ec5fe97a5ec3adb3503ad96edc5b937509c79 100644 (file)
  * i.e. zdrmem_create() buffers.
  * It aims to be compatible with normal rpcgen generated functions.
  */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 #ifdef WIN32
 #include "win32_compat.h"
 #endif
 #include "aros_compat.h"
 #endif
 
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#include <unistd.h>
 #include "libnfs-zdr.h"
+#include "libnfs.h"
+#include "libnfs-raw.h"
+#include "libnfs-private.h"
 
 struct opaque_auth _null_auth;
 
@@ -106,7 +118,7 @@ bool_t libnfs_zdr_int(ZDR *zdrs, int32_t *i)
        return libnfs_zdr_u_int(zdrs, (uint32_t *)i);
 }
 
-bool_t libnfs_zdr_u_quad_t(ZDR *zdrs, uint64_t *u)
+bool_t libnfs_zdr_uint64_t(ZDR *zdrs, uint64_t *u)
 {
        if (zdrs->pos + 8 > zdrs->size) {
                return FALSE;
@@ -133,9 +145,9 @@ bool_t libnfs_zdr_u_quad_t(ZDR *zdrs, uint64_t *u)
        return FALSE;
 }
 
-bool_t libnfs_zdr_quad_t(ZDR *zdrs, int64_t *i)
+bool_t libnfs_zdr_int64_t(ZDR *zdrs, int64_t *i)
 {
-       return libnfs_zdr_u_quad_t(zdrs, (uint64_t *)i);
+       return libnfs_zdr_uint64_t(zdrs, (uint64_t *)i);
 }
 
 bool_t libnfs_zdr_bytes(ZDR *zdrs, char **bufp, uint32_t *size, uint32_t maxsize)
index 94f03cb520455600f5d27c93301e8fecc1122dfb..2bacd9161702c020cf3ae63aac13c939e7db7929 100644 (file)
 #include <strings.h>
 #endif
 
+#ifdef MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#endif
+
+#ifdef MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#endif
+
 #include <stdio.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 #include <errno.h>
+#include <time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -78,6 +87,7 @@
 #include "libnfs-private.h"
 
 #define MAX_DIR_CACHE 128
+#define MAX_LINK_COUNT 40
 
 struct nfsdir {
        struct nfs_fh3 fh;
@@ -168,6 +178,7 @@ struct nfs_cb_data {
        struct nfs_context *nfs;
        struct nfsfh *nfsfh;
        char *saved_path, *path;
+       int link_count, no_follow;
 
        nfs_cb cb;
        void *private_data;
@@ -197,6 +208,7 @@ struct nfs_mcb_data {
 };
 
 static int nfs_lookup_path_async_internal(struct nfs_context *nfs, fattr3 *attr, struct nfs_cb_data *data, struct nfs_fh3 *fh);
+static int nfs_normalize_path(struct nfs_context *nfs, char *path);
 
 void nfs_set_auth(struct nfs_context *nfs, struct AUTH *auth)
 {
@@ -696,6 +708,16 @@ static void free_nfs_cb_data(struct nfs_cb_data *data)
        free(data);
 }
 
+static void free_nfsfh(struct nfsfh *nfsfh)
+{
+       if (nfsfh->fh.data.data_val != NULL) {
+               free(nfsfh->fh.data.data_val);
+               nfsfh->fh.data.data_val = NULL;
+       }
+       free(nfsfh->ra.buf);
+       free(nfsfh);
+}
+
 
 static void nfs_mount_10_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
 {
@@ -771,6 +793,9 @@ static void nfs_mount_8_cb(struct rpc_context *rpc, int status, void *command_da
                return;
        }
 
+       /* NFS TCP connections we want to autoreconnect after sessions are torn down (due to inactivity or error) */
+       rpc_set_autoreconnect(rpc);
+
        args.fsroot = nfs->rootfh;
        if (rpc_nfs3_fsinfo_async(rpc, nfs_mount_9_cb, &args, data) != 0) {
                data->cb(-ENOMEM, nfs, command_data, data->private_data);
@@ -823,9 +848,6 @@ static void nfs_mount_6_cb(struct rpc_context *rpc, int status, void *command_da
                free_nfs_cb_data(data);
                return;
        }
-
-       /* NFS TCP connections we want to autoreconnect after sessions are torn down (due to inactivity or error) */
-       rpc_set_autoreconnect(rpc);
 }
 
 
@@ -897,6 +919,97 @@ int nfs_mount_async(struct nfs_context *nfs, const char *server, const char *exp
  * 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_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;
+       READLINK3res *res;
+       char *path, *newpath;
+
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
+       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;
+       }
+
+       path = res->READLINK3res_u.resok.data;
+
+       /* Handle absolute paths, ensuring that the path lies within the
+        * export. */
+       if (path[0] == '/') {
+               if (strstr(path, nfs->export) == path) {
+                       char *ptr = path + strlen(nfs->export);
+                       if (*ptr == '/') {
+                               newpath = strdup(ptr);
+                       } else if (*ptr == '\0') {
+                               newpath = strdup("/");
+                       } else {
+                               data->cb(-ENOENT, nfs, "Symbolic link points outside export", data->private_data);
+                               free_nfs_cb_data(data);
+                               return;
+                       }
+               } else {
+                       data->cb(-ENOENT, nfs, "Symbolic link points outside export", data->private_data);
+                       free_nfs_cb_data(data);
+                       return;
+               }
+
+               if (!newpath)
+                       goto nomem;
+       } else {
+               /* Handle relative paths, both the case where the current
+                * component is an intermediate component and when it is the
+                * final component. */
+               if (data->path[0]) {
+                       /* Since path points to a component and saved_path
+                        * always starts with '/', path[-1] is valid. */
+                       data->path[-1] = '\0';
+                       newpath = malloc(strlen(data->saved_path) + strlen(path) + strlen(data->path) + 6);
+                       if (!newpath)
+                               goto nomem;
+
+                       sprintf(newpath, "%s/../%s/%s", data->saved_path, path, data->path);
+               } else {
+                       newpath = malloc(strlen(data->saved_path) + strlen(path) + 5);
+                       if (!newpath)
+                               goto nomem;
+
+                       sprintf(newpath, "%s/../%s", data->saved_path, path);
+               }
+       }
+       free(data->saved_path);
+       data->saved_path = newpath;
+
+       if (nfs_normalize_path(nfs, data->saved_path) != 0) {
+               data->cb(-ENOENT, nfs, "Symbolic link resolves to invalid path", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       data->path = data->saved_path;
+       nfs_lookup_path_async_internal(nfs, NULL, data, &nfs->rootfh);
+       return;
+
+nomem:
+       data->cb(-ENOMEM, nfs, "Failed to allocate memory for path", data->private_data);
+       free_nfs_cb_data(data);
+}
+
 static void nfs_lookup_path_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
 {
        struct nfs_cb_data *data = private_data;
@@ -935,7 +1048,7 @@ static void nfs_lookup_path_1_cb(struct rpc_context *rpc, int status, void *comm
        nfs_lookup_path_async_internal(nfs, attr, data, &res->LOOKUP3res_u.resok.object);
 }
 
-static int nfs_lookup_path_async_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data, struct nfs_fh3 *fh)
+static int nfs_lookup_path_async_internal(struct nfs_context *nfs, fattr3 *attr, struct nfs_cb_data *data, struct nfs_fh3 *fh)
 {
        char *path, *slash;
        LOOKUP3args args;
@@ -946,6 +1059,31 @@ static int nfs_lookup_path_async_internal(struct nfs_context *nfs, fattr3 *attr
 
        path = data->path;
        slash = strchr(path, '/');
+
+       if (attr && attr->type == NF3LNK && (!data->no_follow || *path != '\0')) {
+               READLINK3args args;
+
+               if (data->link_count++ >= MAX_LINK_COUNT) {
+                       data->cb(-ELOOP, nfs, "Too many levels of symbolic links", data->private_data);
+                       free_nfs_cb_data(data);
+                       return -1;
+               }
+
+               args.symlink = *fh;
+
+               if (rpc_nfs3_readlink_async(nfs->rpc, nfs_lookup_path_2_cb, &args, 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;
+               }
+
+               if (slash != NULL) {
+                       *slash = '/';
+               }
+               return 0;
+       }
+
        if (slash != NULL) {
                /* Clear slash so that path is a zero terminated string for
                 * the current path component. Set it back to '/' again later
@@ -1129,7 +1267,7 @@ static void nfs_lookup_path_getattr_cb(struct rpc_context *rpc, int status, void
        nfs_lookup_path_async_internal(nfs, attr, data, &nfs->rootfh);
 }
 
-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)
+static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, int no_follow, 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;
        struct GETATTR3args args;
@@ -1154,6 +1292,7 @@ static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, nfs_c
        data->free_continue_data = free_continue_data;
        data->continue_int       = continue_int;
        data->private_data       = private_data;
+       data->no_follow          = no_follow;
        if (path[0] == '/') {
                data->saved_path = strdup(path);
        } else {
@@ -1202,6 +1341,15 @@ static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, nfs_c
 /*
  * Async stat()
  */
+static dev_t specdata3_to_rdev(struct specdata3 *rdev)
+{
+#ifdef makedev
+       return makedev(rdev->specdata1, rdev->specdata2);
+#else
+       return 0;
+#endif
+}
+
 static void nfs_stat_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
 {
        GETATTR3res *res;
@@ -1234,27 +1382,49 @@ static void nfs_stat_1_cb(struct rpc_context *rpc, int status, void *command_dat
                return;
        }
 
-        st.st_dev     = -1;
+       st.st_dev     = res->GETATTR3res_u.resok.obj_attributes.fsid;
         st.st_ino     = res->GETATTR3res_u.resok.obj_attributes.fileid;
         st.st_mode    = res->GETATTR3res_u.resok.obj_attributes.mode;
-       if (res->GETATTR3res_u.resok.obj_attributes.type == NF3DIR) {
-               st.st_mode |= S_IFDIR ;
-       }
-       if (res->GETATTR3res_u.resok.obj_attributes.type == NF3REG) {
-               st.st_mode |= S_IFREG ;
+       switch (res->GETATTR3res_u.resok.obj_attributes.type) {
+       case NF3REG:
+               st.st_mode |= S_IFREG;
+               break;
+       case NF3DIR:
+               st.st_mode |= S_IFDIR;
+               break;
+       case NF3BLK:
+               st.st_mode |= S_IFBLK;
+               break;
+       case NF3CHR:
+               st.st_mode |= S_IFCHR;
+               break;
+       case NF3LNK:
+               st.st_mode |= S_IFLNK;
+               break;
+       case NF3SOCK:
+               st.st_mode |= S_IFSOCK;
+               break;
+       case NF3FIFO:
+               st.st_mode |= S_IFIFO;
+               break;
        }
         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_rdev    = specdata3_to_rdev(&res->GETATTR3res_u.resok.obj_attributes.rdev);
         st.st_size    = res->GETATTR3res_u.resok.obj_attributes.size;
 #ifndef WIN32
         st.st_blksize = NFS_BLKSIZE;
-        st.st_blocks  = res->GETATTR3res_u.resok.obj_attributes.size / NFS_BLKSIZE;
+       st.st_blocks  = (res->GETATTR3res_u.resok.obj_attributes.used + 512 - 1) / 512;
 #endif//WIN32
         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;
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+       st.st_atim.tv_nsec = res->GETATTR3res_u.resok.obj_attributes.atime.nseconds;
+       st.st_mtim.tv_nsec = res->GETATTR3res_u.resok.obj_attributes.mtime.nseconds;
+       st.st_ctim.tv_nsec = res->GETATTR3res_u.resok.obj_attributes.ctime.nseconds;
+#endif
 
        data->cb(0, nfs, &st, data->private_data);
        free_nfs_cb_data(data);
@@ -1278,7 +1448,7 @@ static int nfs_stat_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_,
 
 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) {
+       if (nfs_lookuppath_async(nfs, path, 0, cb, private_data, nfs_stat_continue_internal, NULL, NULL, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -1318,23 +1488,46 @@ static void nfs_stat64_1_cb(struct rpc_context *rpc, int status, void *command_d
                return;
        }
 
-        st.nfs_dev     = -1;
+       st.nfs_dev     = res->GETATTR3res_u.resok.obj_attributes.fsid;
         st.nfs_ino     = res->GETATTR3res_u.resok.obj_attributes.fileid;
         st.nfs_mode    = res->GETATTR3res_u.resok.obj_attributes.mode;
-       if (res->GETATTR3res_u.resok.obj_attributes.type == NF3DIR) {
-               st.nfs_mode |= S_IFDIR ;
-       }
-       if (res->GETATTR3res_u.resok.obj_attributes.type == NF3REG) {
-               st.nfs_mode |= S_IFREG ;
+       switch (res->GETATTR3res_u.resok.obj_attributes.type) {
+       case NF3REG:
+               st.nfs_mode |= S_IFREG;
+               break;
+       case NF3DIR:
+               st.nfs_mode |= S_IFDIR;
+               break;
+       case NF3BLK:
+               st.nfs_mode |= S_IFBLK;
+               break;
+       case NF3CHR:
+               st.nfs_mode |= S_IFCHR;
+               break;
+       case NF3LNK:
+               st.nfs_mode |= S_IFLNK;
+               break;
+       case NF3SOCK:
+               st.nfs_mode |= S_IFSOCK;
+               break;
+       case NF3FIFO:
+               st.nfs_mode |= S_IFIFO;
+               break;
        }
         st.nfs_nlink   = res->GETATTR3res_u.resok.obj_attributes.nlink;
         st.nfs_uid     = res->GETATTR3res_u.resok.obj_attributes.uid;
         st.nfs_gid     = res->GETATTR3res_u.resok.obj_attributes.gid;
-        st.nfs_rdev    = 0;
+       st.nfs_rdev    = specdata3_to_rdev(&res->GETATTR3res_u.resok.obj_attributes.rdev);
         st.nfs_size    = res->GETATTR3res_u.resok.obj_attributes.size;
+       st.nfs_blksize = NFS_BLKSIZE;
+       st.nfs_blocks  = (res->GETATTR3res_u.resok.obj_attributes.used + 512 - 1) / 512;
         st.nfs_atime   = res->GETATTR3res_u.resok.obj_attributes.atime.seconds;
         st.nfs_mtime   = res->GETATTR3res_u.resok.obj_attributes.mtime.seconds;
         st.nfs_ctime   = res->GETATTR3res_u.resok.obj_attributes.ctime.seconds;
+       st.nfs_atime_nsec = res->GETATTR3res_u.resok.obj_attributes.atime.nseconds;
+       st.nfs_mtime_nsec = res->GETATTR3res_u.resok.obj_attributes.mtime.nseconds;
+       st.nfs_ctime_nsec = res->GETATTR3res_u.resok.obj_attributes.ctime.nseconds;
+       st.nfs_used    = res->GETATTR3res_u.resok.obj_attributes.used;
 
        data->cb(0, nfs, &st, data->private_data);
        free_nfs_cb_data(data);
@@ -1356,9 +1549,9 @@ static int nfs_stat64_continue_internal(struct nfs_context *nfs, fattr3 *attr _U
        return 0;
 }
 
-int nfs_stat64_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
+int nfs_stat64_async_internal(struct nfs_context *nfs, const char *path, int no_follow, nfs_cb cb, void *private_data)
 {
-       if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_stat64_continue_internal, NULL, NULL, 0) != 0) {
+       if (nfs_lookuppath_async(nfs, path, no_follow, cb, private_data, nfs_stat64_continue_internal, NULL, NULL, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -1366,6 +1559,16 @@ int nfs_stat64_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void
        return 0;
 }
 
+int nfs_stat64_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
+{
+       return nfs_stat64_async_internal(nfs, path, 0, cb, private_data);
+}
+
+int nfs_lstat64_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
+{
+       return nfs_stat64_async_internal(nfs, path, 1, cb, private_data);
+}
+
 /*
  * Async open()
  */
@@ -1552,7 +1755,7 @@ static int nfs_open_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_,
 
 int nfs_open_async(struct nfs_context *nfs, const char *path, int flags, nfs_cb cb, void *private_data)
 {
-       if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_open_continue_internal, NULL, NULL, flags) != 0) {
+       if (nfs_lookuppath_async(nfs, path, 0, cb, private_data, nfs_open_continue_internal, NULL, NULL, flags) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -1579,7 +1782,7 @@ static int nfs_chdir_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_
 
 int nfs_chdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
 {
-       if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_chdir_continue_internal, NULL, NULL, 0) != 0) {
+       if (nfs_lookuppath_async(nfs, path, 0, cb, private_data, nfs_chdir_continue_internal, NULL, NULL, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -1605,6 +1808,8 @@ static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_dat
        struct nfs_cb_data *data = mdata->data;
        struct nfs_context *nfs = data->nfs;
        READ3res *res;
+       int cb_err;
+       void *cb_data;
 
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
@@ -1703,9 +1908,11 @@ static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_dat
                if (data->max_offset > data->org_offset + data->org_count) {
                        data->max_offset = data->org_offset + data->org_count;
                }
-               data->cb(data->max_offset - data->org_offset, nfs, data->buffer + (data->org_offset - data->offset), data->private_data);
+               cb_err = data->max_offset - data->org_offset;
+               cb_data = data->buffer + (data->org_offset - data->offset);
        } else {
-               data->cb(res->READ3res_u.resok.count, nfs, res->READ3res_u.resok.data.data_val, data->private_data);
+               cb_err = res->READ3res_u.resok.count;
+               cb_data = res->READ3res_u.resok.data.data_val;
        }
 
        data->nfsfh->ra.fh_offset = data->max_offset;
@@ -1717,6 +1924,8 @@ static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_dat
                data->nfsfh->ra.buf_ts = time(NULL);
                data->buffer = NULL;
        }
+
+       data->cb(cb_err, nfs, cb_data, data->private_data);
        free_nfs_cb_data(data);
 }
 
@@ -2135,13 +2344,7 @@ int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count
 
 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->ra.buf);
-       free(nfsfh);
-
+       free_nfsfh(nfsfh);
        cb(0, nfs, NULL, private_data);
        return 0;
 };
@@ -2180,6 +2383,36 @@ int nfs_fstat_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, voi
        return 0;
 }
 
+/*
+ * Async fstat64()
+ */
+int nfs_fstat64_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data)
+{
+       struct nfs_cb_data *data;
+       struct GETATTR3args 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");
+               return -1;
+       }
+       memset(data, 0, sizeof(struct nfs_cb_data));
+       data->nfs          = nfs;
+       data->cb           = cb;
+       data->private_data = private_data;
+
+       memset(&args, 0, sizeof(GETATTR3args));
+       args.object = nfsfh->fh;
+
+       if (rpc_nfs3_getattr_async(nfs->rpc, nfs_stat64_1_cb, &args, 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;
+}
+
 
 
 /*
@@ -2336,7 +2569,7 @@ int nfs_truncate_async(struct nfs_context *nfs, const char *path, uint64_t lengt
 
        offset = length;
 
-       if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_truncate_continue_internal, NULL, NULL, offset) != 0) {
+       if (nfs_lookuppath_async(nfs, path, 0, cb, private_data, nfs_truncate_continue_internal, NULL, NULL, offset) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -2426,7 +2659,7 @@ int nfs_mkdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *
        *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) {
+       if (nfs_lookuppath_async(nfs, new_path, 0, cb, private_data, nfs_mkdir_continue_internal, new_path, free, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path component");
                return -1;
        }
@@ -2513,7 +2746,7 @@ int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *
        *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) {
+       if (nfs_lookuppath_async(nfs, new_path, 0, cb, private_data, nfs_rmdir_continue_internal, new_path, free, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -2527,13 +2760,63 @@ int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *
 /*
  * Async creat()
  */
+struct create_cb_data {
+       char *path;
+       int flags;
+       int mode;
+};
+
+static void free_create_cb_data(void *ptr)
+{
+       struct create_cb_data *data = ptr;
+
+       free(data->path);
+       free(data);
+}
+
+static void nfs_create_trunc_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;
+       struct nfsfh *nfsfh = data->nfsfh;
+       SETATTR3res *res;
+
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               free_nfsfh(nfsfh);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               free_nfsfh(nfsfh);
+               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);
+               free_nfsfh(nfsfh);
+               return;
+       }
+
+       data->cb(0, nfs, nfsfh, data->private_data);
+       free_nfs_cb_data(data);
+}
+
 static void nfs_create_2_cb(struct rpc_context *rpc, 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;
+       struct create_cb_data *cb_data = data->continue_data;
+       char *str = cb_data->path;
 
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
@@ -2566,23 +2849,62 @@ static void nfs_create_2_cb(struct rpc_context *rpc, int status, void *command_d
        }
        memset(nfsfh, 0, sizeof(struct nfsfh));
 
+       if (cb_data->flags & O_SYNC) {
+               nfsfh->is_sync = 1;
+       }
+       if (cb_data->flags & O_APPEND) {
+               nfsfh->is_append = 1;
+       }
+
        /* copy the filehandle */
        nfsfh->fh.data.data_len = res->LOOKUP3res_u.resok.object.data.data_len;
        nfsfh->fh.data.data_val = malloc(nfsfh->fh.data.data_len);
+       if (nfsfh->fh.data.data_val == NULL) {
+               rpc_set_error(nfs->rpc, "Out of memory: Failed to allocate fh structure");
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               free(nfsfh);
+               return;
+       }
        memcpy(nfsfh->fh.data.data_val, res->LOOKUP3res_u.resok.object.data.data_val, nfsfh->fh.data.data_len);
 
+       /* Try to truncate it if we were requested to */
+       if (cb_data->flags & O_TRUNC) {
+               SETATTR3args args;
+
+               data->nfsfh = nfsfh;
+
+               memset(&args, 0, sizeof(SETATTR3args));
+               args.object = nfsfh->fh;
+               args.new_attributes.size.set_it = 1;
+               args.new_attributes.size.set_size3_u.size = 0;
+
+               if (rpc_nfs3_setattr_async(nfs->rpc, nfs_create_trunc_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);
+                       free_nfsfh(nfsfh);
+                       return;
+               }
+               return;
+       }
+
        data->cb(0, nfs, nfsfh, data->private_data);
        free_nfs_cb_data(data);
 }
 
 
 
-static void nfs_creat_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+static void nfs_create_1_cb(struct rpc_context *rpc, 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;
+       struct create_cb_data *cb_data = data->continue_data;
+       char *str = cb_data->path;
        LOOKUP3args args;
 
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
@@ -2620,9 +2942,10 @@ static void nfs_creat_1_cb(struct rpc_context *rpc, int status, void *command_da
        return;
 }
 
-static int nfs_creat_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data)
+static int nfs_create_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data)
 {
-       char *str = data->continue_data;
+       struct create_cb_data *cb_data = data->continue_data;
+       char *str = cb_data->path;
        CREATE3args args;
 
        str = &str[strlen(str) + 1];
@@ -2630,11 +2953,11 @@ static int nfs_creat_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_
        memset(&args, 0, sizeof(CREATE3args));
        args.where.dir = data->fh;
        args.where.name = str;
-       args.how.mode = UNCHECKED;
+       args.how.mode = (cb_data->flags & O_EXCL) ? GUARDED : UNCHECKED;
        args.how.createhow3_u.obj_attributes.mode.set_it = 1;
-       args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode = data->continue_int;
+       args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode = cb_data->mode;
 
-       if (rpc_nfs3_create_async(nfs->rpc, nfs_creat_1_cb, &args, data) != 0) {
+       if (rpc_nfs3_create_async(nfs->rpc, nfs_create_1_cb, &args, 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);
@@ -2643,27 +2966,37 @@ static int nfs_creat_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_
        return 0;
 }
 
-int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data)
+int nfs_create_async(struct nfs_context *nfs, const char *path, int flags, int mode, nfs_cb cb, void *private_data)
 {
-       char *new_path;
+       struct create_cb_data *cb_data;
        char *ptr;
 
-       new_path = strdup(path);
-       if (new_path == NULL) {
+       cb_data = malloc(sizeof(struct create_cb_data));
+       if (cb_data == NULL) {
+               rpc_set_error(nfs->rpc, "Out of memory, failed to allocate mode buffer for cb data");
+               return -1;
+       }
+
+       cb_data->path = strdup(path);
+       if (cb_data->path == NULL) {
                rpc_set_error(nfs->rpc, "Out of memory, failed to allocate mode buffer for path");
+               free(cb_data);
                return -1;
        }
 
-       ptr = strrchr(new_path, '/');
+       ptr = strrchr(cb_data->path, '/');
        if (ptr == NULL) {
                rpc_set_error(nfs->rpc, "Invalid path %s", path);
-               free(new_path);
+               free_create_cb_data(cb_data);
                return -1;
        }
        *ptr = 0;
 
+       cb_data->flags = flags;
+       cb_data->mode = mode;
+
        /* new_path now points to the parent directory,  and beyond the nul terminator 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) {
+       if (nfs_lookuppath_async(nfs, cb_data->path, 0, cb, private_data, nfs_create_continue_internal, cb_data, free_create_cb_data, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -2671,6 +3004,11 @@ int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb
        return 0;
 }
 
+int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data)
+{
+       return nfs_create_async(nfs, path, 0, mode, cb, private_data);
+}
+
 
 
 
@@ -2749,7 +3087,7 @@ int nfs_unlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void
        *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) {
+       if (nfs_lookuppath_async(nfs, new_path, 0, cb, private_data, nfs_unlink_continue_internal, new_path, free, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -2890,7 +3228,7 @@ int nfs_mknod_async(struct nfs_context *nfs, const char *path, int mode, int dev
        cb_data->minor = minor(dev);
 
        /* data->path now points to the parent directory,  and beyond the nul terminateor is the new directory to create */
-       if (nfs_lookuppath_async(nfs, cb_data->path, cb, private_data, nfs_mknod_continue_internal, cb_data, free_mknod_cb_data, 0) != 0) {
+       if (nfs_lookuppath_async(nfs, cb_data->path, 0, cb, private_data, nfs_mknod_continue_internal, cb_data, free_mknod_cb_data, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -2952,13 +3290,21 @@ static void nfs_opendir3_cb(struct rpc_context *rpc, int status, void *command_d
 
                        nfsdirent->atime.tv_sec  = attributes->atime.seconds;
                        nfsdirent->atime.tv_usec = attributes->atime.nseconds/1000;
+                       nfsdirent->atime_nsec = attributes->atime.nseconds;
                        nfsdirent->mtime.tv_sec  = attributes->mtime.seconds;
                        nfsdirent->mtime.tv_usec = attributes->mtime.nseconds/1000;
+                       nfsdirent->mtime_nsec = attributes->mtime.nseconds;
                        nfsdirent->ctime.tv_sec  = attributes->ctime.seconds;
                        nfsdirent->ctime.tv_usec = attributes->ctime.nseconds/1000;
+                       nfsdirent->ctime_nsec = attributes->ctime.nseconds;
                        nfsdirent->uid = attributes->uid;
                        nfsdirent->gid = attributes->gid;
                        nfsdirent->nlink = attributes->nlink;
+                       nfsdirent->dev = attributes->fsid;
+                       nfsdirent->rdev = specdata3_to_rdev(&attributes->rdev);
+                       nfsdirent->blksize = NFS_BLKSIZE;
+                       nfsdirent->blocks = (attributes->used + 512 - 1) / 512;
+                       nfsdirent->used = attributes->used;
                }
        }
 
@@ -3193,13 +3539,21 @@ static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_da
 
                        nfsdirent->atime.tv_sec  = entry->name_attributes.post_op_attr_u.attributes.atime.seconds;
                        nfsdirent->atime.tv_usec = entry->name_attributes.post_op_attr_u.attributes.atime.nseconds/1000;
+                       nfsdirent->atime_nsec = entry->name_attributes.post_op_attr_u.attributes.atime.nseconds;
                        nfsdirent->mtime.tv_sec  = entry->name_attributes.post_op_attr_u.attributes.mtime.seconds;
                        nfsdirent->mtime.tv_usec = entry->name_attributes.post_op_attr_u.attributes.mtime.nseconds/1000;
+                       nfsdirent->mtime_nsec = entry->name_attributes.post_op_attr_u.attributes.mtime.nseconds;
                        nfsdirent->ctime.tv_sec  = entry->name_attributes.post_op_attr_u.attributes.ctime.seconds;
                        nfsdirent->ctime.tv_usec = entry->name_attributes.post_op_attr_u.attributes.ctime.nseconds/1000;
+                       nfsdirent->ctime_nsec = entry->name_attributes.post_op_attr_u.attributes.ctime.nseconds;
                        nfsdirent->uid = entry->name_attributes.post_op_attr_u.attributes.uid;
                        nfsdirent->gid = entry->name_attributes.post_op_attr_u.attributes.gid;
                        nfsdirent->nlink = entry->name_attributes.post_op_attr_u.attributes.nlink;
+                       nfsdirent->dev = entry->name_attributes.post_op_attr_u.attributes.fsid;
+                       nfsdirent->rdev = specdata3_to_rdev(&entry->name_attributes.post_op_attr_u.attributes.rdev);
+                       nfsdirent->blksize = NFS_BLKSIZE;
+                       nfsdirent->blocks = (entry->name_attributes.post_op_attr_u.attributes.used + 512 - 1) / 512;
+                       nfsdirent->used = entry->name_attributes.post_op_attr_u.attributes.used;
                }
 
                nfsdirent->next  = nfsdir->entries;
@@ -3294,7 +3648,7 @@ int nfs_opendir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void
        }
        memset(nfsdir, 0, sizeof(struct nfsdir));
 
-       if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_opendir_continue_internal, nfsdir, free, 0) != 0) {
+       if (nfs_lookuppath_async(nfs, path, 0, cb, private_data, nfs_opendir_continue_internal, nfsdir, free, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -3502,7 +3856,7 @@ static int nfs_statvfs_continue_internal(struct nfs_context *nfs, fattr3 *attr _
 
 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) {
+       if (nfs_lookuppath_async(nfs, path, 0, cb, private_data, nfs_statvfs_continue_internal, NULL, NULL, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -3565,7 +3919,7 @@ static int nfs_readlink_continue_internal(struct nfs_context *nfs, fattr3 *attr
 
 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) {
+       if (nfs_lookuppath_async(nfs, path, 1, cb, private_data, nfs_readlink_continue_internal, NULL, NULL, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -3629,9 +3983,9 @@ static int nfs_chmod_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_
 }
 
 
-int nfs_chmod_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data)
+int nfs_chmod_async_internal(struct nfs_context *nfs, const char *path, int no_follow, 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) {
+       if (nfs_lookuppath_async(nfs, path, no_follow, cb, private_data, nfs_chmod_continue_internal, NULL, NULL, mode) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -3639,6 +3993,16 @@ int nfs_chmod_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb
        return 0;
 }
 
+int nfs_chmod_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data)
+{
+       return nfs_chmod_async_internal(nfs, path, 0, mode, cb, private_data);
+}
+
+int nfs_lchmod_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data)
+{
+       return nfs_chmod_async_internal(nfs, path, 1, mode, cb, private_data);
+}
+
 /*
  * Async fchmod()
  */
@@ -3739,7 +4103,7 @@ static int nfs_chown_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_
 }
 
 
-int nfs_chown_async(struct nfs_context *nfs, const char *path, int uid, int gid, nfs_cb cb, void *private_data)
+int nfs_chown_async_internal(struct nfs_context *nfs, const char *path, int no_follow, int uid, int gid, nfs_cb cb, void *private_data)
 {
        struct nfs_chown_data *chown_data;
 
@@ -3752,7 +4116,7 @@ int nfs_chown_async(struct nfs_context *nfs, const char *path, int uid, int gid,
        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) {
+       if (nfs_lookuppath_async(nfs, path, no_follow, cb, private_data, nfs_chown_continue_internal, chown_data, free, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -3760,6 +4124,15 @@ int nfs_chown_async(struct nfs_context *nfs, const char *path, int uid, int gid,
        return 0;
 }
 
+int nfs_chown_async(struct nfs_context *nfs, const char *path, int uid, int gid, nfs_cb cb, void *private_data)
+{
+       return nfs_chown_async_internal(nfs, path, 0, uid, gid, cb, private_data);
+}
+
+int nfs_lchown_async(struct nfs_context *nfs, const char *path, int uid, int gid, nfs_cb cb, void *private_data)
+{
+       return nfs_chown_async_internal(nfs, path, 1, uid, gid, cb, private_data);
+}
 
 /*
  * Async fchown()
@@ -3872,8 +4245,7 @@ static int nfs_utimes_continue_internal(struct nfs_context *nfs, fattr3 *attr _U
        return 0;
 }
 
-
-int nfs_utimes_async(struct nfs_context *nfs, const char *path, struct timeval *times, nfs_cb cb, void *private_data)
+int nfs_utimes_async_internal(struct nfs_context *nfs, const char *path, int no_follow, struct timeval *times, nfs_cb cb, void *private_data)
 {
        struct timeval *new_times = NULL;
 
@@ -3887,7 +4259,7 @@ int nfs_utimes_async(struct nfs_context *nfs, const char *path, struct timeval *
                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) {
+       if (nfs_lookuppath_async(nfs, path, no_follow, cb, private_data, nfs_utimes_continue_internal, new_times, free, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -3895,6 +4267,16 @@ int nfs_utimes_async(struct nfs_context *nfs, const char *path, struct timeval *
        return 0;
 }
 
+int nfs_utimes_async(struct nfs_context *nfs, const char *path, struct timeval *times, nfs_cb cb, void *private_data)
+{
+       return nfs_utimes_async_internal(nfs, path, 0, times, cb, private_data);
+}
+
+int nfs_lutimes_async(struct nfs_context *nfs, const char *path, struct timeval *times, nfs_cb cb, void *private_data)
+{
+       return nfs_utimes_async_internal(nfs, path, 1, times, cb, private_data);
+}
+
 /*
  * Async utime()
  */
@@ -3915,7 +4297,7 @@ int nfs_utime_async(struct nfs_context *nfs, const char *path, struct utimbuf *t
                new_times[1].tv_usec = 0;
        }
 
-       if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_utimes_continue_internal, new_times, free, 0) != 0) {
+       if (nfs_lookuppath_async(nfs, path, 0, cb, private_data, nfs_utimes_continue_internal, new_times, free, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -3932,7 +4314,7 @@ static void nfs_access_cb(struct rpc_context *rpc, int status, void *command_dat
        ACCESS3res *res;
        struct nfs_cb_data *data = private_data;
        struct nfs_context *nfs = data->nfs;
-       unsigned int nfsmode = 0;
+       unsigned int mode = 0;
 
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
@@ -3955,24 +4337,24 @@ static void nfs_access_cb(struct rpc_context *rpc, int status, void *command_dat
                return;
        }
 
-       if (data->continue_int & R_OK) {
-               nfsmode |= ACCESS3_READ;
+       if ((data->continue_int & R_OK) && (res->ACCESS3res_u.resok.access & ACCESS3_READ)) {
+               mode |= R_OK;
        }
-       if (data->continue_int & W_OK) {
-               nfsmode |= ACCESS3_MODIFY;
+       if ((data->continue_int & W_OK) && (res->ACCESS3res_u.resok.access & (ACCESS3_MODIFY | ACCESS3_EXTEND | ACCESS3_DELETE))) {
+               mode |= W_OK;
        }
-       if (data->continue_int & X_OK) {
-               nfsmode |= ACCESS3_EXECUTE;
+       if ((data->continue_int & X_OK) && (res->ACCESS3res_u.resok.access & (ACCESS3_LOOKUP | ACCESS3_EXECUTE))) {
+               mode |= X_OK;
        }
 
-       if (res->ACCESS3res_u.resok.access != nfsmode) {
+       if (data->continue_int != mode) {
                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->continue_int&R_OK?'r':'-',
+                                       data->continue_int&W_OK?'w':'-',
+                                       data->continue_int&X_OK?'x':'-',
+                                       mode&R_OK?'r':'-',
+                                       mode&W_OK?'w':'-',
+                                       mode&X_OK?'x':'-');
                data->cb(-EACCES, nfs, rpc_get_error(nfs->rpc), data->private_data);
                free_nfs_cb_data(data);
                return;
@@ -3991,10 +4373,10 @@ static int nfs_access_continue_internal(struct nfs_context *nfs, fattr3 *attr _U
                nfsmode |= ACCESS3_READ;
        }
        if (data->continue_int & W_OK) {
-               nfsmode |= ACCESS3_MODIFY;
+               nfsmode |= ACCESS3_MODIFY | ACCESS3_EXTEND | ACCESS3_DELETE;
        }
        if (data->continue_int & X_OK) {
-               nfsmode |= ACCESS3_EXECUTE;
+               nfsmode |= ACCESS3_LOOKUP | ACCESS3_EXECUTE;
        }
 
        memset(&args, 0, sizeof(ACCESS3args));
@@ -4012,7 +4394,81 @@ static int nfs_access_continue_internal(struct nfs_context *nfs, fattr3 *attr _U
 
 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) {
+       if (nfs_lookuppath_async(nfs, path, 0, cb, private_data, nfs_access_continue_internal, NULL, NULL, mode & (R_OK | W_OK | X_OK)) != 0) {
+               rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+
+/*
+ * Async access2()
+ */
+static void nfs_access2_cb(struct rpc_context *rpc, 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 result = 0;
+
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
+       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 (res->ACCESS3res_u.resok.access & ACCESS3_READ) {
+               result |= R_OK;
+       }
+       if (res->ACCESS3res_u.resok.access & (ACCESS3_MODIFY | ACCESS3_EXTEND | ACCESS3_DELETE)) {
+               result |= W_OK;
+       }
+       if (res->ACCESS3res_u.resok.access & (ACCESS3_LOOKUP | ACCESS3_EXECUTE)) {
+               result |= X_OK;
+       }
+
+       data->cb(result, nfs, NULL, data->private_data);
+       free_nfs_cb_data(data);
+}
+
+static int nfs_access2_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data)
+{
+       ACCESS3args args;
+
+       memset(&args, 0, sizeof(ACCESS3args));
+       args.object = data->fh;
+       args.access = ACCESS3_READ | ACCESS3_LOOKUP | ACCESS3_MODIFY | ACCESS3_EXTEND | ACCESS3_DELETE | ACCESS3_EXECUTE;
+
+       if (rpc_nfs3_access_async(nfs->rpc, nfs_access2_cb, &args, 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_access2_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
+{
+       if (nfs_lookuppath_async(nfs, path, 0, cb, private_data, nfs_access2_continue_internal, NULL, NULL, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -4142,7 +4598,7 @@ int nfs_symlink_async(struct nfs_context *nfs, const char *oldpath, const char *
                return -1;
        }
 
-       if (nfs_lookuppath_async(nfs, symlink_data->newpathparent, cb, private_data, nfs_symlink_continue_internal, symlink_data, free_nfs_symlink_data, 0) != 0) {
+       if (nfs_lookuppath_async(nfs, symlink_data->newpathparent, 0, cb, private_data, nfs_symlink_continue_internal, symlink_data, free_nfs_symlink_data, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -4253,7 +4709,7 @@ static int nfs_rename_continue_1_internal(struct nfs_context *nfs, fattr3 *attr
        rename_data->olddir = data->fh;
        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) {
+       if (nfs_lookuppath_async(nfs, rename_data->newpath, 0, 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", newpath);
                data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
                free_nfs_cb_data(data);
@@ -4314,7 +4770,7 @@ int nfs_rename_async(struct nfs_context *nfs, const char *oldpath, const char *n
        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) {
+       if (nfs_lookuppath_async(nfs, rename_data->oldpath, 0, cb, private_data, nfs_rename_continue_1_internal, rename_data, free_nfs_rename_data, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
@@ -4416,7 +4872,7 @@ static int nfs_link_continue_1_internal(struct nfs_context *nfs, fattr3 *attr _U
        link_data->oldfh = data->fh;
        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) {
+       if (nfs_lookuppath_async(nfs, link_data->newpath, 0, 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);
@@ -4465,7 +4921,7 @@ int nfs_link_async(struct nfs_context *nfs, const char *oldpath, const char *new
        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) {
+       if (nfs_lookuppath_async(nfs, link_data->oldpath, 0, cb, private_data, nfs_link_continue_1_internal, link_data, free_nfs_link_data, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
                return -1;
        }
index f973f0178b923a0dab3f369fea14a04b076e85ab..1c1853c7cd7989cb84ea3f52774f2e0c6782b4a1 100644 (file)
 #include <sys/socket.h>
 #endif
 
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
 #ifdef HAVE_NETINET_TCP_H
 #include <netinet/tcp.h>
 #endif
@@ -68,6 +72,7 @@
 #include <fcntl.h>
 #include <string.h>
 #include <errno.h>
+#include <time.h>
 #include <sys/types.h>
 #include "libnfs-zdr.h"
 #include "libnfs.h"
@@ -506,7 +511,7 @@ static int rpc_connect_sockaddr_async(struct rpc_context *rpc, struct sockaddr_s
                                        ((struct sockaddr_in6 *)&ss)->sin6_port = port;
                                        ((struct sockaddr_in6 *)&ss)->sin6_family      = AF_INET6;
 #ifdef HAVE_SOCKADDR_LEN
-                                       ((struct sockaddr_in6 *)&ss)->sin6_len = sizeof(struct sockaddr6_in);
+                                       ((struct sockaddr_in6 *)&ss)->sin6_len = sizeof(struct sockaddr_in6);
 #endif
                                        break;
                                }
index c15537299f7a0397b2b5d5a82f4293cc0f175107..de62dcee0c4826c694177b1451e418f1681961ad 100644 (file)
@@ -19,6 +19,6 @@ nfs-stamp : nfs.x
 
 compile_rpc:   
        cat nfs.x | head -29 >libnfs-raw-nfs.h
-       rpcgen -h nfs.x | sed -e "s/#include <rpc\/rpc.h>/#include <nfsc\/libnfs-zdr.h>/" -e "s/xdr/zdr/g" -e "s/XDR/ZDR/g" -e "s/#define _NFS_H_RPCGEN/#define _NFS_H_RPCGEN\n#include <nfsc\/libnfs-zdr.h>/g" -e "s/#define NFS3_COOKIEVERFSIZE 8/#define NFS3_COOKIEVERFSIZE 8\n\n#if defined(ANDROID)\ntypedef long long int quad_t;\ntypedef long long unsigned u_quad_t;\n#endif\n#if defined(WIN32)\ntypedef long long int quad_t;\ntypedef long long unsigned u_quad_t;\n#endif/g" -e "s/ CLIENT / void /g" -e "s/SVCXPRT /void /g" -e "s/bool_t/uint32_t/g" >> libnfs-raw-nfs.h
+       rpcgen -h nfs.x | sed -e "s/#include <rpc\/rpc.h>/#include <nfsc\/libnfs-zdr.h>/" -e "s/xdr/zdr/g" -e "s/XDR/ZDR/g" -e "s/#define _NFS_H_RPCGEN/#define _NFS_H_RPCGEN\n#include <nfsc\/libnfs-zdr.h>/g" -e "s/#define NFS3_COOKIEVERFSIZE 8/#define NFS3_COOKIEVERFSIZE 8\n\n/g" -e "s/ CLIENT / void /g" -e "s/SVCXPRT /void /g" -e "s/bool_t/uint32_t/g" >> libnfs-raw-nfs.h
        cat nfs.x | head -29 >libnfs-raw-nfs.c
        rpcgen -c nfs.x | sed -e "s/#include \".*nfs.h\"/#include \"libnfs-xdr.h\"\n#include \"libnfs-raw-nfs.h\"/" -e "s/xdr/zdr/g" -e "s/XDR/ZDR/g" -e "s/register int32_t \*buf;/register int32_t *buf;\n    buf = NULL;/" -e "s/bool_t/uint32_t/g" >> libnfs-raw-nfs.c
index 925048a32d36f32264a69f33092631b247058be5..6a023463ffb8d27526a7ad15b6cde6014e0d6cda 100644 (file)
@@ -52,7 +52,7 @@ zdr_cookie3 (ZDR *zdrs, cookie3 *objp)
        register int32_t *buf;
        buf = NULL;
 
-        if (!zdr_u_quad_t (zdrs, objp))
+        if (!zdr_uint64_t (zdrs, objp))
                 return FALSE;
        return TRUE;
 }
@@ -142,7 +142,7 @@ zdr_size3 (ZDR *zdrs, size3 *objp)
        register int32_t *buf;
        buf = NULL;
 
-        if (!zdr_u_quad_t (zdrs, objp))
+        if (!zdr_uint64_t (zdrs, objp))
                 return FALSE;
        return TRUE;
 }
@@ -153,7 +153,7 @@ zdr_fileid3 (ZDR *zdrs, fileid3 *objp)
        register int32_t *buf;
        buf = NULL;
 
-        if (!zdr_u_quad_t (zdrs, objp))
+        if (!zdr_uint64_t (zdrs, objp))
                 return FALSE;
        return TRUE;
 }
@@ -206,7 +206,7 @@ zdr_fattr3 (ZDR *zdrs, fattr3 *objp)
                 return FALSE;
         if (!zdr_specdata3 (zdrs, &objp->rdev))
                 return FALSE;
-        if (!zdr_u_quad_t (zdrs, &objp->fsid))
+        if (!zdr_uint64_t (zdrs, &objp->fsid))
                 return FALSE;
         if (!zdr_fileid3 (zdrs, &objp->fileid))
                 return FALSE;
@@ -268,7 +268,7 @@ zdr_offset3 (ZDR *zdrs, offset3 *objp)
        register int32_t *buf;
        buf = NULL;
 
-        if (!zdr_u_quad_t (zdrs, objp))
+        if (!zdr_uint64_t (zdrs, objp))
                 return FALSE;
        return TRUE;
 }
index 835029e7ff4c88f1d8770586514d2339a37c437f..a3b60f566d52bdd8e26b6a1dede106fd8a0d6c77 100644 (file)
@@ -48,18 +48,11 @@ extern "C" {
 #define NFS3_CREATEVERFSIZE 8
 #define NFS3_COOKIEVERFSIZE 8
 
-#if defined(ANDROID)
-typedef long long int quad_t;
-typedef long long unsigned u_quad_t;
-#endif
-#if defined(WIN32)
-typedef long long int quad_t;
-typedef long long unsigned u_quad_t;
-#endif
+
 
 typedef char cookieverf3[NFS3_COOKIEVERFSIZE];
 
-typedef u_quad_t cookie3;
+typedef uint64_t cookie3;
 
 struct nfs_fh3 {
        struct {
@@ -94,9 +87,9 @@ typedef u_int uid3;
 
 typedef u_int gid3;
 
-typedef u_quad_t size3;
+typedef uint64_t size3;
 
-typedef u_quad_t fileid3;
+typedef uint64_t fileid3;
 
 struct specdata3 {
        u_int specdata1;
@@ -119,7 +112,7 @@ struct fattr3 {
        size3 size;
        size3 used;
        specdata3 rdev;
-       u_quad_t fsid;
+       uint64_t fsid;
        fileid3 fileid;
        nfstime3 atime;
        nfstime3 mtime;
@@ -175,7 +168,7 @@ enum stable_how {
 };
 typedef enum stable_how stable_how;
 
-typedef u_quad_t offset3;
+typedef uint64_t offset3;
 
 typedef u_int count3;
 
index a97d40ab7d543e6d8a37ce97b7a5d5665a3dd852..335c67bf395f709f7f197b016cd55aa86c91a048 100644 (file)
--- a/nfs/nfs.c
+++ b/nfs/nfs.c
@@ -86,7 +86,7 @@ int nfsstat3_to_errno(int error)
        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_NOTEMPTY:    return -ENOTEMPTY; break;
        case NFS3ERR_DQUOT:       return -ERANGE; break;
        case NFS3ERR_STALE:       return -EIO; break;
        case NFS3ERR_REMOTE:      return -EIO; break;
@@ -697,7 +697,7 @@ int rpc_nfs_readdirplus_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3
        args.cookie = cookie;
        memcpy(&args.cookieverf, cookieverf, sizeof(cookieverf3)); 
        args.dircount = count;
-       args.maxcount = count;
+       args.maxcount = count * 8;
 
        return rpc_nfs3_readdirplus_async(rpc, cb, &args, private_data);
 }
index ca48c0305886d7cbf6ab25fccc5c59a52eee2fca..ab874a002b0309c2fc23489252373d2f8453d4b9 100644 (file)
--- a/nfs/nfs.x
+++ b/nfs/nfs.x
@@ -37,7 +37,7 @@ const NFS3_COOKIEVERFSIZE = 8;
 
 typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE];
 
-typedef u_quad_t cookie3;
+typedef uint64_t cookie3;
 
 struct nfs_fh3 {
        opaque       data<NFS3_FHSIZE>;
@@ -66,9 +66,9 @@ typedef unsigned int uid3;
 
 typedef unsigned int gid3;
 
-typedef u_quad_t size3;
+typedef uint64_t size3;
 
-typedef u_quad_t fileid3;
+typedef uint64_t fileid3;
 
 struct specdata3 {
        unsigned int specdata1;
@@ -89,7 +89,7 @@ struct fattr3 {
        size3        size;
        size3        used;
        specdata3    rdev;
-       u_quad_t       fsid;
+       uint64_t     fsid;
        fileid3      fileid;
        nfstime3     atime;
        nfstime3     mtime;
@@ -142,7 +142,7 @@ enum stable_how {
        FILE_SYNC = 2
 };
 
-typedef u_quad_t offset3;
+typedef uint64_t offset3;
 
 typedef unsigned int count3;
 
index ccb2c822875ad9040647d4bd8b8bd39dad62a65d..40d70f612cbb33f9828eb706ccd9c6d1267339e7 100644 (file)
@@ -91,9 +91,9 @@ zdr_nlm4_holder (ZDR *zdrs, nlm4_holder *objp)
                 return FALSE;
         if (!zdr_nlm4_oh (zdrs, &objp->oh))
                 return FALSE;
-        if (!zdr_u_quad_t (zdrs, &objp->l_offset))
+        if (!zdr_uint64_t (zdrs, &objp->l_offset))
                 return FALSE;
-        if (!zdr_u_quad_t (zdrs, &objp->l_len))
+        if (!zdr_uint64_t (zdrs, &objp->l_len))
                 return FALSE;
        return TRUE;
 }
@@ -112,9 +112,9 @@ zdr_nlm4_lock (ZDR *zdrs, nlm4_lock *objp)
                 return FALSE;
         if (!zdr_u_int (zdrs, &objp->svid))
                 return FALSE;
-        if (!zdr_u_quad_t (zdrs, &objp->l_offset))
+        if (!zdr_uint64_t (zdrs, &objp->l_offset))
                 return FALSE;
-        if (!zdr_u_quad_t (zdrs, &objp->l_len))
+        if (!zdr_uint64_t (zdrs, &objp->l_len))
                 return FALSE;
        return TRUE;
 }
index ce852598ef3bedf96a021c2f8d6ff6e3d95c2aaf..53635f5cfc34bd65126310a0a6d8fa119ac92149 100644 (file)
@@ -79,8 +79,8 @@ struct nlm4_holder {
        uint32_t exclusive;
        u_int svid;
        nlm4_oh oh;
-       u_quad_t l_offset;
-       u_quad_t l_len;
+       uint64_t l_offset;
+       uint64_t l_len;
 };
 typedef struct nlm4_holder nlm4_holder;
 #define NLM_MAXNAME 256
@@ -90,8 +90,8 @@ struct nlm4_lock {
        struct nlm_fh4 fh;
        nlm4_oh oh;
        u_int svid;
-       u_quad_t l_offset;
-       u_quad_t l_len;
+       uint64_t l_offset;
+       uint64_t l_len;
 };
 typedef struct nlm4_lock nlm4_lock;
 
index 6cd39c2b6c8d29c0364dbbe624c92021e6b2cb80..072aa99321b7f24453e2977a4a388e8059341572 100644 (file)
--- a/nlm/nlm.x
+++ b/nlm/nlm.x
@@ -54,8 +54,8 @@ struct nlm4_holder {
        bool           exclusive;
        unsigned int   svid;
        nlm4_oh        oh;
-       u_quad_t       l_offset;
-       u_quad_t       l_len;
+       uint64_t       l_offset;
+       uint64_t       l_len;
 };
 
 const NLM_MAXNAME = 256;
@@ -64,8 +64,8 @@ struct nlm4_lock {
        struct nlm_fh4 fh;
        nlm4_oh        oh;
        unsigned int   svid;
-       u_quad_t       l_offset;
-       u_quad_t       l_len;
+       uint64_t       l_offset;
+       uint64_t       l_len;
 };
 
 struct nlm4_share {
index 0535743e48d7aabacaa75b83784f08b1747d9101..35e3825df4620c94351b7ef91e9fdfb9397d4dfd 100644 (file)
@@ -108,6 +108,15 @@ Utility programs for LibNFS
 %{_mandir}/man1/nfs-ls.1.gz
 
 %changelog
+* Tue Nov 25 2014 : Version 1.9.6
+ - Add O_TRUNC support for nfs_create
+ - Handle OOM during create
+ - Return more stats fields as part of readdir since we get these for "free"
+   when we use READDIRPLUS
+ - Follow symlinks during path resolution
+ - Add lchown, lstat and lutimes
+ - Replace all [u_]quad types with [u]int types in our RPC layer
+ - Solaris build fixes
 * Sat Jul 19 2014 : Version 1.9.5
  - Remove old ONC-RPC symbols
 * Wed Mar 19 2014 : Version 1.9.3