summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmmanuel Dreyfus <manu@netbsd.org>2014-10-02 02:14:07 +0200
committerVijay Bellur <vbellur@redhat.com>2014-10-03 02:05:48 -0700
commitbaa6f0ad759af024e4731a0f3889fe857ae2f1bf (patch)
tree0e38bc2c2ee77fa6261e54d236e1d7c596629563
parenta97ad9b69bb17f2351c59512fa9c6cb25d82b4da (diff)
POSIX filesystem compliance: PATH_MAX
POSIX mandates the filesystem to support paths of lengths up to _XOPEN_PATH_MAX (1024). This is the PATH_MAX limit here: http://pubs.opengroup.org/onlinepubs/009604499/basedefs/limits.h.html When using a path of 1023 bytes, the posix xlator attempts to create an absolute path by prefixing the 1023 bytes path by the brick base path. The result is an absolute path of more than _XOPEN_PATH_MAX bytes which may be rejected by the backend filesystem. Linux's ext3fs PATH_MAX seems to defaut to 4096, which means it will work (except if brick base path is longer than 2072 bytes but it is unlikely to happen. NetBSD's FFS PATH_MAX defaults to 1024, which means the bug can happen regardless of brick base path length. If this condition is detected for a brick, the proposed fix is to chdir() the brick glusterfsd daemon to its brick base directory. Then when encountering a path that will exceed _XOPEN_PATH_MAX once prefixed by the brick base path, a relative path is used instead of an absolute one. We do not always use relative path because some operations require an absolute path on the brick base path itself (e.g.: statvfs). At least on NetBSD, this chdir() uncovers a race condition which causes file lookup to fail with ENODATA for a few seconds. The volume quickly reaches a sane state, but regression tests are fast enough to choke on it. The reason is obscure (as often with race conditions), but sleeping one second after the chdir() seems to change scheduling enough that the problem disapear. Note that since the chdir() is done if brick backend filesystem does not support path long enough, it will not occur with Linux ext3fs (except if brick base path is over 2072 bytes long). BUG: 1129939 Change-Id: I7db3567948bc8fa8d99ca5f5ba6647fe425186a9 Signed-off-by: Emmanuel Dreyfus <manu@netbsd.org> Reviewed-on: http://review.gluster.org/8596 Tested-by: Gluster Build System <jenkins@build.gluster.com> Reviewed-by: Harshavardhana <harsha@harshavardhana.net> Tested-by: Harshavardhana <harsha@harshavardhana.net> Reviewed-by: Vijay Bellur <vbellur@redhat.com>
-rw-r--r--xlators/storage/posix/src/posix-handle.h22
-rw-r--r--xlators/storage/posix/src/posix.c34
-rw-r--r--xlators/storage/posix/src/posix.h3
3 files changed, 55 insertions, 4 deletions
diff --git a/xlators/storage/posix/src/posix-handle.h b/xlators/storage/posix/src/posix-handle.h
index a34b936229b..0ce9575251f 100644
--- a/xlators/storage/posix/src/posix-handle.h
+++ b/xlators/storage/posix/src/posix-handle.h
@@ -15,10 +15,16 @@
#include "config.h"
#endif
+#include <limits.h>
#include <sys/types.h>
#include "xlator.h"
#include "gf-dirent.h"
+/* From Open Group Base Specifications Issue 6 */
+#ifndef _XOPEN_PATH_MAX
+#define _XOPEN_PATH_MAX 1024
+#endif
+
#define TRASH_DIR "landfill"
#define UUID0_STR "00000000-0000-0000-0000-000000000000"
@@ -120,10 +126,18 @@
} while (0)
#define MAKE_REAL_PATH(var, this, path) do { \
- var = alloca (strlen (path) + POSIX_BASE_PATH_LEN(this) + 2); \
- strcpy (var, POSIX_BASE_PATH(this)); \
- strcpy (&var[POSIX_BASE_PATH_LEN(this)], path); \
- } while (0)
+ size_t path_len = strlen(path); \
+ size_t var_len = path_len + POSIX_BASE_PATH_LEN(this) + 1; \
+ if (POSIX_PATH_MAX(this) != -1 && \
+ var_len >= POSIX_PATH_MAX(this)) { \
+ var = alloca (path_len + 1); \
+ strcpy (var, (path[0] == '/') ? path + 1 : path); \
+ } else { \
+ var = alloca (var_len); \
+ strcpy (var, POSIX_BASE_PATH(this)); \
+ strcpy (&var[POSIX_BASE_PATH_LEN(this)], path); \
+ } \
+ } while (0)
#define MAKE_HANDLE_PATH(var, this, gfid, base) do { \
int __len; \
diff --git a/xlators/storage/posix/src/posix.c b/xlators/storage/posix/src/posix.c
index 4abea837765..6b12d39df79 100644
--- a/xlators/storage/posix/src/posix.c
+++ b/xlators/storage/posix/src/posix.c
@@ -5663,6 +5663,40 @@ init (xlator_t *this)
_private->base_path = gf_strdup (dir_data->data);
_private->base_path_length = strlen (_private->base_path);
+ /*
+ * _XOPEN_PATH_MAX is the longest file path len we MUST
+ * support according to POSIX standard. When prepended
+ * by the brick base path it may exceed backed filesystem
+ * capacity (which MAY be bigger than _XOPEN_PATH_MAX). If
+ * this is the case, chdir() to the brick base path and
+ * use relative paths when they are too long. See also
+ * MAKE_REAL_PATH in posix-handle.h
+ */
+ _private->path_max = pathconf(_private->base_path, _PC_PATH_MAX);
+ if (_private->path_max != -1 &&
+ _XOPEN_PATH_MAX + _private->base_path_length > _private->path_max) {
+ ret = chdir(_private->base_path);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "chdir() to \"%s\" failed",
+ _private->base_path);
+ goto out;
+ }
+#ifdef __NetBSD__
+ /*
+ * At least on NetBSD, the chdir() above uncovers a
+ * race condition which cause file lookup to fail
+ * with ENODATA for a few seconds. The volume quickly
+ * reaches a sane state, but regression tests are fast
+ * enough to choke on it. The reason is obscure (as
+ * often with race conditions), but sleeping here for
+ * a second seems to workaround the problem.
+ */
+ sleep(1);
+#endif
+ }
+
+
LOCK_INIT (&_private->lock);
ret = dict_get_str (this->options, "hostname", &_private->hostname);
diff --git a/xlators/storage/posix/src/posix.h b/xlators/storage/posix/src/posix.h
index efd52f00b18..5bb5e873c82 100644
--- a/xlators/storage/posix/src/posix.h
+++ b/xlators/storage/posix/src/posix.h
@@ -76,6 +76,7 @@ struct posix_fd {
struct posix_private {
char *base_path;
int32_t base_path_length;
+ int32_t path_max;
gf_lock_t lock;
@@ -189,6 +190,8 @@ typedef struct {
#define POSIX_BASE_PATH_LEN(this) (((struct posix_private *)this->private)->base_path_length)
+#define POSIX_PATH_MAX(this) (((struct posix_private *)this->private)->path_max)
+
/* Helper functions */
int posix_gfid_set (xlator_t *this, const char *path, loc_t *loc,
dict_t *xattr_req);