Add support for chdir and getcwd

Add chdir and getcwd and store cwd in the nfs_context.
Add functions to process the paths specified and normalize them
by performing the transforms :
    // -> /
    /./ -> /
    ^/../ -> error
    ^[^/] -> error
    /string/../ -> /
    /$ -> \0
    /.$ -> \0
    ^/..$ -> error
    /string/..$ -> /

Update the path lookup function to allow specifying relative paths based on
cwd for all functions.
libnfs-4.0.0-vitalif
Ronnie Sahlberg 2014-01-27 20:54:14 -08:00
parent 0a63cc7258
commit f893b680f2
5 changed files with 275 additions and 35 deletions

22
README
View File

@ -1,15 +1,15 @@
LIBNFS is a client library for accessing NFS shares over a network.
LIBNFS offers three different APIs, for different use :
1, RAW : A fully async low level rpc library for nfs protocols
1, RAW : A fully async low level RPC library for NFS protocols
This API is described in include/libnfs-raw.h
it offers a fully async interface to raw XDR encoded blobs.
This api provides very flexible and precice control of the RPC issued.
This API provides very flexible and precise control of the RPC issued.
examples/nfsclient-raw.c provides examples on how to use the raw API
2, NFS ASYNC : A fully asynchronous library for high level vfs functions
This API is described by the *_async() fucntions in include/libnfs.h.
This API is described by the *_async() functions in include/libnfs.h.
This API provides a fully async access to posix vfs like functions such as
stat(), read(), ...
@ -17,7 +17,7 @@ examples/nfsclient-async.c provides examples on how to use this API
3, NFS SYNC : A synchronous library for high level vfs functions
This API is described by the *_sync() fucntions in include/libnfs.h.
This API is described by the *_sync() functions in include/libnfs.h.
This API provides access to posix vfs like functions such as
stat(), read(), ...
@ -31,8 +31,8 @@ The basic syntax of these URLs is :
nfs://<server|ipv4>/path[?arg=val[&arg=val]*]
Arguments supported by libnfs are :
tcp-syncnt=<int> : Number of SYNs to send during the seccion establish
before failing settin up the tcp connection to the
tcp-syncnt=<int> : Number of SYNs to send during the session establish
before failing setting up the tcp connection to the
server.
uid=<int> : UID value to use when talking to the server.
default it 65534 on Windows and getuid() on unixen.
@ -43,7 +43,7 @@ Arguments supported by libnfs are :
ROOT vs NON-ROOT
================
When running as root, libnfs tries to allocate a system port for its connection
to the nfs server. When running as non-root it will use a normal
to the NFS server. When running as non-root it will use a normal
ephemeral port.
Many NFS servers default to a mode where they do not allow non-system
ports from connecting.
@ -75,7 +75,7 @@ PLATFORM support
=================
This is a truly multiplatform library.
Linux: - tested with Ubuntu 10.04 - should work with others aswell
Linux: - tested with Ubuntu 10.04 - should work with others as well
Cygwin: - tested under 64bit win2k8.
MacOSX: - tested with SDK 10.4 (under Snow Leopard) - should also work with later SDKs and 64Bit
iOS: - tested with iOS SDK 4.2 - running on iOS 4.3.x
@ -92,8 +92,8 @@ Release tarballs are available at https://sites.google.com/site/libnfstarballs/l
MAILINGLIST
===========
A libnfs mailinglist is available at http://groups.google.com/group/libnfs
MAILING LIST
============
A libnfs mailing list is available at http://groups.google.com/group/libnfs
Announcements of new versions of libnfs will be posted to this list.

View File

@ -281,7 +281,7 @@ EXTERN int nfs_fstat(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *
*/
EXTERN int nfs_open_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data);
/*
* Sync stat(<filename>)
* Sync open(<filename>)
* Function returns
* 0 : The operation was successfull. *nfsfh is filled in.
* -errno : The command failed.
@ -723,7 +723,7 @@ EXTERN struct nfsdirent *nfs_readdir(struct nfs_context *nfs, struct nfsdir *nfs
/*
* READDIR()
* CLOSEDIR()
*/
/*
* nfs_closedir() never blocks, so no special sync/async versions are available
@ -731,6 +731,49 @@ EXTERN struct nfsdirent *nfs_readdir(struct nfs_context *nfs, struct nfsdir *nfs
EXTERN void nfs_closedir(struct nfs_context *nfs, struct nfsdir *nfsdir);
/*
* CHDIR()
*/
/*
* Async chdir(<path>)
*
* Function returns
* 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
* <0 : An error occured when trying to set up the operation. The callback will not be invoked.
*
* When the callback is invoked, status indicates the result:
* 0 : Success.
* data is NULL;
* -errno : An error occured.
* data is the error string.
*/
EXTERN int nfs_chdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
/*
* Sync chdir(<path>)
* Function returns
* 0 : The operation was successfull.
* -errno : The command failed.
*/
EXTERN int nfs_chdir(struct nfs_context *nfs, const char *path);
/*
* GETCWD()
*/
/*
* nfs_getcwd() never blocks, so no special sync/async versions are available
*/
/*
* Sync getcwd()
* This function returns a pointer to the current working directory.
* This pointer is only stable until the next [f]chdir or when the
* context is destroyed.
*
* Function returns
* 0 : The operation was successfull and *cwd is filled in.
* -errno : The command failed.
*/
EXTERN void nfs_getcwd(struct nfs_context *nfs, const char **cwd);
/*
* STATVFS()

View File

@ -237,8 +237,6 @@ int nfs_stat(struct nfs_context *nfs, const char *path, struct stat *st)
}
/*
* open()
*/
@ -277,6 +275,38 @@ int nfs_open(struct nfs_context *nfs, const char *path, int mode, struct nfsfh *
return cb_data.status;
}
/*
* chdir()
*/
static void chdir_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, "chdir call failed with \"%s\"", (char *)data);
return;
}
}
int nfs_chdir(struct nfs_context *nfs, const char *path)
{
struct sync_cb_data cb_data;
cb_data.is_finished = 0;
if (nfs_chdir_async(nfs, path, chdir_cb, &cb_data) != 0) {
nfs_set_error(nfs, "nfs_chdir_async failed with %s",
nfs_get_error(nfs));
return -1;
}
wait_for_nfs_reply(nfs, &cb_data);
return cb_data.status;
}

View File

@ -7,6 +7,7 @@ nfs_find_local_servers
free_nfs_srvr_list
nfs_access
nfs_access_async
nfs_chdir
nfs_chmod
nfs_chmod_async
nfs_chown
@ -31,6 +32,7 @@ nfs_get_error
nfs_get_fd
nfs_get_readmax
nfs_get_writemax
nfs_getcwd
nfs_init_context
nfs_link
nfs_link_async

View File

@ -93,6 +93,7 @@ struct nfs_context {
struct nfs_fh3 rootfh;
uint64_t readmax;
uint64_t writemax;
char *cwd;
};
void nfs_free_nfsdir(struct nfsdir *nfsdir)
@ -334,17 +335,15 @@ struct nfs_context *nfs_init_context(void)
if (nfs == NULL) {
return NULL;
}
memset(nfs, 0, sizeof(struct nfs_context));
nfs->rpc = rpc_init_context();
if (nfs->rpc == NULL) {
free(nfs);
return NULL;
}
nfs->server = NULL;
nfs->export = NULL;
nfs->rootfh.data.data_len = 0;
nfs->rootfh.data.data_val = NULL;
nfs->cwd = strdup("/");
return nfs;
}
@ -364,6 +363,11 @@ void nfs_destroy_context(struct nfs_context *nfs)
nfs->export = NULL;
}
if (nfs->cwd) {
free(nfs->cwd);
nfs->cwd = NULL;
}
if (nfs->rootfh.data.data_val != NULL) {
free(nfs->rootfh.data.data_val);
nfs->rootfh.data.data_val = NULL;
@ -932,7 +936,7 @@ static void nfs_lookup_path_1_cb(struct rpc_context *rpc, int status, void *comm
static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh)
{
char *path, *str;
char *path, *slash;
LOOKUP3args args;
while (*data->path == '/') {
@ -940,10 +944,16 @@ static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb
}
path = data->path;
str = strchr(path, '/');
if (str != NULL) {
*str = 0;
data->path = str+1;
slash = strchr(path, '/');
if (slash != NULL) {
/* Clear slash so that path is a zero terminated string for
* the current path component. Set it back to '/' again later
* when we are finished referencing this component so that
* data->saved_path will still point to the full
* normalized path.
*/
*slash = 0;
data->path = slash+1;
} else {
while (*data->path != 0) {
data->path++;
@ -960,6 +970,9 @@ static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb
return -1;
}
memcpy(data->fh.data.data_val, fh->data.data_val, data->fh.data.data_len);
if (slash != NULL) {
*slash = '/';
}
data->continue_cb(nfs, data);
return 0;
}
@ -975,6 +988,109 @@ static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb
free_nfs_cb_data(data);
return -1;
}
if (slash != NULL) {
*slash = '/';
}
return 0;
}
static int nfs_normalize_path(struct nfs_context *nfs, char *path)
{
char *str;
int len;
/* // -> / */
while (str = strstr(path, "//")) {
while(*str) {
*str = *(str + 1);
str++;
}
}
/* /./ -> / */
while (str = strstr(path, "/./")) {
while(*(str + 1)) {
*str = *(str + 2);
str++;
}
}
/* ^/../ -> error */
if (!strncmp(path, "/../", 4)) {
rpc_set_error(nfs->rpc,
"Absolute path starts with '/../' "
"during normalization");
return -1;
}
/* ^[^/] -> error */
if (path[0] != '/') {
rpc_set_error(nfs->rpc,
"Absolute path does not start with '/'");
return -1;
}
/* /string/../ -> / */
while (str = strstr(path, "/../")) {
char *tmp;
if (!strncmp(path, "/../", 4)) {
rpc_set_error(nfs->rpc,
"Absolute path starts with '/../' "
"during normalization");
return -1;
}
tmp = str - 1;
while (*tmp != '/') {
tmp--;
}
str += 3;
while((*(tmp++) = *(str++)) != '\0')
;
}
/* /$ -> \0 */
len = strlen(path);
if (len >= 1) {
if (path[len - 1] == '/') {
path[len - 1] = '\0';
len--;
}
}
if (path[0] == '\0') {
rpc_set_error(nfs->rpc,
"Absolute path became '' "
"during normalization");
return -1;
}
/* /.$ -> \0 */
if (len >= 2) {
if (!strcmp(&path[len - 2], "/.")) {
path[len - 2] = '\0';
len -= 2;
}
}
/* ^/..$ -> error */
if (!strcmp(path, "/..")) {
rpc_set_error(nfs->rpc,
"Absolute path is '/..' "
"during normalization");
return -1;
}
/* /string/..$ -> / */
if (len >= 3) {
if (!strcmp(&path[len - 3], "/..")) {
char *tmp = &path[len - 3];
while (*--tmp != '/')
;
*tmp = '\0';
}
}
return 0;
}
@ -982,14 +1098,15 @@ static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, nfs_c
{
struct nfs_cb_data *data;
if (path[0] != 0 && path[0] != '/') {
rpc_set_error(nfs->rpc, "Pathname is not absolute %s", path);
if (path[0] == '\0') {
rpc_set_error(nfs->rpc, "Path is empty");
return -1;
}
data = malloc(sizeof(struct nfs_cb_data));
if (data == NULL) {
rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
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));
@ -1000,12 +1117,29 @@ 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->saved_path = strdup(path);
if (path[0] == '/') {
data->saved_path = strdup(path);
} else {
data->saved_path = malloc(strlen(path) + strlen(nfs->cwd) + 2);
if (data->saved_path == NULL) {
rpc_set_error(nfs->rpc, "out of memory: failed to "
"malloc path string");
free_nfs_cb_data(data);
return -1;
}
sprintf(data->saved_path, "%s/%s", nfs->cwd, path);
}
if (data->saved_path == NULL) {
rpc_set_error(nfs->rpc, "out of memory: failed to copy path string");
free_nfs_cb_data(data);
return -1;
}
if (nfs_normalize_path(nfs, data->saved_path) != 0) {
free_nfs_cb_data(data);
return -1;
}
data->path = data->saved_path;
if (nfs_lookup_path_async_internal(nfs, data, &nfs->rootfh) != 0) {
@ -1108,8 +1242,6 @@ int nfs_stat_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *p
/*
* Async open()
*/
@ -1226,7 +1358,31 @@ int nfs_open_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb c
}
/*
* Async chdir()
*/
static int nfs_chdir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
{
/* steal saved_path */
free(nfs->cwd);
nfs->cwd = data->saved_path;
data->saved_path = NULL;
data->cb(0, nfs, NULL, data->private_data);
free_nfs_cb_data(data);
return 0;
}
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) {
rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
return -1;
}
return 0;
}
/*
@ -2754,15 +2910,24 @@ struct nfsdirent *nfs_readdir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir
}
/*
* closedir()
*/
void nfs_closedir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir)
{
nfs_free_nfsdir(nfsdir);
}
/*
* getcwd()
*/
void nfs_getcwd(struct nfs_context *nfs, const char **cwd)
{
if (cwd) {
*cwd = nfs->cwd;
}
}
/*
@ -3928,7 +4093,7 @@ void nfs_set_error(struct nfs_context *nfs, char *error_string, ...)
free(nfs->rpc->error_string);
}
nfs->rpc->error_string = str;
va_end(ap);
va_end(ap);
}