* Copyright (c) 2016, Intel Corporation.
*/
#include <errno.h>
+#include <fcntl.h>
#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <libcfs/util/param.h>
#include <linux/lustre/lustre_user.h>
}
if (strlen(pattern) + 3 > sizeof(pattern))
return -E2BIG;
- strncat(pattern, "-*", sizeof(pattern));
+ strncat(pattern, "-*", sizeof(pattern) - 1);
break;
case FILTER_BY_FS_NAME:
rc = snprintf(pattern, sizeof(pattern) - 1, "%s-*", filter);
return rc;
}
+
+int llapi_param_get_paths(const char *pattern, glob_t *paths)
+{
+ return get_lustre_param_path(NULL, NULL, FILTER_BY_NONE,
+ pattern, paths);
+}
+
+/**
+ * Read to the end of the file and count the bytes read.
+ */
+static int bytes_remaining(int fd, size_t *file_size)
+{
+ int rc = 0;
+ size_t bytes_read = 0;
+ long page_size = sysconf(_SC_PAGESIZE);
+ char *temp_buf;
+
+ temp_buf = malloc(page_size);
+ if (temp_buf == NULL)
+ return -ENOMEM;
+
+ while (1) {
+ ssize_t count = read(fd, temp_buf, page_size);
+
+ if (count == 0) {
+ *file_size = bytes_read;
+ break;
+ }
+
+ if (count < 0) {
+ rc = -errno;
+ break;
+ }
+ bytes_read += count;
+ }
+
+ free(temp_buf);
+ return rc;
+}
+
+/**
+ * Determine the size of a file by reading it.
+ */
+static int required_size(const char *path, size_t *file_size)
+{
+ int rc = 0;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ rc = bytes_remaining(fd, file_size);
+
+ close(fd);
+ *file_size += 1;
+ return rc;
+}
+
+static
+int copy_file_expandable(const char *path, char **buf, size_t *file_size)
+{
+ long page_size = sysconf(_SC_PAGESIZE);
+ int rc = 0;
+ char *temp_buf;
+ int fd;
+ FILE *fp;
+
+ fp = open_memstream(buf, file_size);
+ if (fp == NULL) {
+ rc = -errno;
+ goto out;
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ rc = -errno;
+ goto close_stream;
+ }
+
+ temp_buf = calloc(1, page_size);
+ if (buf == NULL) {
+ rc = -ENOMEM;
+ goto close_file;
+ }
+
+ while (1) {
+ ssize_t count = read(fd, temp_buf, page_size);
+
+ if (count == 0)
+ break;
+ if (count < 0) {
+ rc = -errno;
+ break;
+ }
+
+ if (fwrite(temp_buf, 1, count, fp) != count) {
+ rc = -errno;
+ break;
+ }
+ }
+
+ free(temp_buf);
+close_file:
+ close(fd);
+close_stream:
+ fclose(fp);
+out:
+ /* If rc != 0 and *buf != NULL, the caller may retry.
+ * This would likely result in copy_file_fixed() being called
+ * on accident, and a likely memory error.
+ */
+ if (rc != 0) {
+ free(*buf);
+ *buf = NULL;
+ }
+ return rc;
+}
+
+/**
+ * Copy file to a buffer and write the number of bytes copied
+ */
+static int copy_file_fixed(const char *path, char *buf, size_t *buflen)
+{
+ int rc = 0;
+ size_t bytes_read = 0;
+ size_t max_read = *buflen - 1;
+ size_t remaining = 0;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ while (bytes_read < max_read) {
+ ssize_t count = read(fd,
+ buf + bytes_read,
+ max_read - bytes_read);
+
+ /* read the entire file */
+ if (count == 0) {
+ *buflen = bytes_read + 1;
+ buf[bytes_read] = '\0';
+ goto out;
+ }
+
+ if (count < 0)
+ goto check_size;
+
+ bytes_read += count;
+ }
+
+check_size:
+ /* need to check size in case error due to buf being too small
+ * for read() or exited loop due to buf being full
+ */
+ buf[max_read] = '\0';
+
+ rc = bytes_remaining(fd, &remaining);
+ if (rc != 0) {
+ rc = -errno;
+ goto out;
+ }
+ *buflen = bytes_read + remaining;
+
+ /* file was not (*buflen - 1) bytes, add 1 for reallocating */
+ if (remaining != 0) {
+ *buflen += 1;
+ rc = -EOVERFLOW;
+ }
+
+out:
+ close(fd);
+
+ return rc;
+}
+
+/**
+ * Read the value of the file with location \a path
+ * into a buffer.
+ *
+ * \param path[in] the location of a parameter file
+ * \param buf[in,out] a pointer to a pointer to a buffer
+ * \param buflen[in,out] the length of a pre-allocated buffer
+ * when passed in, and either the number
+ * of bytes written or the suggested
+ * size of *buf when passed out.
+ *
+ * There are 3 behaviors based on the value of buf.
+ * If buf == NULL, then the buffer size needed to read the file at
+ * \a path will be written to \a *buflen.
+ * If \a buf != NULL and \a *buf == NULL, the value of *buf will point
+ * to a buffer that will be automatically sized to fit the file
+ * contents. A NUL byte will be added to the end of the buffer.
+ * The value of \a *buflen will be set to the number of bytes written
+ * excuding the NUL byte.
+ * If \a buf != NULL and \a *buf != NULL, it will be assumed that \a *buf
+ * points to a pre-allocated buffer with a capacity of \a *buflen.
+ * If there is sufficient space, the file contents and NUL terminating
+ * byte will be written to the buffer at .\a *buf.
+ * Otherwise, the required size of \a *buflen with be written to \a *buflen.
+ *
+ * Returns 0 for success with null terminated string in \a *buf.
+ * Returns negative errno value on error.
+ * For case of \a buf != NULL and \a *buf != NULL, a return value
+ * of -EOVERFLOW indicates that it's possible retry with a larger
+ * buffer.
+ */
+int llapi_param_get_value(const char *path, char **buf, size_t *buflen)
+{
+ int rc = 0;
+
+ if (path == NULL || buflen == NULL)
+ rc = -EINVAL;
+ else if (buf == NULL)
+ rc = required_size(path, buflen);
+ /* handle for buffer, but no buffer
+ * create a buffer of the required size
+ */
+ else if (*buf == NULL)
+ rc = copy_file_expandable(path, buf, buflen);
+ /* preallocated buffer given, attempt to copy
+ * file to it, return file size if buffer too small
+ */
+ else
+ rc = copy_file_fixed(path, *buf, buflen);
+
+ errno = -rc;
+
+ return rc;
+}
+
+void llapi_param_paths_free(glob_t *paths)
+{
+ cfs_free_param_data(paths);
+}