Net-LibNFS

 view release on metacpan or  search on metacpan

libnfs/lib/nfs_v3.c  view on Meta::CPAN

#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#else
#define PRIu64 "llu"
#endif

#ifdef HAVE_UTIME_H
#include <utime.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_SYS_VFS_H
#include <sys/vfs.h>
#endif

#ifdef HAVE_SYS_STATVFS_H
#include <sys/statvfs.h>
#endif

#if defined(__ANDROID__) && !defined(HAVE_SYS_STATVFS_H)
#define statvfs statfs
#endif

#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif

#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif

#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#endif

#ifdef HAVE_SYS_SYSMACROS_H
#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>
#include "libnfs-zdr.h"
#include "slist.h"
#include "libnfs.h"
#include "libnfs-raw.h"
#include "libnfs-raw-mount.h"
#include "libnfs-private.h"

static dev_t
specdata3_to_rdev(struct specdata3 *rdev)
{
#ifdef makedev
	return makedev(rdev->specdata1, rdev->specdata2);
#else
	return 0;
#endif
}

struct mount_attr_cb {
	int wait_count;
	struct nfs_cb_data *data;
};

struct mount_attr_item_cb {
	struct mount_attr_cb *ma;
	struct nested_mounts *mnt;
};

struct nfs_mcb_data {
       struct nfs_cb_data *data;
       uint64_t offset;
       size_t count;
};

static int
check_nfs3_error(struct nfs_context *nfs, int status,
                 struct nfs_cb_data *data, void *command_data)
{
	if (status == RPC_STATUS_ERROR) {
		data->cb(-EFAULT, nfs, command_data, data->private_data);
		return 1;
	}
	if (status == RPC_STATUS_CANCEL) {
		data->cb(-EINTR, nfs, "Command was cancelled",
			 data->private_data);
		return 1;
	}
	if (status == RPC_STATUS_TIMEOUT) {
		data->cb(-EINTR, nfs, "Command timed out",
			 data->private_data);
		return 1;
	}

	return 0;
}

static int nfs3_lookup_path_async_internal(struct nfs_context *nfs,
                                           struct nfs_attr *attr,
                                           struct nfs_cb_data *data,
                                           struct nfs_fh *fh);

/*
 * 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
nfs3_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;

libnfs/lib/nfs_v3.c  view on Meta::CPAN

			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;
	nfs3_lookup_path_async_internal(nfs, NULL, data, &nfs->nfsi->rootfh);
	return;

nomem:
	data->cb(-ENOMEM, nfs, "Failed to allocate memory for path",
                 data->private_data);
	free_nfs_cb_data(data);
}

static void
fattr3_to_nfs_attr(struct nfs_attr *attr, fattr3 *fa3)
{
        attr->type  = fa3->type;
        attr->mode  = fa3->mode;
        attr->uid   = fa3->uid;
        attr->gid   = fa3->gid;
        attr->nlink = fa3->nlink;
        attr->size  = fa3->size;
        attr->used  = fa3->used;
        attr->fsid  = fa3->fsid;
        attr->rdev.specdata1 = fa3->rdev.specdata1;
        attr->rdev.specdata2 = fa3->rdev.specdata2;
        attr->atime.seconds  = fa3->atime.seconds;
        attr->atime.nseconds = fa3->atime.nseconds;
        attr->mtime.seconds  = fa3->mtime.seconds;
        attr->mtime.nseconds = fa3->mtime.nseconds;
        attr->ctime.seconds  = fa3->ctime.seconds;
        attr->ctime.nseconds = fa3->ctime.nseconds;
}

static void
nfs3_lookup_path_1_cb(struct rpc_context *rpc, int status, void *command_data,
                      void *private_data)
{
	struct nfs_cb_data *data = private_data;
	struct nfs_context *nfs = data->nfs;
	LOOKUP3res *res;
	struct nfs_attr attr;
        struct nfs_fh fh;

	assert(rpc->magic == RPC_CONTEXT_MAGIC);

	if (check_nfs3_error(nfs, status, data, command_data)) {
		free_nfs_cb_data(data);
		return;
	}

	res = command_data;
	if (res->status != NFS3_OK) {
		nfs_set_error(nfs, "NFS: Lookup of %s failed with "
                              "%s(%d)", data->saved_path,
                              nfsstat3_to_str(res->status),
                              nfsstat3_to_errno(res->status));
		data->cb(nfsstat3_to_errno(res->status), nfs,
                         nfs_get_error(nfs), data->private_data);
		free_nfs_cb_data(data);
		return;
	}

        memset(&attr, 0, sizeof(attr));
	if (res->LOOKUP3res_u.resok.obj_attributes.attributes_follow) {
                fattr3_to_nfs_attr(&attr, &res->LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes);
        }
                
	/* This function will always invoke the callback and cleanup
	 * for failures. So no need to check the return value.
	 */
        fh.val = res->LOOKUP3res_u.resok.object.data.data_val;
        fh.len = res->LOOKUP3res_u.resok.object.data.data_len;
	nfs3_lookup_path_async_internal(nfs, &attr, data, &fh);
}

static int
nfs3_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_attr *attr,
                                struct nfs_cb_data *data, struct nfs_fh *fh)
{
	char *path, *slash;
	LOOKUP3args args;

	while (*data->path == '/') {
	      data->path++;
	}

libnfs/lib/nfs_v3.c  view on Meta::CPAN

	LOOKUP3res *res = command_data;
	struct rdpe_lookup_cb_data *rdpe_lookup_cb_data = private_data;
	struct rdpe_cb_data *rdpe_cb_data = rdpe_lookup_cb_data->rdpe_cb_data;
	struct nfs_cb_data *data = rdpe_cb_data->data;
	struct nfsdir *nfsdir = data->continue_data;
	struct nfs_context *nfs = data->nfs;
	struct nfsdirent *nfsdirent = rdpe_lookup_cb_data->nfsdirent;

	assert(rpc->magic == RPC_CONTEXT_MAGIC);

	free(rdpe_lookup_cb_data);

	rdpe_cb_data->getattrcount--;

	if (status == RPC_STATUS_ERROR) {

		nfs_set_error(nfs, "LOOKUP during READDIRPLUS emulation "
			      "failed with RPC_STATUS_ERROR");
		rdpe_cb_data->status = RPC_STATUS_ERROR;
	}
	if (status == RPC_STATUS_CANCEL) {
		nfs_set_error(nfs, "LOOKUP during READDIRPLUS emulation "
			      "failed with RPC_STATUS_CANCEL");
		rdpe_cb_data->status = RPC_STATUS_CANCEL;
	}
	if (status == RPC_STATUS_TIMEOUT) {
		nfs_set_error(nfs, "LOOKUP during READDIRPLUS emulation "
			      "timed out");
		rdpe_cb_data->status = RPC_STATUS_CANCEL;
	}
	if (status == RPC_STATUS_SUCCESS && res->status == NFS3_OK) {
		if (res->LOOKUP3res_u.resok.obj_attributes.attributes_follow) {
			fattr3 *attributes = &res->LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes;

			nfsdirent->type = attributes->type;
			nfsdirent->mode = attributes->mode;
			switch (nfsdirent->type) {
			case NF3REG:  nfsdirent->mode |= S_IFREG; break;
			case NF3DIR:  nfsdirent->mode |= S_IFDIR; break;
			case NF3BLK:  nfsdirent->mode |= S_IFBLK; break;
			case NF3CHR:  nfsdirent->mode |= S_IFCHR; break;
			case NF3LNK:  nfsdirent->mode |= S_IFLNK; break;
			case NF3SOCK: nfsdirent->mode |= S_IFSOCK; break;
			case NF3FIFO: nfsdirent->mode |= S_IFIFO; break;
			};
			nfsdirent->size = attributes->size;

			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;
		}
	}

	if (rdpe_cb_data->getattrcount == 0) {
		if (rdpe_cb_data->status != RPC_STATUS_SUCCESS) {
			nfs_set_error(nfs, "READDIRPLUS emulation "
			      "failed: %s", rpc_get_error(rpc));
			data->cb(-ENOMEM, nfs, nfs_get_error(nfs),
				data->private_data);
			nfs_free_nfsdir(nfsdir);
		} else {
			data->cb(0, nfs, nfsdir, data->private_data);
		}
		free(rdpe_cb_data);
		data->continue_data = NULL;
		free_nfs_cb_data(data);
	}
}

static int
lookup_missing_attributes(struct nfs_context *nfs,
                          struct nfsdir *nfsdir,
                          struct nfs_cb_data *data)
{
	struct rdpe_cb_data *rdpe_cb_data = NULL;
	struct nfsdirent *nfsdirent;

	for (nfsdirent = nfsdir->entries;
	     nfsdirent;
	     nfsdirent = nfsdirent->next) {
		struct rdpe_lookup_cb_data *rdpe_lookup_cb_data;
		LOOKUP3args args;

		/* If type == 0 we assume it is a case of the server not
		 * giving us the attributes for this entry during READIR[PLUS]
		 * so we fallback to LOOKUP3
		 */
		if (nfsdirent->type != 0) {
			continue;
		}

		if (rdpe_cb_data == NULL) {
			rdpe_cb_data = malloc(sizeof(struct rdpe_cb_data));
			rdpe_cb_data->getattrcount = 0;
			rdpe_cb_data->status = RPC_STATUS_SUCCESS;
			rdpe_cb_data->data = data;
		}
		rdpe_lookup_cb_data = malloc(sizeof(struct rdpe_lookup_cb_data));
		rdpe_lookup_cb_data->rdpe_cb_data = rdpe_cb_data;
		rdpe_lookup_cb_data->nfsdirent = nfsdirent;

		memset(&args, 0, sizeof(LOOKUP3args));
		args.what.dir.data.data_len = data->fh.len;
		args.what.dir.data.data_val = data->fh.val;
		args.what.name = nfsdirent->name;

		if (rpc_nfs3_lookup_async(nfs->rpc, nfs3_opendir_3_cb, &args,

libnfs/lib/nfs_v3.c  view on Meta::CPAN

	}

	entry =res->READDIRPLUS3res_u.resok.reply.entries;
	while (entry != NULL) {
		struct nfsdirent *nfsdirent;
		struct nfs_attr attr;
                int has_attr = 0;

                memset(&attr, 0, sizeof(attr));

		nfsdirent = malloc(sizeof(struct nfsdirent));
		if (nfsdirent == NULL) {
			data->cb(-ENOMEM, nfs, "Failed to allocate dirent",
                                 data->private_data);
			nfs_free_nfsdir(nfsdir);
			data->continue_data = NULL;
			free_nfs_cb_data(data);
			return;
		}
		memset(nfsdirent, 0, sizeof(struct nfsdirent));
		nfsdirent->name = strdup(entry->name);
		if (nfsdirent->name == NULL) {
			data->cb(-ENOMEM, nfs, "Failed to allocate "
                                 "dirent->name", data->private_data);
			free(nfsdirent);
			nfs_free_nfsdir(nfsdir);
			data->continue_data = NULL;
			free_nfs_cb_data(data);
			return;
		}
		nfsdirent->inode = entry->fileid;

		if (entry->name_attributes.attributes_follow) {
			fattr3_to_nfs_attr(&attr, &entry->name_attributes.post_op_attr_u.attributes);
                        has_attr = 1;
                }

		if (!has_attr) {
			struct nested_mounts *mnt;
			int splen = strlen(data->saved_path);

			/* A single '/' is a special case, treat it as
			 * zero-length below. */
			if (splen == 1)
				splen = 0;

			/* No name attributes. Is it a nested mount then?*/
			for(mnt = nfs->nfsi->nested_mounts; mnt; mnt = mnt->next) {
				if (strncmp(data->saved_path, mnt->path, splen))
					continue;
				if (mnt->path[splen] != '/')
					continue;
				if (strcmp(mnt->path + splen + 1, entry->name))
					continue;
				attr = mnt->attr;
                                has_attr = 1;
				break;
			}
		}
		if (has_attr) {
                        struct specdata3 sd3 = { attr.rdev.specdata1,
                                                 attr.rdev.specdata2 };

			nfsdirent->type = attr.type;
			nfsdirent->mode = attr.mode;
			switch (nfsdirent->type) {
			case NF3REG:  nfsdirent->mode |= S_IFREG; break;
			case NF3DIR:  nfsdirent->mode |= S_IFDIR; break;
			case NF3BLK:  nfsdirent->mode |= S_IFBLK; break;
			case NF3CHR:  nfsdirent->mode |= S_IFCHR; break;
			case NF3LNK:  nfsdirent->mode |= S_IFLNK; break;
			case NF3SOCK: nfsdirent->mode |= S_IFSOCK; break;
			case NF3FIFO: nfsdirent->mode |= S_IFIFO; break;
			};
			nfsdirent->size = attr.size;

			nfsdirent->atime.tv_sec  = attr.atime.seconds;
			nfsdirent->atime.tv_usec = attr.atime.nseconds/1000;
			nfsdirent->atime_nsec = attr.atime.nseconds;
			nfsdirent->mtime.tv_sec  = attr.mtime.seconds;
			nfsdirent->mtime.tv_usec = attr.mtime.nseconds/1000;
			nfsdirent->mtime_nsec = attr.mtime.nseconds;
			nfsdirent->ctime.tv_sec  = attr.ctime.seconds;
			nfsdirent->ctime.tv_usec = attr.ctime.nseconds/1000;
			nfsdirent->ctime_nsec = attr.ctime.nseconds;
			nfsdirent->uid = attr.uid;
			nfsdirent->gid = attr.gid;
			nfsdirent->nlink = attr.nlink;
			nfsdirent->dev = attr.fsid;
			nfsdirent->rdev = specdata3_to_rdev(&sd3);
			nfsdirent->blksize = NFS_BLKSIZE;
			nfsdirent->blocks = (attr.used + 512 - 1) / 512;
			nfsdirent->used = attr.used;
		}

		nfsdirent->next  = nfsdir->entries;
		nfsdir->entries  = nfsdirent;

		cookie = entry->cookie;
		entry  = entry->nextentry;
	}

	if (res->READDIRPLUS3res_u.resok.reply.eof == 0) {
		READDIRPLUS3args args;

		args.dir.data.data_len = data->fh.len;
		args.dir.data.data_val = data->fh.val;
		args.cookie = cookie;
		memcpy(&args.cookieverf,
                       res->READDIRPLUS3res_u.resok.cookieverf,
                       sizeof(cookieverf3));
		args.dircount = nfs->nfsi->readdir_dircount;
		args.maxcount = nfs->nfsi->readdir_maxcount;

	     	if (rpc_nfs3_readdirplus_async(nfs->rpc, nfs3_opendir_cb,
                                               &args, data) != 0) {
			nfs_set_error(nfs, "RPC error: Failed to send "
                                      "READDIRPLUS call for %s", data->path);
			data->cb(-ENOMEM, nfs, nfs_get_error(nfs),
                                 data->private_data);
			nfs_free_nfsdir(nfsdir);
			data->continue_data = NULL;
			free_nfs_cb_data(data);
			return;
		}
		return;
	}

	if (res->READDIRPLUS3res_u.resok.dir_attributes.attributes_follow) {
		fattr3_to_nfs_attr(&nfsdir->attr, &res->READDIRPLUS3res_u.resok.dir_attributes.post_op_attr_u.attributes);
        }

	/* steal the dirhandle */
	nfsdir->current = nfsdir->entries;

	if (lookup_missing_attributes(nfs, nfsdir, data) == 0) {
		data->cb(0, nfs, nfsdir, data->private_data);
		/* We can not free data->continue_data here */
		data->continue_data = NULL;
		free_nfs_cb_data(data);
		return;
	}
}

static int
nfs3_opendir_continue_internal(struct nfs_context *nfs,
                               struct nfs_attr *attr,
                               struct nfs_cb_data *data)
{
	READDIRPLUS3args args;

libnfs/lib/nfs_v3.c  view on Meta::CPAN

	free(data->path);
	free(data);
}

static void
nfs3_mknod_cb(struct rpc_context *rpc, int status, void *command_data,
              void *private_data)
{
	MKNOD3res *res;
	struct nfs_cb_data *data = private_data;
	struct nfs_context *nfs = data->nfs;
	char *str = data->continue_data;

	assert(rpc->magic == RPC_CONTEXT_MAGIC);

	str = &str[strlen(str) + 1];

	if (check_nfs3_error(nfs, status, data, command_data)) {
		free_nfs_cb_data(data);
		return;
	}

	res = command_data;
	if (res->status != NFS3_OK) {
		nfs_set_error(nfs, "NFS: MKNOD of %s/%s failed with "
                              "%s(%d)", data->saved_path, str,
                              nfsstat3_to_str(res->status),
                              nfsstat3_to_errno(res->status));
		data->cb(nfsstat3_to_errno(res->status), nfs,
                         nfs_get_error(nfs), data->private_data);
		free_nfs_cb_data(data);
		return;
	}

	nfs_dircache_drop(nfs, &data->fh);
	data->cb(0, nfs, NULL, data->private_data);
	free_nfs_cb_data(data);
}

static int
nfs3_mknod_continue_internal(struct nfs_context *nfs,
                             struct nfs_attr *attr _U_,
                             struct nfs_cb_data *data)
{
	struct mknod_cb_data *cb_data = data->continue_data;
	char *str = cb_data->path;
	MKNOD3args args;

        memset(&args, 0, sizeof(args));

	str = &str[strlen(str) + 1];

	args.where.dir.data.data_len = data->fh.len;
	args.where.dir.data.data_val = data->fh.val;
	args.where.name = str;
	switch (cb_data->mode & S_IFMT) {
	case S_IFCHR:
		args.what.type = NF3CHR;
		args.what.mknoddata3_u.chr_device.dev_attributes.mode.set_it = 1;
		args.what.mknoddata3_u.chr_device.dev_attributes.mode.set_mode3_u.mode = cb_data->mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
		args.what.mknoddata3_u.chr_device.spec.specdata1 = cb_data->major;
		args.what.mknoddata3_u.chr_device.spec.specdata2 = cb_data->minor;
		break;
	case S_IFBLK:
		args.what.type = NF3BLK;
		args.what.mknoddata3_u.blk_device.dev_attributes.mode.set_it = 1;
		args.what.mknoddata3_u.blk_device.dev_attributes.mode.set_mode3_u.mode = cb_data->mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
		args.what.mknoddata3_u.blk_device.spec.specdata1 = cb_data->major;
		args.what.mknoddata3_u.blk_device.spec.specdata2 = cb_data->minor;
                break;
	case S_IFSOCK:
		args.what.type = NF3SOCK;
		args.what.mknoddata3_u.sock_attributes.mode.set_it = 1;
		args.what.mknoddata3_u.sock_attributes.mode.set_mode3_u.mode = cb_data->mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
		break;
	case S_IFIFO:
		args.what.type = NF3FIFO;
		args.what.mknoddata3_u.pipe_attributes.mode.set_it = 1;
		args.what.mknoddata3_u.pipe_attributes.mode.set_mode3_u.mode = cb_data->mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
		break;
	default:
		nfs_set_error(nfs, "Invalid file type for "
                              "NFS3/MKNOD call");
		data->cb(-EINVAL, nfs, nfs_get_error(nfs),
                         data->private_data);
		free_nfs_cb_data(data);
		return -1;
	}

	if (rpc_nfs3_mknod_async(nfs->rpc, nfs3_mknod_cb, &args, data) != 0) {
		data->cb(-ENOMEM, nfs, nfs_get_error(nfs),
                         data->private_data);
		free_nfs_cb_data(data);
		return -1;
	}
	return 0;
}

int
nfs3_mknod_async(struct nfs_context *nfs, const char *path, int mode, int dev,
                 nfs_cb cb, void *private_data)
{
	char *ptr;
	struct mknod_cb_data *cb_data;

	cb_data = malloc(sizeof(struct mknod_cb_data));
	if (cb_data == NULL) {
		nfs_set_error(nfs, "Out of memory, failed to allocate "
                              "mode buffer for cb data");
		return -1;
	}

        ptr = strrchr(path, '/');
        if (ptr) {
                cb_data->path = strdup(path);
                if (cb_data->path == NULL) {
                        nfs_set_error(nfs, "Out of memory, failed to allocate "
                                      "buffer for mknod path");
                        return -1;
                }
                ptr = strrchr(cb_data->path, '/');
                *ptr = 0;
        } else {
                cb_data->path = malloc(strlen(path) + 2);
                if (cb_data->path == NULL) {
                        nfs_set_error(nfs, "Out of memory, failed to allocate "
                                      "buffer for mknod path");
                        return -1;
                }

libnfs/lib/nfs_v3.c  view on Meta::CPAN

nfs3_stat_1_cb(struct rpc_context *rpc, int status, void *command_data,
               void *private_data)
{
	GETATTR3res *res;
	struct nfs_cb_data *data = private_data;
	struct nfs_context *nfs = data->nfs;
#ifdef WIN32
  struct __stat64 st;
#else
	struct stat st;
#endif

	assert(rpc->magic == RPC_CONTEXT_MAGIC);

	if (check_nfs3_error(nfs, status, data, command_data)) {
		free_nfs_cb_data(data);
		return;
	}

	res = command_data;
	if (res->status != NFS3_OK) {
		nfs_set_error(nfs, "NFS: GETATTR of %s failed with "
                              "%s(%d)", data->saved_path,
                              nfsstat3_to_str(res->status),
                              nfsstat3_to_errno(res->status));
		data->cb(nfsstat3_to_errno(res->status), nfs,
                         nfs_get_error(nfs), data->private_data);
		free_nfs_cb_data(data);
		return;
	}

	st.st_dev     = (dev_t)res->GETATTR3res_u.resok.obj_attributes.fsid;
        st.st_ino     = (ino_t)res->GETATTR3res_u.resok.obj_attributes.fileid;
        st.st_mode    = res->GETATTR3res_u.resok.obj_attributes.mode;
	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    = 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.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);
}

int
nfs3_fstat_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) {
		nfs_set_error(nfs, "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.data.data_len = nfsfh->fh.len;
	args.object.data.data_val = nfsfh->fh.val;

	if (rpc_nfs3_getattr_async(nfs->rpc, nfs3_stat_1_cb, &args,
                                   data) != 0) {
		data->cb(-ENOMEM, nfs, nfs_get_error(nfs),
                         data->private_data);
		free_nfs_cb_data(data);
		return -1;
	}
	return 0;
}

static void
nfs3_stat64_1_cb(struct rpc_context *rpc, int status, void *command_data,
                 void *private_data)
{
	GETATTR3res *res;
	struct nfs_cb_data *data = private_data;
	struct nfs_context *nfs = data->nfs;
	struct nfs_stat_64 st;

	assert(rpc->magic == RPC_CONTEXT_MAGIC);

	if (check_nfs3_error(nfs, status, data, command_data)) {
		free_nfs_cb_data(data);
		return;
	}

	res = command_data;
	if (res->status != NFS3_OK) {
		nfs_set_error(nfs, "NFS: GETATTR of %s failed with "
                              "%s(%d)", data->saved_path,
                              nfsstat3_to_str(res->status),
                              nfsstat3_to_errno(res->status));
		data->cb(nfsstat3_to_errno(res->status), nfs,
                         nfs_get_error(nfs), data->private_data);
		free_nfs_cb_data(data);
		return;
	}

	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;
	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    = 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);
}

static int
nfs3_stat64_continue_internal(struct nfs_context *nfs,
                              struct nfs_attr *attr _U_,
                              struct nfs_cb_data *data)
{
	struct GETATTR3args args;

	memset(&args, 0, sizeof(GETATTR3args));
	args.object.data.data_len = data->fh.len;
	args.object.data.data_val = data->fh.val;

	if (rpc_nfs3_getattr_async(nfs->rpc, nfs3_stat64_1_cb,
                                   &args, data) != 0) {
		data->cb(-ENOMEM, nfs, nfs_get_error(nfs),
                         data->private_data);
		free_nfs_cb_data(data);
		return -1;
	}
	return 0;
}

int
nfs3_stat64_async(struct nfs_context *nfs, const char *path,
                  int no_follow, nfs_cb cb, void *private_data)
{
	if (nfs3_lookuppath_async(nfs, path, no_follow, cb, private_data,
                                  nfs3_stat64_continue_internal,
                                  NULL, NULL, 0) != 0) {
		return -1;
	}

	return 0;
}

int
nfs3_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) {
		nfs_set_error(nfs, "out of memory: failed to allocate "
                              "nfs_cb_data structure");

libnfs/lib/nfs_v3.c  view on Meta::CPAN

	}

	data->nfsfh->ra.fh_offset = data->max_offset;

	nfs_pagecache_put(&data->nfsfh->pagecache, data->offset, data->buffer,
                          (size_t)(data->max_offset - data->offset));

	if (data->max_offset > data->org_offset + data->org_count) {
		data->max_offset = data->org_offset + data->org_count;
	}
	if (data->update_pos) {
		data->nfsfh->offset = data->max_offset;
	}

	cb_err = (int)(data->max_offset - data->org_offset);
	cb_data = data->buffer + (data->org_offset - data->offset);
	data->cb(cb_err, nfs, cb_data, data->private_data);
	free_nfs_cb_data(data);
	return;
}

int
nfs3_pread_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh,
                          uint64_t offset, size_t count, nfs_cb cb,
                          void *private_data, int update_pos)
{
	struct nfs_cb_data *data;

	data = malloc(sizeof(struct nfs_cb_data));
	if (data == NULL) {
		nfs_set_error(nfs, "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;
	data->nfsfh        = nfsfh;
	data->org_offset   = offset;
	data->org_count    = count;
	data->update_pos   = update_pos;

	assert(data->num_calls == 0);

	if (nfsfh->pagecache.num_entries) {
		/* align start offset to blocksize */
		count += offset & (NFS_BLKSIZE - 1);
		offset &= ~(NFS_BLKSIZE - 1);

		/* align end offset to blocksize */
		count += NFS_BLKSIZE - 1 ;
		count &= ~(NFS_BLKSIZE - 1);
	}

	data->offset = offset;
	data->count = (count3)count;

	if (nfsfh->pagecache.num_entries) {
		while (count > 0) {
			char *cdata = nfs_pagecache_get(&nfsfh->pagecache,
                                                        offset);
			if (!cdata) {
				break;
			}
			/* we copy data from the pagecache so we need a
                         * reassembly buffer */
			if (data->buffer == NULL) {
				data->buffer = malloc(data->count);
				if (data->buffer == NULL) {
					free_nfs_cb_data(data);
					return -ENOMEM;
				}
			}
			memcpy(data->buffer + offset - data->offset, cdata,
                               NFS_BLKSIZE);
			offset += NFS_BLKSIZE;
			count -= NFS_BLKSIZE;
		}
		if (!count) {
			data->nfsfh->ra.fh_offset = data->offset + data->count;
			if (update_pos) {
				data->nfsfh->offset = data->org_offset + data->org_count;
			}
			data->cb(data->org_count, nfs, data->buffer + (data->org_offset - data->offset), data->private_data);
			free_nfs_cb_data(data);
			return 0;
		}
	}

	if (nfs->rpc->readahead) {
		nfsfh->ra.cur_ra = MAX(NFS_BLKSIZE, nfsfh->ra.cur_ra);
		if (offset >= nfsfh->ra.fh_offset &&
			offset <= nfsfh->ra.fh_offset + nfsfh->ra.cur_ra + NFS_BLKSIZE) {
			if (nfs->rpc->readahead > nfsfh->ra.cur_ra) {
				nfsfh->ra.cur_ra <<= 1;
			}
		} else {
			nfsfh->ra.cur_ra = 0;
		}
		count += nfsfh->ra.cur_ra;
		data->count += nfsfh->ra.cur_ra;
	}

	if ((data->count > nfs_get_readmax(nfs) || data->count > data->org_count) &&
	    (data->buffer == NULL || nfsfh->ra.cur_ra > 0)) {
		/* we do readahead, a big read or aligned out the request so we
		 * need a (bigger) reassembly buffer */
		data->buffer = realloc(data->buffer, data->count + nfsfh->ra.cur_ra);
		if (data->buffer == NULL) {
			free_nfs_cb_data(data);
			return -ENOMEM;
		}
	}

	data->max_offset = data->offset;

	/* chop requests into chunks of at most READMAX bytes if necessary.
	 * we send all reads in parallel so that performance is still good.
	 */
	do {
		size_t readcount = count;
		struct nfs_mcb_data *mdata;
		READ3args args;

		if (readcount > nfs_get_readmax(nfs)) {
		  readcount = (size_t)nfs_get_readmax(nfs);
		}

		mdata = malloc(sizeof(struct nfs_mcb_data));
		if (mdata == NULL) {
			nfs_set_error(nfs, "out of memory: failed to allocate nfs_mcb_data structure");
			if (data->num_calls == 0) {
				free_nfs_cb_data(data);
				return -1;



( run in 1.591 second using v1.01-cache-2.11-cpan-39bf76dae61 )