Whamcloud - gitweb
b=19053
[fs/lustre-release.git] / lustre / utils / liblustreapi.c
index 0164cd7..7a315c7 100644 (file)
@@ -58,6 +58,7 @@
 #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
@@ -373,25 +374,6 @@ int llapi_file_create_pool(const char *name, unsigned long long stripe_size,
         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;
-}
-
 /*
  * Find the fsname, the full path, and/or an open fd.
  * Either the fsname or path must not be NULL
@@ -399,13 +381,16 @@ static int print_pool_members(char *fs, char *pool_dir, char *pool_file)
 #define WANT_PATH   0x1
 #define WANT_FSNAME 0x2
 #define WANT_FD     0x4
-static int get_root_path(int want, char *fsname, int *outfd, char *path)
+#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];
-        char *ptr;
+        char buf[PATH_MAX], mntdir[PATH_MAX];
+        char *name = NULL, *ptr;
         FILE *fp;
-        int fd;
+        int idx = 0, len = 0, mntlen, fd;
         int rc = -ENODEV;
 
         /* get the mount point */
@@ -423,27 +408,47 @@ static int get_root_path(int want, char *fsname, int *outfd, char *path)
                 if (!llapi_is_lustre_mnt(&mnt))
                         continue;
 
+                mntlen = strlen(mnt.mnt_dir);
                 ptr = strrchr(mnt.mnt_fsname, '/');
-                if (!ptr) {
+                if (!ptr && !len) {
                         rc = -EINVAL;
                         break;
                 }
                 ptr++;
 
-                /* If path was specified and matches, store the fsname */
-                if ((want & WANT_FSNAME) && (strcmp(mnt.mnt_dir, path) == 0))
-                        strcpy(fsname, ptr);
-                /* Else check the fsname for a match */
-                else if (strcmp(ptr, fsname) != 0)
+                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;
 
-                /* Found it */
-                rc = 0;
-                if (want & WANT_PATH)
-                        strcpy(path, mnt.mnt_dir);
+                /* 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;
+                }
+        }
+        endmntent(fp);
+
+        /* 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(mnt.mnt_dir,
-                                  O_RDONLY | O_DIRECTORY | O_NONBLOCK);
+                        fd = open(mntdir, O_RDONLY | O_DIRECTORY | O_NONBLOCK);
                         if (fd < 0) {
                                 perror("open");
                                 rc = -errno;
@@ -451,19 +456,45 @@ static int get_root_path(int want, char *fsname, int *outfd, char *path)
                                 *outfd = fd;
                         }
                 }
-                break;
-        }
-        endmntent(fp);
-        if (rc)
+        } 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, fsname, NULL, (char *)pathname);
+        return get_root_path(WANT_FSNAME | WANT_ERROR, fsname, NULL,
+                             (char *)pathname, -1);
 }
 
 /* return the first file matching this pattern */
@@ -496,7 +527,7 @@ static int poolpath(char *fsname, char *pathname, char *pool_pathname)
         char buffer[PATH_MAX];
 
         if (fsname == NULL) {
-                rc = get_root_path(WANT_FSNAME, buffer, NULL, pathname);
+                rc = llapi_search_fsname(pathname, buffer);
                 if (rc != 0)
                         return rc;
                 fsname = buffer;
@@ -515,13 +546,108 @@ static int poolpath(char *fsname, char *pathname, char *pool_pathname)
         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 *poolname;
-        char *fsname;
-        char rname[PATH_MAX + 1], pathname[PATH_MAX + 1];
+        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 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, '/');
@@ -542,16 +668,10 @@ int llapi_poollist(char *name)
                                   " 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) {
@@ -561,32 +681,79 @@ int llapi_poollist(char *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);
 
@@ -2489,7 +2656,7 @@ int llapi_changelog_start(void **priv, int flags, const char *device,
         int rc, fd;
 
         if (device[0] == '/')
-                rc = get_root_path(WANT_FSNAME, mdtname, NULL, (char *)device);
+                rc = llapi_search_fsname(device, mdtname);
         else
                 strncpy(mdtname, device, sizeof(mdtname));
 
@@ -2642,7 +2809,7 @@ int llapi_changelog_clear(const char *mdtname, const char *idstr,
                 ptr = fsname + strlen(fsname) - 8;
                 *ptr = '\0';
                 index = strtol(ptr + 4, NULL, 10);
-                rc = get_root_path(WANT_FD, fsname, &fd, NULL);
+                rc = get_root_path(WANT_FD | WANT_ERROR, fsname, &fd, NULL, -1);
         }
         if (rc < 0) {
                 llapi_err(LLAPI_MSG_ERROR | LLAPI_MSG_NO_ERRNO,
@@ -2684,7 +2851,8 @@ int llapi_fid2path(const char *device, const char *fidstr, char *buf,
         if (device[0] == '/') {
                 strcpy(path, device);
         } else {
-                rc = get_root_path(WANT_PATH, (char *)device, NULL, path);
+                rc = get_root_path(WANT_PATH | WANT_ERROR, (char *)device,
+                                   NULL, path, -1);
                 if (rc < 0)
                         return rc;
         }
@@ -2712,15 +2880,35 @@ int llapi_fid2path(const char *device, const char *fidstr, char *buf,
         return rc;
 }
 
+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)
 {
         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);
+        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;