/* Copyright (c) 2011 Gluster, Inc. This file is part of GlusterFS. GlusterFS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. GlusterFS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _CONFIG_H #define _CONFIG_H #include "config.h" #endif #include #include #include #include #include #ifdef GF_LINUX_HOST_OS #include #endif #include "posix-handle.h" #include "posix.h" #include "xlator.h" #define HANDLE_PFX ".glusterfs" #define UUID0_STR "00000000-0000-0000-0000-000000000000" #define SLEN(str) (sizeof(str) - 1) int posix_handle_relpath (xlator_t *this, uuid_t gfid, const char *basename, char *buf, size_t buflen) { char *uuid_str = NULL; int len = 0; len = SLEN("../") + SLEN("../") + SLEN("00/") + SLEN("00/") + SLEN(UUID0_STR) + 1 /* '\0' */ ; if (basename) { len += (strlen (basename) + 1); } if (buflen < len || !buf) return len; uuid_str = uuid_utoa (gfid); if (basename) { len = snprintf (buf, buflen, "../../%02x/%02x/%s/%s", gfid[0], gfid[1], uuid_str, basename); } else { len = snprintf (buf, buflen, "../../%02x/%02x/%s", gfid[0], gfid[1], uuid_str); } return len; } /* TODO: explain how this pump fixes ELOOP */ int posix_handle_pump (xlator_t *this, char *buf, int len, int maxlen, char *base_str, int base_len, int pfx_len) { char linkname[512] = {0,}; /* "../..//" */ int ret = 0; int blen = 0; int link_len = 0; /* is a directory's symlink-handle */ ret = readlink (base_str, linkname, 512); if (ret == -1) { gf_log (this->name, GF_LOG_ERROR, "internal readlink failed on %s (%s)", base_str, strerror (errno)); goto err; } if (ret < 512) linkname[ret] = 0; link_len = ret; if ((ret == 8) && memcmp (linkname, "../../..", 8) == 0) { if (strcmp (base_str, buf) == 0) { strncpy (buf + pfx_len, "..", 3); } goto out; } if (ret < 50 || ret >= 512) { gf_log (this->name, GF_LOG_ERROR, "malformed internal link %s for %s", linkname, base_str); goto err; } if (memcmp (linkname, "../../", 6) != 0) { gf_log (this->name, GF_LOG_ERROR, "malformed internal link %s for %s", linkname, base_str); goto err; } if ((linkname[2] != '/') || (linkname[5] != '/') || (linkname[8] != '/') || (linkname[11] != '/') || (linkname[48] != '/')) { gf_log (this->name, GF_LOG_ERROR, "malformed internal link %s for %s", linkname, base_str); goto err; } if ((linkname[20] != '-') || (linkname[25] != '-') || (linkname[30] != '-') || (linkname[35] != '-')) { gf_log (this->name, GF_LOG_ERROR, "malformed internal link %s for %s", linkname, base_str); goto err; } blen = link_len - 48; memmove (buf + base_len + blen, buf + base_len, (strlen (buf) - base_len) + 1); strncpy (base_str + pfx_len, linkname + 6, 42); if (len + blen < maxlen) strncpy (buf + pfx_len, linkname + 6, link_len - 6); out: return len + blen; err: return -1; } /* posix_handle_path differs from posix_handle_gfid_path in the way that the path filled in @buf by posix_handle_path will return type IA_IFDIR when an lstat() is performed on it, whereas posix_handle_gfid_path returns path to the handle symlink (typically used for the purpose of unlinking it). posix_handle_path also guarantees immunity to ELOOP on the path returned by it */ int posix_handle_path (xlator_t *this, uuid_t gfid, const char *basename, char *ubuf, size_t size) { struct posix_private *priv = NULL; char *uuid_str = NULL; int len = 0; int ret = -1; struct stat stat; char *base_str = NULL; int base_len = 0; int pfx_len; int maxlen; char *buf; priv = this->private; uuid_str = uuid_utoa (gfid); if (ubuf) { buf = ubuf; maxlen = size; } else { maxlen = PATH_MAX; buf = alloca (maxlen); } base_len = (priv->base_path_length + SLEN(HANDLE_PFX) + 45); base_str = alloca (base_len + 1); base_len = snprintf (base_str, base_len + 1, "%s/%s/%02x/%02x/%s", priv->base_path, HANDLE_PFX, gfid[0], gfid[1], uuid_str); pfx_len = priv->base_path_length + 1 + SLEN(HANDLE_PFX) + 1; if (basename) { len = snprintf (buf, maxlen, "%s/%s", base_str, basename); } else { len = snprintf (buf, maxlen, "%s", base_str); } ret = lstat (base_str, &stat); if (!(ret == 0 && S_ISLNK(stat.st_mode) && stat.st_nlink == 1)) goto out; do { errno = 0; ret = posix_handle_pump (this, buf, len, maxlen, base_str, base_len, pfx_len); if (ret == -1) break; len = ret; ret = lstat (buf, &stat); } while ((ret == -1) && errno == ELOOP); out: return len + 1; } int posix_handle_gfid_path (xlator_t *this, uuid_t gfid, const char *basename, char *buf, size_t buflen) { struct posix_private *priv = NULL; char *uuid_str = NULL; int len = 0; priv = this->private; len = priv->base_path_length /* option directory "/export" */ + SLEN("/") + SLEN(HANDLE_PFX) + SLEN("/") + SLEN("00/") + SLEN("00/") + SLEN(UUID0_STR) + 1 /* '\0' */ ; if (basename) { len += (strlen (basename) + 1); } else { len += 256; /* worst-case for directory's symlink-handle expansion */ } if ((buflen < len) || !buf) return len; uuid_str = uuid_utoa (gfid); if (__is_root_gfid (gfid)) { if (basename) { len = snprintf (buf, buflen, "%s/%s", priv->base_path, basename); } else { strncpy (buf, priv->base_path, buflen); } goto out; } if (basename) { len = snprintf (buf, buflen, "%s/%s/%02x/%02x/%s/%s", priv->base_path, HANDLE_PFX, gfid[0], gfid[1], uuid_str, basename); } else { len = snprintf (buf, buflen, "%s/%s/%02x/%02x/%s", priv->base_path, HANDLE_PFX, gfid[0], gfid[1], uuid_str); } out: return len; } int posix_handle_init (xlator_t *this) { struct posix_private *priv = NULL; char *handle_pfx = NULL; int ret = 0; int len = 0; struct stat stbuf; struct stat rootbuf; struct stat exportbuf; char *rootstr = NULL; uuid_t gfid = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; priv = this->private; ret = stat (priv->base_path, &exportbuf); if (ret || !S_ISDIR (exportbuf.st_mode)) { gf_log (this->name, GF_LOG_ERROR, "Not a directory: %s", priv->base_path); return -1; } handle_pfx = alloca (priv->base_path_length + 1 + strlen (HANDLE_PFX) + 1); sprintf (handle_pfx, "%s/%s", priv->base_path, HANDLE_PFX); ret = stat (handle_pfx, &stbuf); switch (ret) { case -1: if (errno == ENOENT) { ret = mkdir (handle_pfx, 0600); if (ret != 0) { gf_log (this->name, GF_LOG_ERROR, "Creating directory %s failed: %s", handle_pfx, strerror (errno)); return -1; } } else { gf_log (this->name, GF_LOG_ERROR, "Checking for %s failed: %s", handle_pfx, strerror (errno)); return -1; } break; case 0: if (!S_ISDIR (stbuf.st_mode)) { gf_log (this->name, GF_LOG_ERROR, "Not a directory: %s", handle_pfx); return -1; } break; default: break; } stat (handle_pfx, &priv->handledir); len = posix_handle_path (this, gfid, NULL, NULL, 0); rootstr = alloca (len); posix_handle_path (this, gfid, NULL, rootstr, len); ret = stat (rootstr, &rootbuf); switch (ret) { case -1: if (errno != ENOENT) { gf_log (this->name, GF_LOG_ERROR, "%s: %s", priv->base_path, strerror (errno)); return -1; } ret = posix_handle_mkdir_hashes (this, rootstr); if (ret) { gf_log (this->name, GF_LOG_WARNING, "mkdir %s failed (%s)", rootstr, strerror (errno)); return -1; } ret = symlink ("../../..", rootstr); if (ret) { gf_log (this->name, GF_LOG_ERROR, "symlink %s creation failed (%s)", rootstr, strerror (errno)); return -1; } break; case 0: if ((exportbuf.st_ino == rootbuf.st_ino) && (exportbuf.st_dev == rootbuf.st_dev)) return 0; gf_log (this->name, GF_LOG_ERROR, "Different dirs %s (%lld/%lld) != %s (%lld/%lld)", priv->base_path, (long long) exportbuf.st_ino, (long long) exportbuf.st_dev, rootstr, (long long) rootbuf.st_ino, (long long) rootbuf.st_dev); return -1; break; } return 0; } int posix_handle_mkdir_hashes (xlator_t *this, const char *newpath) { char *duppath = NULL; char *parpath = NULL; int ret = 0; duppath = strdupa (newpath); parpath = dirname (duppath); parpath = dirname (duppath); ret = mkdir (parpath, 0700); if (ret == -1 && errno != EEXIST) { gf_log (this->name, GF_LOG_ERROR, "error mkdir hash-1 %s (%s)", parpath, strerror (errno)); return -1; } strcpy (duppath, newpath); parpath = dirname (duppath); ret = mkdir (parpath, 0700); if (ret == -1 && errno != EEXIST) { gf_log (this->name, GF_LOG_ERROR, "error mkdir hash-2 %s (%s)", parpath, strerror (errno)); return -1; } return 0; } int posix_handle_hard (xlator_t *this, const char *oldpath, uuid_t gfid, struct stat *oldbuf) { char *newpath = NULL; struct stat newbuf; int ret = -1; MAKE_HANDLE_PATH (newpath, this, gfid, NULL); ret = lstat (newpath, &newbuf); if (ret == -1 && errno != ENOENT) { gf_log (this->name, GF_LOG_WARNING, "%s: %s", newpath, strerror (errno)); return -1; } if (ret == -1 && errno == ENOENT) { ret = posix_handle_mkdir_hashes (this, newpath); if (ret) { gf_log (this->name, GF_LOG_WARNING, "mkdir %s failed (%s)", newpath, strerror (errno)); return -1; } ret = link (oldpath, newpath); if (ret) { gf_log (this->name, GF_LOG_WARNING, "link %s -> %s failed (%s)", oldpath, newpath, strerror (errno)); return -1; } ret = lstat (newpath, &newbuf); if (ret) { gf_log (this->name, GF_LOG_WARNING, "lstat on %s failed (%s)", newpath, strerror (errno)); return -1; } } ret = lstat (newpath, &newbuf); if (ret) { gf_log (this->name, GF_LOG_WARNING, "lstat on %s failed (%s)", newpath, strerror (errno)); return -1; } if (newbuf.st_ino != oldbuf->st_ino || newbuf.st_dev != oldbuf->st_dev) { gf_log (this->name, GF_LOG_WARNING, "mismatching ino/dev between file %s (%lld/%lld) " "and handle %s (%lld/%lld)", oldpath, (long long) oldbuf->st_ino, (long long) oldbuf->st_dev, newpath, (long long) newbuf.st_ino, (long long) newbuf.st_dev); ret = -1; } return ret; } int posix_handle_soft (xlator_t *this, const char *real_path, loc_t *loc, uuid_t gfid, struct stat *oldbuf) { char *oldpath = NULL; char *newpath = NULL; struct stat newbuf; int ret = -1; MAKE_HANDLE_PATH (newpath, this, gfid, NULL); MAKE_HANDLE_RELPATH (oldpath, this, loc->pargfid, loc->name); ret = lstat (newpath, &newbuf); if (ret == -1 && errno != ENOENT) { gf_log (this->name, GF_LOG_WARNING, "%s: %s", newpath, strerror (errno)); return -1; } if (ret == -1 && errno == ENOENT) { ret = posix_handle_mkdir_hashes (this, newpath); if (ret) { gf_log (this->name, GF_LOG_WARNING, "mkdir %s failed (%s)", newpath, strerror (errno)); return -1; } ret = symlink (oldpath, newpath); if (ret) { gf_log (this->name, GF_LOG_WARNING, "symlink %s -> %s failed (%s)", oldpath, newpath, strerror (errno)); return -1; } ret = lstat (newpath, &newbuf); if (ret) { gf_log (this->name, GF_LOG_WARNING, "stat on %s failed (%s)", newpath, strerror (errno)); return -1; } } ret = stat (real_path, &newbuf); if (ret) { gf_log (this->name, GF_LOG_WARNING, "stat on %s failed (%s)", newpath, strerror (errno)); return -1; } if (!oldbuf) return ret; if (newbuf.st_ino != oldbuf->st_ino || newbuf.st_dev != oldbuf->st_dev) { gf_log (this->name, GF_LOG_WARNING, "mismatching ino/dev between file %s (%lld/%lld) " "and handle %s (%lld/%lld)", oldpath, (long long) oldbuf->st_ino, (long long) oldbuf->st_dev, newpath, (long long) newbuf.st_ino, (long long) newbuf.st_dev); ret = -1; } return ret; } static int posix_handle_unset_gfid (xlator_t *this, uuid_t gfid) { char *path = NULL; int ret = 0; struct stat stat; MAKE_HANDLE_GFID_PATH (path, this, gfid, NULL); ret = lstat (path, &stat); if (ret == -1) { if (errno != ENOENT) { gf_log (this->name, GF_LOG_WARNING, "%s: %s", path, strerror (errno)); } goto out; } ret = unlink (path); if (ret == -1) { gf_log (this->name, GF_LOG_WARNING, "unlink %s failed (%s)", path, strerror (errno)); } out: return ret; } int posix_handle_unset (xlator_t *this, uuid_t gfid, const char *basename) { int ret; struct iatt stat; char *path = NULL; if (!basename) { ret = posix_handle_unset_gfid (this, gfid); return ret; } MAKE_HANDLE_PATH (path, this, gfid, basename); ret = posix_istat (this, gfid, basename, &stat); if (ret == -1) { gf_log (this->name, GF_LOG_WARNING, "%s: %s", path, strerror (errno)); return -1; } ret = posix_handle_unset_gfid (this, stat.ia_gfid); return ret; } int posix_create_link_if_gfid_exists (xlator_t *this, uuid_t gfid, char *real_path) { int ret = -1; struct stat stbuf = {0,}; char *newpath = NULL; MAKE_HANDLE_PATH (newpath, this, gfid, NULL); ret = lstat (newpath, &stbuf); if (!ret) { ret = link (newpath, real_path); } return ret; }