#include <sys/stat.h>
#include <sys/types.h>
#include <sys/syscall.h>
+#include <sys/xattr.h>
#include <fnmatch.h>
#include <glob.h>
#ifdef HAVE_LINUX_UNISTD_H
#else
#include <unistd.h>
#endif
+#include <poll.h>
-#include <libcfs/libcfsutil.h> /* l_ioctl */
#include <liblustre.h>
#include <lnet/lnetctl.h>
#include <obd.h>
return 0;
}
-int llapi_stripe_limit_check(unsigned long stripe_size, int stripe_offset,
+int llapi_stripe_limit_check(unsigned long long stripe_size, int stripe_offset,
int stripe_count, int stripe_pattern)
{
int page_size;
stripe_count);
return -EINVAL;
}
- if (stripe_count > 0 && (__u64)stripe_size * stripe_count > 0xffffffff){
+ if (stripe_size >= (1ULL << 32)){
errno = -EINVAL;
- llapi_err(LLAPI_MSG_ERROR, "error: stripe_size %lu * "
- "stripe_count %u exceeds 4GB", stripe_size,
- stripe_count);
+ llapi_err(LLAPI_MSG_ERROR, "warning: stripe size larger than 4G"
+ " is not currently supported and would wrap");
return -EINVAL;
}
return 0;
static int poolpath(char *fsname, char *pathname, char *pool_pathname);
int llapi_file_open_pool(const char *name, int flags, int mode,
- unsigned long stripe_size, int stripe_offset,
+ unsigned long long stripe_size, int stripe_offset,
int stripe_count, int stripe_pattern, char *pool_name)
{
struct lov_user_md_v3 lum = { 0 };
}
int llapi_file_open(const char *name, int flags, int mode,
- unsigned long stripe_size, int stripe_offset,
+ unsigned long long stripe_size, int stripe_offset,
int stripe_count, int stripe_pattern)
{
return llapi_file_open_pool(name, flags, mode, stripe_size,
stripe_pattern, NULL);
}
-int llapi_file_create(const char *name, unsigned long stripe_size,
+int llapi_file_create(const char *name, unsigned long long stripe_size,
int stripe_offset, int stripe_count, int stripe_pattern)
{
int fd;
return 0;
}
-int llapi_file_create_pool(const char *name, unsigned long stripe_size,
+int llapi_file_create_pool(const char *name, unsigned long long stripe_size,
int stripe_offset, int stripe_count,
int stripe_pattern, char *pool_name)
{
return 0;
}
-
-static int print_pool_members(char *fs, char *pool_dir, char *pool_file)
-{
- char path[PATH_MAX + 1];
- char buf[1024];
- FILE *fd;
-
- llapi_printf(LLAPI_MSG_NORMAL, "Pool: %s.%s\n", fs, pool_file);
- sprintf(path, "%s/%s", pool_dir, pool_file);
- if ((fd = fopen(path, "r")) == NULL) {
- llapi_err(LLAPI_MSG_ERROR, "Cannot open %s\n", path);
- return -EINVAL;
- }
- while (fgets(buf, sizeof(buf), fd) != NULL)
- llapi_printf(LLAPI_MSG_NORMAL, buf);
-
- fclose(fd);
- return 0;
-}
-
/*
- * Resolve lustre fsname from pathname
+ * Find the fsname, the full path, and/or an open fd.
+ * Either the fsname or path must not be NULL
*/
-static int search_fsname(char *pathname, char *fsname)
-{
- char *ptr;
+#define WANT_PATH 0x1
+#define WANT_FSNAME 0x2
+#define WANT_FD 0x4
+#define WANT_INDEX 0x8
+#define WANT_ERROR 0x10
+static int get_root_path(int want, char *fsname, int *outfd, char *path,
+ int index)
+{
+ struct mntent mnt;
+ char buf[PATH_MAX], mntdir[PATH_MAX];
+ char *name = NULL, *ptr;
FILE *fp;
- struct mntent *mnt = NULL;
+ int idx = 0, len = 0, mntlen, fd;
+ int rc = -ENODEV;
/* get the mount point */
fp = setmntent(MOUNTED, "r");
strerror (errno));
return -EIO;
}
- mnt = getmntent(fp);
- while ((feof(fp) == 0) && ferror(fp) == 0) {
- if (llapi_is_lustre_mnt(mnt)) {
- /* search by pathname */
- if (strncmp(mnt->mnt_dir, pathname,
- max(strlen(pathname),
- strlen(mnt->mnt_dir))) == 0) {
- ptr = strchr(mnt->mnt_fsname, '/');
- if (ptr == NULL)
- return -EINVAL;
- ptr++;
- strcpy(fsname, ptr);
- return 0;
- }
+ while (1) {
+ if (getmntent_r(fp, &mnt, buf, sizeof(buf)) == NULL)
+ break;
+
+ if (!llapi_is_lustre_mnt(&mnt))
+ continue;
+
+ mntlen = strlen(mnt.mnt_dir);
+ ptr = strrchr(mnt.mnt_fsname, '/');
+ if (!ptr && !len) {
+ rc = -EINVAL;
+ break;
+ }
+ ptr++;
+
+ if ((want & WANT_INDEX) && (idx++ != index))
+ continue;
+
+ /* Check the fsname for a match */
+ if (!(want & WANT_FSNAME) && fsname != NULL &&
+ (strlen(fsname) > 0) && (strcmp(ptr, fsname) != 0))
+ continue;
+
+ /* If the path isn't set return the first one we find */
+ if (path == NULL || strlen(path) == 0) {
+ strcpy(mntdir, mnt.mnt_dir);
+ name = ptr;
+ rc = 0;
+ break;
+ /* Otherwise find the longest matching path */
+ } else if ((strlen(path) >= mntlen) && (mntlen >= len) &&
+ (strncmp(mnt.mnt_dir, path, mntlen) == 0)) {
+ strcpy(mntdir, mnt.mnt_dir);
+ mntlen = len;
+ name = ptr;
+ rc = 0;
}
- mnt = getmntent(fp);
}
endmntent(fp);
- return -ENOENT;
+
+ /* Found it */
+ if (rc == 0) {
+ if ((want & WANT_FSNAME) && fsname != NULL)
+ strcpy(fsname, name);
+ if ((want & WANT_PATH) && path != NULL)
+ strcpy(path, mntdir);
+ if (want & WANT_FD) {
+ fd = open(mntdir, O_RDONLY | O_DIRECTORY | O_NONBLOCK);
+ if (fd < 0) {
+ perror("open");
+ rc = -errno;
+ } else {
+ *outfd = fd;
+ }
+ }
+ } else if (want & WANT_ERROR)
+ llapi_err(LLAPI_MSG_ERROR | LLAPI_MSG_NO_ERRNO,
+ "can't find fs root for '%s': %d",
+ (want & WANT_PATH) ? fsname : path, rc);
+ return rc;
+}
+
+/*
+ * search lustre mounts
+ *
+ * Calling this function will return to the user the mount point, mntdir, and
+ * the file system name, fsname, if the user passed a buffer to this routine.
+ *
+ * The user inputs are pathname and index. If the pathname is supplied then
+ * the value of the index will be ignored. The pathname will return data if
+ * the pathname is located on a lustre mount. Index is used to pick which
+ * mount point you want in the case of multiple mounted lustre file systems.
+ * See function lfs_osts in lfs.c for a example of the index use.
+ */
+int llapi_search_mounts(const char *pathname, int index, char *mntdir,
+ char *fsname)
+{
+ int want = WANT_PATH, idx = -1;
+
+ if (!pathname) {
+ want |= WANT_INDEX;
+ idx = index;
+ } else
+ strcpy(mntdir, pathname);
+
+ if (fsname)
+ want |= WANT_FSNAME;
+ return get_root_path(want, fsname, NULL, mntdir, idx);
+}
+
+int llapi_search_fsname(const char *pathname, char *fsname)
+{
+ return get_root_path(WANT_FSNAME | WANT_ERROR, fsname, NULL,
+ (char *)pathname, -1);
}
/* return the first file matching this pattern */
char buffer[PATH_MAX];
if (fsname == NULL) {
- rc = search_fsname(pathname, buffer);
+ rc = llapi_search_fsname(pathname, buffer);
if (rc != 0)
return rc;
fsname = buffer;
strcpy(pathname, fsname);
}
- snprintf(pattern, PATH_MAX,
- "/proc/fs/lustre/lov/%s-*/pools",
- fsname);
+ snprintf(pattern, PATH_MAX, "/proc/fs/lustre/lov/%s-*/pools", fsname);
rc = first_match(pattern, buffer);
if (rc)
return rc;
return 0;
}
-int llapi_poollist(char *name)
+/**
+ * Get the list of pool members.
+ * \param poolname string of format <fsname>.<poolname>
+ * \param members caller-allocated array of char*
+ * \param list_size size of the members array
+ * \param buffer caller-allocated buffer for storing OST names
+ * \param buffer_size size of the buffer
+ *
+ * \return number of members retrieved for this pool
+ * \retval -error failure
+ */
+int llapi_get_poolmembers(const char *poolname, char **members,
+ int list_size, char *buffer, int buffer_size)
+{
+ char fsname[PATH_MAX + 1];
+ char *pool, *tmp;
+ char pathname[PATH_MAX + 1];
+ char path[PATH_MAX + 1];
+ char buf[1024];
+ FILE *fd;
+ int rc = 0;
+ int nb_entries = 0;
+ int used = 0;
+
+ /* name is FSNAME.POOLNAME */
+ if (strlen(poolname) > PATH_MAX)
+ return -EOVERFLOW;
+ strcpy(fsname, poolname);
+ pool = strchr(fsname, '.');
+ if (pool == NULL)
+ return -EINVAL;
+
+ *pool = '\0';
+ pool++;
+
+ rc = poolpath(fsname, NULL, pathname);
+ if (rc != 0) {
+ errno = -rc;
+ llapi_err(LLAPI_MSG_ERROR, "Lustre filesystem '%s' not found",
+ fsname);
+ return rc;
+ }
+
+ llapi_printf(LLAPI_MSG_NORMAL, "Pool: %s.%s\n", fsname, pool);
+ sprintf(path, "%s/%s", pathname, pool);
+ if ((fd = fopen(path, "r")) == NULL) {
+ llapi_err(LLAPI_MSG_ERROR, "Cannot open %s", path);
+ return -EINVAL;
+ }
+
+ rc = 0;
+ while (fgets(buf, sizeof(buf), fd) != NULL) {
+ if (nb_entries >= list_size) {
+ rc = -EOVERFLOW;
+ break;
+ }
+ /* remove '\n' */
+ if ((tmp = strchr(buf, '\n')) != NULL)
+ *tmp='\0';
+ if (used + strlen(buf) + 1 > buffer_size) {
+ rc = -EOVERFLOW;
+ break;
+ }
+
+ strcpy(buffer + used, buf);
+ members[nb_entries] = buffer + used;
+ used += strlen(buf) + 1;
+ nb_entries++;
+ rc = nb_entries;
+ }
+
+ fclose(fd);
+ return rc;
+}
+
+/**
+ * Get the list of pools in a filesystem.
+ * \param name filesystem name or path
+ * \param poollist caller-allocated array of char*
+ * \param list_size size of the poollist array
+ * \param buffer caller-allocated buffer for storing pool names
+ * \param buffer_size size of the buffer
+ *
+ * \return number of pools retrieved for this filesystem
+ * \retval -error failure
+ */
+int llapi_get_poollist(const char *name, char **poollist, int list_size,
+ char *buffer, int buffer_size)
{
- char *poolname;
- char *fsname;
- char rname[PATH_MAX + 1], pathname[PATH_MAX + 1];
+ char fsname[PATH_MAX + 1], rname[PATH_MAX + 1], pathname[PATH_MAX + 1];
char *ptr;
+ DIR *dir;
+ struct dirent pool;
+ struct dirent *cookie = NULL;
int rc = 0;
+ unsigned int nb_entries = 0;
+ unsigned int used = 0;
+ unsigned int i;
+
+ /* initilize output array */
+ for (i = 0; i < list_size; i++)
+ poollist[i] = NULL;
/* is name a pathname ? */
ptr = strchr(name, '/');
return -EINVAL;
if (!realpath(name, rname)) {
rc = -errno;
- llapi_err(LLAPI_MSG_ERROR,
- "llapi_poollist: invalid path '%s'",
- name);
+ llapi_err(LLAPI_MSG_ERROR, "invalid path '%s'", name);
return rc;
}
rc = poolpath(NULL, rname, pathname);
if (rc != 0) {
errno = -rc;
- llapi_err(LLAPI_MSG_ERROR,
- "llapi_poollist: '%s' is not"
- " a Lustre filesystem",
- name);
+ llapi_err(LLAPI_MSG_ERROR, "'%s' is not"
+ " a Lustre filesystem", name);
return rc;
}
- fsname = rname;
- poolname = NULL;
+ strcpy(fsname, rname);
} else {
- /* name is FSNAME[.POOLNAME] */
- fsname = name;
- poolname = strchr(name, '.');
- if (poolname != NULL) {
- *poolname = '\0';
- poolname++;
- }
+ /* name is FSNAME */
+ strcpy(fsname, name);
rc = poolpath(fsname, NULL, pathname);
- if (rc != 0) {
- errno = -rc;
- llapi_err(LLAPI_MSG_ERROR,
- "llapi_poollist: Lustre filesystem '%s'"
- " not found", name);
- return rc;
- }
}
if (rc != 0) {
errno = -rc;
- llapi_err(LLAPI_MSG_ERROR,
- "llapi_poollist: Lustre filesystem '%s' not found",
+ llapi_err(LLAPI_MSG_ERROR, "Lustre filesystem '%s' not found",
name);
return rc;
}
- if (poolname != NULL) {
- rc = print_pool_members(fsname, pathname, poolname);
- poolname--;
- *poolname = '.';
- } else {
- DIR *dir;
- struct dirent *pool;
+ llapi_printf(LLAPI_MSG_NORMAL, "Pools from %s:\n", fsname);
+ if ((dir = opendir(pathname)) == NULL) {
+ llapi_err(LLAPI_MSG_ERROR, "Could not open pool list for '%s'",
+ name);
+ return -errno;
+ }
- llapi_printf(LLAPI_MSG_NORMAL, "Pools from %s:\n", fsname);
- if ((dir = opendir(pathname)) == NULL) {
- return -EINVAL;
- }
- while ((pool = readdir(dir)) != NULL) {
- if (!((pool->d_name[0] == '.') &&
- (pool->d_name[1] == '\0')) &&
- !((pool->d_name[0] == '.') &&
- (pool->d_name[1] == '.') &&
- (pool->d_name[2] == '\0')))
- llapi_printf(LLAPI_MSG_NORMAL, " %s.%s\n",
- fsname, pool->d_name);
- }
- closedir(dir);
+ while(1) {
+ rc = readdir_r(dir, &pool, &cookie);
+
+ if (rc != 0) {
+ llapi_err(LLAPI_MSG_ERROR,
+ "Error reading pool list for '%s'", name);
+ return -errno;
+ } else if ((rc == 0) && (cookie == NULL))
+ /* end of directory */
+ break;
+
+ /* ignore . and .. */
+ if (!strcmp(pool.d_name, ".") || !strcmp(pool.d_name, ".."))
+ continue;
+
+ /* check output bounds */
+ if (nb_entries >= list_size)
+ return -EOVERFLOW;
+
+ /* +2 for '.' and final '\0' */
+ if (used + strlen(pool.d_name) + strlen(fsname) + 2
+ > buffer_size)
+ return -EOVERFLOW;
+
+ sprintf(buffer + used, "%s.%s", fsname, pool.d_name);
+ poollist[nb_entries] = buffer + used;
+ used += strlen(pool.d_name) + strlen(fsname) + 2;
+ nb_entries++;
}
- return rc;
+
+ closedir(dir);
+ return nb_entries;
+}
+
+/* wrapper for lfs.c and obd.c */
+int llapi_poollist(const char *name)
+{
+ /* list of pool names (assume that pool count is smaller
+ than OST count) */
+ char *list[FIND_MAX_OSTS];
+ char *buffer;
+ /* fsname-OST0000_UUID < 32 char, 1 per OST */
+ int bufsize = FIND_MAX_OSTS * 32;
+ int i, nb;
+
+ buffer = malloc(bufsize);
+ if (buffer == NULL)
+ return -ENOMEM;
+
+ if ((name[0] == '/') || (strchr(name, '.') == NULL))
+ /* name is a path or fsname */
+ nb = llapi_get_poollist(name, list, FIND_MAX_OSTS, buffer,
+ bufsize);
+ else
+ /* name is a pool name (<fsname>.<poolname>) */
+ nb = llapi_get_poolmembers(name, list, FIND_MAX_OSTS, buffer,
+ bufsize);
+
+ for (i = 0; i < nb; i++)
+ llapi_printf(LLAPI_MSG_NORMAL, "%s\n", list[i]);
+
+ free(buffer);
+ return (nb < 0 ? nb : 0);
}
+
typedef int (semantic_func_t)(char *path, DIR *parent, DIR *d,
void *data, cfs_dirent_t *de);
return 0;
}
+static void lov_dump_user_lmm_header(struct lov_user_md *lum, char *path,
+ int is_dir, int verbose, int quiet,
+ char *pool_name)
+{
+ char *prefix = is_dir ? "" : "lmm_";
+ char nl = is_dir ? ' ' : '\n';
+
+ if (verbose && path)
+ llapi_printf(LLAPI_MSG_NORMAL, "%s\n", path);
+
+ if ((verbose & VERBOSE_DETAIL) && !is_dir) {
+ llapi_printf(LLAPI_MSG_NORMAL, "lmm_magic: 0x%08X\n",
+ lum->lmm_magic);
+ llapi_printf(LLAPI_MSG_NORMAL, "lmm_object_gr: "LPX64"\n",
+ lum->lmm_object_gr);
+ llapi_printf(LLAPI_MSG_NORMAL, "lmm_object_id: "LPX64"\n",
+ lum->lmm_object_id);
+ }
+
+ if (verbose & VERBOSE_COUNT) {
+ if (!quiet)
+ llapi_printf(LLAPI_MSG_NORMAL, "%sstripe_count: ",
+ prefix);
+ llapi_printf(LLAPI_MSG_NORMAL, "%u%c",
+ (int)lum->lmm_stripe_count, nl);
+ }
+
+ if (verbose & VERBOSE_SIZE) {
+ if (!quiet)
+ llapi_printf(LLAPI_MSG_NORMAL, "%sstripe_size: ",
+ prefix);
+ llapi_printf(LLAPI_MSG_NORMAL, "%u%c", lum->lmm_stripe_size,
+ nl);
+ }
+
+ if ((verbose & VERBOSE_DETAIL) && !is_dir) {
+ llapi_printf(LLAPI_MSG_NORMAL, "lmm_stripe_pattern: %x%c",
+ lum->lmm_pattern, nl);
+ }
+
+ if (verbose & VERBOSE_OFFSET) {
+ if (!quiet)
+ llapi_printf(LLAPI_MSG_NORMAL, "%sstripe_offset: ",
+ prefix);
+ llapi_printf(LLAPI_MSG_NORMAL, "%u%c",
+ lum->lmm_objects[0].l_ost_idx, nl);
+ }
+
+ if ((verbose & VERBOSE_POOL) && (pool_name != NULL))
+ llapi_printf(LLAPI_MSG_NORMAL, "pool: %s%c", pool_name, nl);
+
+ if (is_dir)
+ llapi_printf(LLAPI_MSG_NORMAL, "\n");
+}
+
void lov_dump_user_lmm_v1v3(struct lov_user_md *lum, char *pool_name,
struct lov_user_ost_data_v1 *objects,
char *path, int is_dir,
break;
}
}
- } else if (!quiet) {
- llapi_printf(LLAPI_MSG_NORMAL, "%s\n", path);
+ } else {
+ if (!quiet)
+ llapi_printf(LLAPI_MSG_NORMAL, "%s\n", path);
obdstripe = 1;
}
llapi_printf(LLAPI_MSG_NORMAL, "(Default) ");
lum->lmm_object_gr = LOV_OBJECT_GROUP_CLEAR;
}
- llapi_printf(LLAPI_MSG_NORMAL,
- "stripe_count: %d stripe_size: %u "
- "stripe_offset: %d%s%s\n",
- lum->lmm_stripe_count == (__u16)-1 ? -1 :
- lum->lmm_stripe_count,
- lum->lmm_stripe_size,
- lum->lmm_stripe_offset == (__u16)-1 ? -1 :
- lum->lmm_stripe_offset,
- pool_name != NULL ? " pool: " : "",
- pool_name != NULL ? pool_name : "");
+ /* maintain original behavior */
+ if (!header)
+ header |= VERBOSE_ALL;
+ lov_dump_user_lmm_header(lum, path, is_dir, header,
+ quiet, pool_name);
}
return;
}
- if (header && (obdstripe == 1)) {
- llapi_printf(LLAPI_MSG_NORMAL, "lmm_magic: 0x%08X\n",
- lum->lmm_magic);
- llapi_printf(LLAPI_MSG_NORMAL, "lmm_object_gr: "LPX64"\n",
- lum->lmm_object_gr);
- llapi_printf(LLAPI_MSG_NORMAL, "lmm_object_id: "LPX64"\n",
- lum->lmm_object_id);
- llapi_printf(LLAPI_MSG_NORMAL, "lmm_stripe_count: %u\n",
- (int)lum->lmm_stripe_count);
- llapi_printf(LLAPI_MSG_NORMAL, "lmm_stripe_size: %u\n",
- lum->lmm_stripe_size);
- llapi_printf(LLAPI_MSG_NORMAL, "lmm_stripe_pattern: %x\n",
- lum->lmm_pattern);
- if (pool_name != NULL)
- llapi_printf(LLAPI_MSG_NORMAL,
- "lmm_pool_name: %s\n", pool_name);
- }
+ if (header && (obdstripe == 1))
+ lov_dump_user_lmm_header(lum, NULL, is_dir, header, quiet,
+ pool_name);
if (body) {
if ((!quiet) && (obdstripe == 1))
break;
}
}
- } else if (!quiet) {
- llapi_printf(LLAPI_MSG_NORMAL, "%s\n", path);
+ } else {
+ if (!quiet)
+ llapi_printf(LLAPI_MSG_NORMAL, "%s\n", path);
obdstripe = 1;
}
if (header && obdstripe == 1) {
- llapi_printf(LLAPI_MSG_NORMAL, "lmm_magic: 0x%08X\n",
- lumj->lmm_magic);
- llapi_printf(LLAPI_MSG_NORMAL, "lmm_object_gr: "LPX64"\n",
- lumj->lmm_object_gr);
- llapi_printf(LLAPI_MSG_NORMAL, "lmm_object_id: "LPX64"\n",
- lumj->lmm_object_id);
- llapi_printf(LLAPI_MSG_NORMAL, "lmm_stripe_count: %u\n",
- (int)lumj->lmm_stripe_count);
- llapi_printf(LLAPI_MSG_NORMAL, "lmm_stripe_size: %u\n",
- lumj->lmm_stripe_size);
- llapi_printf(LLAPI_MSG_NORMAL, "lmm_stripe_pattern: %x\n",
- lumj->lmm_pattern);
+ lov_dump_user_lmm_header(lum, NULL, 0, header, quiet, NULL);
+
llapi_printf(LLAPI_MSG_NORMAL, "lmm_extent_count: %x\n",
lumj->lmm_extent_count);
}
continue;
/* Don't traverse .lustre directory */
- if (!(strcmp(dent->d_name, mdd_dot_lustre_name)))
+ if (!(strcmp(dent->d_name, dot_lustre_name)))
continue;
path[len] = 0;
exit(execvp(argv[0], argv));
}
-/* format must have %s%s, buf must be > 16 */
-static int get_mdtname(const char *name, char *format, char *buf)
+/* Print mdtname 'name' into 'buf' using 'format'. Add -MDT0000 if needed.
+ * format must have %s%s, buf must be > 16
+ */
+static int get_mdtname(char *name, char *format, char *buf)
{
char suffix[]="-MDT0000";
int len = strlen(name);
- if (len > 16) {
- llapi_err(LLAPI_MSG_ERROR, "bad MDT name |%s|\n", name);
- return -EINVAL;
+ if ((len > 5) && (strncmp(name + len - 5, "_UUID", 5) == 0)) {
+ name[len - 5] = '\0';
+ len -= 5;
}
- if ((len > 8) && (strncmp(name + len - 8, "-MDT", 4) == 0))
- suffix[0] = '\0';
+ if (len > 8) {
+ if ((len <= 16) && strncmp(name + len - 8, "-MDT", 4) == 0) {
+ suffix[0] = '\0';
+ } else {
+ /* Not enough room to add suffix */
+ llapi_err(LLAPI_MSG_ERROR | LLAPI_MSG_NO_ERRNO,
+ "MDT name too long |%s|", name);
+ return -EINVAL;
+ }
+ }
return sprintf(buf, format, name, suffix);
}
+/****** Changelog API ********/
+#define CHANGELOG_PRIV_MAGIC 0xCA8E1080
+struct changelog_private {
+ int magic;
+ int fd;
+ int flags;
+};
-/* Return a file descriptor to a readable changelog */
-int llapi_changelog_open(const char *mdtname, long long startrec)
+/** Start reading from a changelog
+ * @param priv Opaque private control structure
+ * @param flags Start flags (e.g. follow)
+ * @param device Report changes recorded on this MDT
+ * @param startrec Report changes beginning with this record number
+ */
+int llapi_changelog_start(void **priv, int flags, const char *device,
+ long long startrec)
{
+ struct changelog_private *cp;
char path[256];
+ char mdtname[20];
int rc, fd;
+ if (device[0] == '/')
+ rc = llapi_search_fsname(device, mdtname);
+ else
+ strncpy(mdtname, device, sizeof(mdtname));
+
/* Use either the mdd changelog (preferred) or a client mdc changelog */
if (get_mdtname(mdtname,
"/proc/fs/lustre/md[cd]/%s%s{,-mdc-*}/changelog",
return -errno;
}
- return fd;
+ cp = malloc(sizeof(*cp));
+ if (cp == NULL) {
+ close(fd);
+ return -ENOMEM;
+ }
+
+ cp->magic = CHANGELOG_PRIV_MAGIC;
+ cp->fd = fd;
+ cp->flags = flags;
+ *priv = cp;
+
+ return 0;
}
-static int dev_ioctl(struct obd_ioctl_data *data, int dev, int cmd)
+/** Finish reading from a changelog */
+int llapi_changelog_fini(void **priv)
{
- static char rawbuf[8192];
- static char *buf = rawbuf;
- int rc;
+ struct changelog_private *cp = (struct changelog_private *)*priv;
- data->ioc_dev = dev;
- memset(buf, 0, sizeof(rawbuf));
+ if (!cp || (cp->magic != CHANGELOG_PRIV_MAGIC))
+ return -EINVAL;
- if ((rc = obd_ioctl_pack(data, &buf, sizeof(rawbuf)))) {
- llapi_err(LLAPI_MSG_ERROR,
- "error: ioctl pack (%d) failed: rc %d", cmd, rc);
- return rc;
- }
+ close(cp->fd);
+ free(cp);
+ *priv = NULL;
+ return 0;
+}
- rc = l_ioctl(OBD_DEV_ID, cmd, buf);
- if (rc < 0) {
- /* ioctl returns -1 with errno set */
- rc = -errno;
- return rc;
- }
+static int pollwait(int fd) {
+ struct pollfd pfds[1];
+ int rc;
- if (obd_ioctl_unpack(data, buf, sizeof(rawbuf))) {
- llapi_err(LLAPI_MSG_ERROR,
- "error: invalid reply\n");
- return -EPROTO;
- }
- return rc;
+ pfds[0].fd = fd;
+ pfds[0].events = POLLIN;
+ rc = poll(pfds, 1, -1);
+ return rc < 0 ? -errno : rc;
}
-static int dev_name2dev(char *name)
+/** Read the next changelog entry
+ * @param priv Opaque private control structure
+ * @param rech Changelog record handle; record will be allocated here
+ * @return 0 valid message received; rec is set
+ * <0 error code
+ * 1 EOF
+ */
+int llapi_changelog_recv(void *priv, struct changelog_rec **rech)
{
- struct obd_ioctl_data data;
- int rc;
+ struct changelog_private *cp = (struct changelog_private *)priv;
+ struct changelog_rec rec, *recp;
+ int rc = 0;
- memset(&data, 0, sizeof(data));
- data.ioc_inllen1 = strlen(name) + 1;
- data.ioc_inlbuf1 = name;
+ if (!cp || (cp->magic != CHANGELOG_PRIV_MAGIC))
+ return -EINVAL;
+ if (rech == NULL)
+ return -EINVAL;
+
+readrec:
+ /* Read in the rec to get the namelen */
+ rc = read(cp->fd, &rec, sizeof(rec));
+ if (rc < 0)
+ return -errno;
+ if (rc == 0) {
+ if (cp->flags && CHANGELOG_FLAG_FOLLOW) {
+ rc = pollwait(cp->fd);
+ if (rc < 0)
+ return rc;
+ goto readrec;
+ }
+ return 1;
+ }
- rc = dev_ioctl(&data, -1, OBD_IOC_NAME2DEV);
+ recp = malloc(sizeof(rec) + rec.cr_namelen);
+ if (recp == NULL)
+ return -ENOMEM;
+ memcpy(recp, &rec, sizeof(rec));
+ rc = read(cp->fd, recp->cr_name, rec.cr_namelen);
if (rc < 0) {
- llapi_err(LLAPI_MSG_ERROR, "Device %s not found %d\n", name,rc);
- return rc;
+ free(recp);
+ llapi_err(LLAPI_MSG_ERROR, "Can't read entire filename");
+ return -errno;
}
- return data.ioc_dev;
-}
-/* We need the full mdc name, and we shouldn't just grep from proc... */
-static void do_get_mdcname(char *obd_type_name, char *obd_name,
- char *obd_uuid, void *name)
-{
- if (strncmp(obd_name, (char *)name, strlen((char *)name)) == 0)
- strcpy((char *)name, obd_name);
+ *rech = recp;
+ return 0;
}
-static int get_mdcdev(const char *mdtname)
+/** Release the changelog record when done with it. */
+int llapi_changelog_free(struct changelog_rec **rech)
{
- char name[MAX_OBD_NAME];
- char *type[] = { "mdc" };
- int rc;
-
- strcpy(name, mdtname);
- rc = llapi_target_iterate(1, type, (void *)name, do_get_mdcname);
- rc = rc < 0 ? : -rc;
- if (rc < 0) {
- llapi_err(LLAPI_MSG_ERROR, "Device %s not found %d\n", name,rc);
- return rc;
- }
- return dev_name2dev(name);
+ if (*rech)
+ free(*rech);
+ *rech = NULL;
+ return 0;
}
int llapi_changelog_clear(const char *mdtname, const char *idstr,
long long endrec)
{
- struct obd_ioctl_data data;
- int dev, id, rc;
+ struct ioc_changelog_clear data;
+ char fsname[17];
+ char *ptr;
+ int id, fd, index, rc;
if (endrec < 0) {
llapi_err(LLAPI_MSG_ERROR | LLAPI_MSG_NO_ERRNO,
return -EINVAL;
}
- dev = get_mdcdev(mdtname);
- if (dev < 0) {
+ /* Take path, fsname, or MDTNAME. Assume MDT0000 in the former cases */
+ if (mdtname[0] == '/') {
+ index = 0;
+ fd = open(mdtname, O_RDONLY | O_DIRECTORY | O_NONBLOCK);
+ rc = fd < 0 ? -errno : 0;
+ } else {
+ if (get_mdtname((char *)mdtname, "%s%s", fsname) < 0)
+ return -EINVAL;
+ ptr = fsname + strlen(fsname) - 8;
+ *ptr = '\0';
+ index = strtol(ptr + 4, NULL, 10);
+ rc = get_root_path(WANT_FD | WANT_ERROR, fsname, &fd, NULL, -1);
+ }
+ if (rc < 0) {
llapi_err(LLAPI_MSG_ERROR | LLAPI_MSG_NO_ERRNO,
- "can't find mdc for '%s'\n", mdtname);
- return dev;
+ "Can't open %s: %d\n", mdtname, rc);
+ return rc;
}
- memset(&data, 0, sizeof(data));
- data.ioc_u32_1 = id;
- data.ioc_u64_1 = endrec;
- rc = dev_ioctl(&data, dev, OBD_IOC_CHANGELOG_CLEAR);
+ data.icc_mdtindex = index;
+ data.icc_id = id;
+ data.icc_recno = endrec;
+ rc = ioctl(fd, OBD_IOC_CHANGELOG_CLEAR, &data);
if (rc)
- llapi_err(LLAPI_MSG_ERROR | LLAPI_MSG_NO_ERRNO,
- "ioctl err %d", rc);
+ llapi_err(LLAPI_MSG_ERROR, "ioctl err %d", rc);
+
+ close(fd);
return rc;
}
-
-int llapi_fid2path(char *device, char *fidstr, char *buf, int buflen,
- long long *recno, int *linkno)
+int llapi_fid2path(const char *device, const char *fidstr, char *buf,
+ int buflen, long long *recno, int *linkno)
{
+ char path[PATH_MAX];
struct lu_fid fid;
- struct obd_ioctl_data data;
- char buffer[256];
- int dev, rc;
+ struct getinfo_fid2path *gf;
+ int fd, rc;
while (*fidstr == '[')
fidstr++;
- sscanf(fidstr, "0x%llx:0x%x:0x%x", &(fid.f_seq), &(fid.f_oid),
- &(fid.f_ver));
+ sscanf(fidstr, SFID, RFID(&fid));
if (!fid_is_sane(&fid)) {
llapi_err(LLAPI_MSG_ERROR | LLAPI_MSG_NO_ERRNO,
"bad FID format [%s], should be "DFID"\n",
return -EINVAL;
}
- rc = get_mdtname(device, "%s%s", buffer);
- if (rc < 0)
- return rc;
+ /* Take path or fsname */
+ if (device[0] == '/') {
+ strcpy(path, device);
+ } else {
+ rc = get_root_path(WANT_PATH | WANT_ERROR, (char *)device,
+ NULL, path, -1);
+ if (rc < 0)
+ return rc;
+ }
+ sprintf(path, "%s/%s/fid/%s", path, dot_lustre_name, fidstr);
+ fd = open(path, O_RDONLY | O_NONBLOCK);
+ if (fd < 0)
+ return -errno;
- dev = dev_name2dev(buffer);
- if (dev < 0)
- return dev;
-
- memset(&data, 0, sizeof(data));
- data.ioc_inlbuf1 = (char *)&fid;
- data.ioc_inllen1 = sizeof(fid);
- data.ioc_inlbuf2 = (char *)recno;
- data.ioc_inllen2 = sizeof(__u64);
- data.ioc_inlbuf3 = (char *)linkno;
- data.ioc_inllen3 = sizeof(int);
- data.ioc_plen1 = buflen;
- data.ioc_pbuf1 = buf;
- rc = dev_ioctl(&data, dev, OBD_IOC_FID2PATH);
+ gf = malloc(sizeof(*gf) + buflen);
+ gf->gf_fid = fid;
+ gf->gf_recno = *recno;
+ gf->gf_linkno = *linkno;
+ gf->gf_pathlen = buflen;
+ rc = ioctl(fd, OBD_IOC_FID2PATH, gf);
+ if (rc) {
+ llapi_err(LLAPI_MSG_ERROR, "ioctl err %d", rc);
+ } else {
+ memcpy(buf, gf->gf_path, gf->gf_pathlen);
+ *recno = gf->gf_recno;
+ *linkno = gf->gf_linkno;
+ }
+ free(gf);
+ close(fd);
return rc;
}
-int llapi_path2fid(const char *path, unsigned long long *seq,
- unsigned long *oid, unsigned long *ver)
+static int path2fid_from_lma(const char *path, lustre_fid *fid)
+{
+ char buf[512];
+ struct lustre_mdt_attrs *lma;
+ int rc;
+
+ rc = lgetxattr(path, XATTR_NAME_LMA, buf, sizeof(buf));
+ if (rc < 0)
+ return -errno;
+ lma = (struct lustre_mdt_attrs *)buf;
+ fid_be_to_cpu(fid, &lma->lma_self_fid);
+ return 0;
+}
+
+int llapi_path2fid(const char *path, lustre_fid *fid)
{
- struct lu_fid fid;
int fd, rc;
- fd = open(path, O_RDONLY);
- if (fd < 0)
+ memset(fid, 0, sizeof(*fid));
+ fd = open(path, O_RDONLY | O_NONBLOCK | O_NOFOLLOW);
+ if (fd < 0) {
+ if (errno == ELOOP) /* symbolic link */
+ return path2fid_from_lma(path, fid);
return -errno;
+ }
- rc = ioctl(fd, LL_IOC_PATH2FID, &fid);
- *seq = fid_seq(&fid);
- *oid = fid_oid(&fid);
- *ver = fid_ver(&fid);
+ rc = ioctl(fd, LL_IOC_PATH2FID, fid) < 0 ? -errno : 0;
+ if (rc == -EINVAL) /* char special device */
+ rc = path2fid_from_lma(path, fid);
close(fd);
return rc;
}
+/****** HSM Copytool API ********/
+#define CT_PRIV_MAGIC 0xC0BE2001
+struct copytool_private {
+ int magic;
+ lustre_netlink lnl;
+ int archive_num_count;
+ int archive_nums[0];
+};
+
+#include <libcfs/libcfs.h>
+
+/** Register a copytool
+ * @param priv Opaque private control structure
+ * @param flags Open flags, currently unused (e.g. O_NONBLOCK)
+ * @param archive_nums Which archive numbers this copytool is responsible for
+ */
+int llapi_copytool_start(void **priv, int flags, int archive_num_count,
+ int *archive_nums)
+{
+ struct copytool_private *ct;
+ int rc;
+
+ if (archive_num_count > 0 && archive_nums == NULL) {
+ llapi_err(LLAPI_MSG_ERROR | LLAPI_MSG_NO_ERRNO,
+ "NULL archive numbers");
+ return -EINVAL;
+ }
+
+ ct = malloc(sizeof(*ct) +
+ archive_num_count * sizeof(ct->archive_nums[0]));
+ if (ct == NULL)
+ return -ENOMEM;
+
+ ct->magic = CT_PRIV_MAGIC;
+ ct->archive_num_count = archive_num_count;
+ if (ct->archive_num_count > 0)
+ memcpy(ct->archive_nums, archive_nums, archive_num_count *
+ sizeof(ct->archive_nums[0]));
+
+ rc = libcfs_ulnl_start(&ct->lnl, LNL_GRP_HSM);
+ if (rc < 0)
+ goto out_err;
+
+ *priv = ct;
+ return 0;
+
+out_err:
+ free(ct);
+ return rc;
+}
+
+/** Deregister a copytool */
+int llapi_copytool_fini(void **priv)
+{
+ struct copytool_private *ct = (struct copytool_private *)*priv;
+
+ if (!ct || (ct->magic != CT_PRIV_MAGIC))
+ return -EINVAL;
+
+ libcfs_ulnl_stop(&ct->lnl);
+ free(ct);
+ *priv = NULL;
+ return 0;
+}
+
+/** Wait for the next hsm_action_list
+ * @param priv Opaque private control structure
+ * @param halh Action list handle, will be allocated here
+ * @param msgsize Number of bytes in the message, will be set here
+ * @return 0 valid message received; halh and msgsize are set
+ * <0 error code
+ */
+int llapi_copytool_recv(void *priv, struct hsm_action_list **halh, int *msgsize)
+{
+ struct copytool_private *ct = (struct copytool_private *)priv;
+ struct lnl_hdr *lnlh;
+ struct hsm_action_list *hal;
+ int rc = 0;
+
+ if (!ct || (ct->magic != CT_PRIV_MAGIC))
+ return -EINVAL;
+ if (halh == NULL || msgsize == NULL)
+ return -EINVAL;
+
+ rc = libcfs_ulnl_msg_get(&ct->lnl, HAL_MAXSIZE,
+ LNL_TRANSPORT_HSM, &lnlh);
+ if (rc < 0)
+ return rc;
+
+ /* Handle generic messages */
+ if (lnlh->lnl_transport == LNL_TRANSPORT_GENERIC &&
+ lnlh->lnl_msgtype == LNL_MSG_SHUTDOWN) {
+ rc = -ESHUTDOWN;
+ goto out_free;
+ }
+
+ if (lnlh->lnl_transport != LNL_TRANSPORT_HSM ||
+ lnlh->lnl_msgtype != HMT_ACTION_LIST) {
+ llapi_err(LLAPI_MSG_ERROR | LLAPI_MSG_NO_ERRNO,
+ "Unknown HSM message type %d:%d\n",
+ lnlh->lnl_transport, lnlh->lnl_msgtype);
+ rc = -EPROTO;
+ goto out_free;
+ }
+
+ /* Our message is an hsm_action_list */
+
+ hal = (struct hsm_action_list *)(lnlh + 1);
+
+ /* Check that we have registered for this archive # */
+ for (rc = 0; rc < ct->archive_num_count; rc++) {
+ if (hal->hal_archive_num == ct->archive_nums[rc])
+ break;
+ }
+ if (rc >= ct->archive_num_count) {
+ CDEBUG(D_INFO, "This copytool does not service archive #%d, "
+ "ignoring this request.\n", hal->hal_archive_num);
+ rc = 0;
+ goto out_free;
+ }
+
+ *halh = hal;
+ *msgsize = lnlh->lnl_msglen - sizeof(*lnlh);
+ return 0;
+
+out_free:
+ libcfs_ulnl_msg_free(&lnlh);
+ *halh = NULL;
+ *msgsize = 0;
+ return rc;
+}
+
+/** Release the action list when done with it. */
+int llapi_copytool_free(struct hsm_action_list **hal)
+{
+ if (*hal) {
+ struct lnl_hdr *lnlh = (struct lnl_hdr *)*hal - 1;
+ libcfs_ulnl_msg_free(&lnlh);
+ }
+ *hal = NULL;
+ return 0;
+}
+
+