From f893b680f2af4c1f2bf62902610e734c2640e5f2 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 27 Jan 2014 20:54:14 -0800 Subject: [PATCH] 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. --- README | 22 ++--- include/nfsc/libnfs.h | 47 +++++++++- lib/libnfs-sync.c | 34 ++++++- lib/libnfs-win32.def | 2 + lib/libnfs.c | 205 +++++++++++++++++++++++++++++++++++++----- 5 files changed, 275 insertions(+), 35 deletions(-) diff --git a/README b/README index 6d3eb73..f717c07 100644 --- a/README +++ b/README @@ -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:///path[?arg=val[&arg=val]*] Arguments supported by libnfs are : - tcp-syncnt= : Number of SYNs to send during the seccion establish - before failing settin up the tcp connection to the + tcp-syncnt= : Number of SYNs to send during the session establish + before failing setting up the tcp connection to the server. uid= : 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. diff --git a/include/nfsc/libnfs.h b/include/nfsc/libnfs.h index 7a9accd..e075064 100644 --- a/include/nfsc/libnfs.h +++ b/include/nfsc/libnfs.h @@ -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() + * Sync open() * 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() + * + * 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() + * 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() diff --git a/lib/libnfs-sync.c b/lib/libnfs-sync.c index baadefa..8ab05d7 100644 --- a/lib/libnfs-sync.c +++ b/lib/libnfs-sync.c @@ -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; +} diff --git a/lib/libnfs-win32.def b/lib/libnfs-win32.def index eb5f55a..b1018ec 100644 --- a/lib/libnfs-win32.def +++ b/lib/libnfs-win32.def @@ -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 diff --git a/lib/libnfs.c b/lib/libnfs.c index b19a373..d4ad029 100644 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -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); } -- 2.34.1