Whamcloud - gitweb
LU-2182 llapi: implementation of new llapi_layout API 02/5302/37
authorNed Bass <bass6@llnl.gov>
Mon, 7 Jul 2014 21:51:37 +0000 (17:51 -0400)
committerOleg Drokin <oleg.drokin@intel.com>
Mon, 25 Aug 2014 16:57:02 +0000 (16:57 +0000)
Add a new set of llapi routines for interacting with file layouts that
hide the details of the wire-protocol data structures from the user.
Define an opaque struct llapi_layout to abstract the layout of a
lustre file and generic accessor functions to read and write it.  The
following documented functions are added the liblustreapi public
interface with accompanying man pages:

  llapi_layout_alloc - allocate a new layout
  llapi_layout_free - free memory allocated for a layout
  llapi_layout_file_create - create new file with given layout
  llapi_layout_file_open - open or create a file with given layout
  llapi_layout_get_by_path - get file layout given a path
  llapi_layout_get_by_fd - get file layout given a file descriptor
  llapi_layout_get_by_fid - get file layout given a Lustre FID
  llapi_layout_ost_index_get - get OST index associated with a stripe
  llapi_layout_ost_index_set - set OST index associated with a stripe
  llapi_layout_pattern_get - get RAID pattern of a layout
  llapi_layout_pattern_set - set RAID pattern of a layout
  llapi_layout_pool_name_get - get pool name of a layout
  llapi_layout_pool_name_set - set pool name of a layout
  llapi_layout_stripe_count_get - get stripe count of a layout
  llapi_layout_stripe_count_set - set stripe count of a layout
  llapi_layout_stripe_size_get - get stripe size of a layout
  llapi_layout_stripe_size_set - set stripe size of a layout

The layouts are read and written using fgetxattr() and fsetxattr()
instead of ioctl() to make it easier for architectures like Blue Gene
to function-ship the system calls.

Signed-off-by: Ned Bass <bass6@llnl.gov>
Signed-off-by: James Simmons <uja.ornl@gmail.com>
Change-Id: I35fb51055b6438ef3090f43c28a4083a66eaa907
Reviewed-on: http://review.whamcloud.com/5302
Tested-by: Jenkins
Tested-by: Maloo <hpdd-maloo@intel.com>
Reviewed-by: John L. Hammond <john.hammond@intel.com>
Reviewed-by: Jinshan Xiong <jinshan.xiong@intel.com>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
33 files changed:
lustre/doc/Makefile.am
lustre/doc/llapi_layout.7 [new file with mode: 0644]
lustre/doc/llapi_layout_alloc.3 [new file with mode: 0644]
lustre/doc/llapi_layout_file_create.3 [new file with mode: 0644]
lustre/doc/llapi_layout_file_open.3 [new file with mode: 0644]
lustre/doc/llapi_layout_free.3 [new file with mode: 0644]
lustre/doc/llapi_layout_get_by_fd.3 [new file with mode: 0644]
lustre/doc/llapi_layout_get_by_fid.3 [new file with mode: 0644]
lustre/doc/llapi_layout_get_by_path.3 [new file with mode: 0644]
lustre/doc/llapi_layout_ost_index_get.3 [new file with mode: 0644]
lustre/doc/llapi_layout_ost_index_set.3 [new file with mode: 0644]
lustre/doc/llapi_layout_pattern_get.3 [new file with mode: 0644]
lustre/doc/llapi_layout_pattern_set.3 [new file with mode: 0644]
lustre/doc/llapi_layout_pool_name_get.3 [new file with mode: 0644]
lustre/doc/llapi_layout_pool_name_set.3 [new file with mode: 0644]
lustre/doc/llapi_layout_stripe_count_get.3 [new file with mode: 0644]
lustre/doc/llapi_layout_stripe_count_set.3 [new file with mode: 0644]
lustre/doc/llapi_layout_stripe_size_get.3 [new file with mode: 0644]
lustre/doc/llapi_layout_stripe_size_set.3 [new file with mode: 0644]
lustre/doc/llapi_path2fid.3 [new file with mode: 0644]
lustre/doc/lustreapi.7
lustre/include/lustre/lustre_idl.h
lustre/include/lustre/lustreapi.h
lustre/tests/.gitignore
lustre/tests/Makefile.am
lustre/tests/llapi_layout_test.c [new file with mode: 0644]
lustre/tests/sanity.sh
lustre/tests/test-framework.sh
lustre/utils/LCOPYING
lustre/utils/Makefile.am
lustre/utils/liblustreapi.c
lustre/utils/liblustreapi_layout.c [new file with mode: 0644]
lustre/utils/lustreapi_internal.h

index 3dd6a9a..ad96c11 100644 (file)
@@ -44,7 +44,16 @@ MANFILES = lustre.7 lfs.1 mount.lustre.8 lctl.8 \
        lustre_rsync.8 lfs_migrate.1 lhbadm.8 ldev.8 ldev.conf.5 nids.5 \
        lfs-hsm.1 llapi_hsm_state_get.3 llapi_hsm_state_set.3 \
        lustre_routes_config.8 lustre_routes_conversion.8 \
-       lfs-setdirstripe.1 lfs-mkdir.1 lfs-getdirstripe.1
+       lfs-setdirstripe.1 lfs-mkdir.1 lfs-getdirstripe.1 \
+       llapi_layout.7 llapi_layout_alloc.3 llapi_layout_file_create.3 \
+       llapi_layout_file_open.3 llapi_layout_free.3 llapi_layout_get_by_fd.3 \
+       llapi_layout_get_by_fid.3 llapi_layout_get_by_path.3 \
+       llapi_layout_ost_index_get.3 llapi_layout_ost_index_set.3 \
+       llapi_layout_pattern_get.3 llapi_layout_pattern_set.3 \
+       llapi_layout_pool_name_get.3 llapi_layout_pool_name_set.3 \
+       llapi_layout_stripe_count_get.3 llapi_layout_stripe_count_set.3 \
+       llapi_layout_stripe_size_get.3
+
 SERVER_MANFILES = mkfs.lustre.8 tunefs.lustre.8
 
 if SERVER
diff --git a/lustre/doc/llapi_layout.7 b/lustre/doc/llapi_layout.7
new file mode 100644 (file)
index 0000000..045d742
--- /dev/null
@@ -0,0 +1,124 @@
+.TH llapi_layout 7 "2013 Oct 31" "Lustre User API"
+.SH NAME
+llapi_layout \- abstract interface to the layout of a Lustre file
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.SH DESCRIPTION
+.LP
+The
+.B llapi_layout
+family of functions functions provides an abstract interface to
+manipulating the layout information of a file in a Lustre filesystem.
+Layouts are represented by the opaque data type
+.B llapi_layout_t
+which is passed as a handle to the various functions.
+.PP
+A layout has a number of attributes that describe how a file's data are
+stored in the filesystem.  These include stripe count, stripe size, RAID
+pattern, pool name, and the OST index associated with each stripe. Refer
+to the Lustre Operations Manual for detailed descriptions of these
+attributes.  For each attribute, there exists a pair of functions with
+the suffixes
+.B _get
+and
+.B _set
+that are used to read and assign the attribute value in a given layout.
+.PP
+Using this interface to create a file might consist of the following steps.
+.IP \[bu]
+Allocate a layout with
+.BR llapi_layout_alloc() .
+.IP \[bu]
+Assign attribute values using the
+.B llapi_layout_*_set()
+functions.
+.IP \[bu]
+Create the file using
+.BR llapi_layout_file_create() .
+.IP \[bu]
+Free the layout memory using
+.BR llapi_layout_free() .
+.PP
+Similarly, these steps might be used to read a file layout:
+.IP \[bu]
+Obtain the layout with
+.BR llapi_layout_get_by_path() ,
+.BR llapi_layout_get_by_fd() ,
+or
+.BR llapi_layout_get_by_fid() .
+.IP \[bu]
+Read attribute values using the
+.B llapi_layout_*_get()
+functions.
+.IP \[bu]
+Free the layout memory using
+.BR llapi_layout_free() .
+.SH "EXAMPLE"
+.nf
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <lustre/lustreapi.h>
+
+int main(int argc, char *argv[])
+{
+       int             fd;
+       struct llapi_layout *layout;
+       uint64_t        count = 2;
+       uint64_t        size = 1048576;
+       char            *path;
+
+       if (argc != 2)
+               return -1;
+
+       path = argv[1];
+       layout = llapi_layout_alloc();
+       llapi_layout_stripe_count_set(layout, count);
+       llapi_layout_stripe_size_set(layout, size);
+       fd = llapi_layout_file_create(path, 0, 0640, layout);
+       if (fd < 0) {
+               fprintf(stderr, "cannot create %s: %s\\n", path,
+                       strerror(errno));
+               return -1;
+       }
+       close(fd);
+       llapi_layout_free(layout);
+
+       layout = llapi_layout_get_by_path(path, 0);
+       llapi_layout_stripe_size_get(layout, &size),
+       llapi_layout_stripe_count_get(layout, &count);
+       printf("%s with stripe size %llu, striped across %llu OSTs,"
+              " has been created!\\n", path, size, count);
+       llapi_layout_free(layout);
+       return 0;
+}
+.fi
+.SH "BUGS"
+Setting the OST index number is only supported for stripe number 0.
+
+The RAID pattern may only be set to 0.
+.SH "SEE ALSO"
+.BR open (2),
+.BR lustre (7),
+.BR lustreapi (7),
+.BR llapi_layout_alloc (3),
+.BR llapi_layout_file_create (3),
+.BR llapi_layout_file_open (3),
+.BR llapi_layout_free (3),
+.BR llapi_layout_get_by_fd (3),
+.BR llapi_layout_get_by_fid (3),
+.BR llapi_layout_get_by_path (3),
+.BR llapi_layout_ost_index_get (3),
+.BR llapi_layout_ost_index_set (3),
+.BR llapi_layout_pattern_get (3),
+.BR llapi_layout_pattern_set (3),
+.BR llapi_layout_pool_name_get (3),
+.BR llapi_layout_pool_name_set (3),
+.BR llapi_layout_stripe_count_get (3),
+.BR llapi_layout_stripe_count_set (3),
+.BR llapi_layout_stripe_size_get (3),
+.BR llapi_layout_stripe_size_set (3),
+.BR lfs (1)
diff --git a/lustre/doc/llapi_layout_alloc.3 b/lustre/doc/llapi_layout_alloc.3
new file mode 100644 (file)
index 0000000..571ba55
--- /dev/null
@@ -0,0 +1,64 @@
+.TH llapi_layout_alloc 3 "2013 Oct 31" "Lustre User API"
+.SH NAME
+llapi_layout_alloc, llapi_layout_free \- allocate and destroy
+.B llapi_layout_t
+objects.
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.sp
+.BI "struct llapi_layout *llapi_layout_alloc(void);"
+.sp
+.BI "void llapi_layout_free(struct llapi_layout *"layout );
+.sp
+.fi
+.SH DESCRIPTION
+.LP
+.B llapi_layout_alloc()
+returns a pointer to a newly-allocated
+.BR struct llapi_layout .
+The
+.B llapi_layout_t
+is an opaque entity containing the layout information for a file in a
+Lustre filesystem.  Its internal structure should not be directly
+accessed by an application.  It may be used in
+subsequent calls to the functions referenced in the
+.BR llapi_layout (7)
+manual page to modify layout attributes and create files with the given
+layout.
+.PP
+The returned
+.B llapi_layout_t
+object is initialized with default attribute values that will effect the
+standard Lustre behavior for assigning layouts to newly-created files.
+These values may be modified using the group of
+functions in the
+.B llapi_layout
+API whose names end with
+.BR _set .
+The pointer should be freed with
+.B llapi_layout_free()
+when it is no longer needed.
+.PP
+.B llapi_layout_free()
+frees the memory associated with
+.IR layout .
+.SH RETURN VALUES
+.PP
+.B llapi_layout_alloc()
+returns a valid pointer on success or
+.B NULL
+on failure with
+.B errno
+set to an approporiate error code.
+.sp
+.B llapi_layout_free()
+returns no value.
+.SH ERRORS
+.TP 15
+.SM ENOMEM
+Insufficient storage space is available.
+.SH "SEE ALSO"
+.BR llapi_layout_file_create (3),
+.BR llapi_layout (7),
+.BR liblustreapi (7)
diff --git a/lustre/doc/llapi_layout_file_create.3 b/lustre/doc/llapi_layout_file_create.3
new file mode 100644 (file)
index 0000000..7f828f8
--- /dev/null
@@ -0,0 +1,102 @@
+.TH llapi_layout_file_open 3 "2013 Oct 31" "Lustre User API"
+.SH NAME
+llapi_layout_file_open, llapi_layout_file_create \- open and apply a layout to a Lustre file
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.PP
+.BI "int llapi_layout_file_open(char *" path ", int " open_flags ", int " mode ,
+.BI "                           const struct llapi_layout *" layout );
+.PP
+.BI "int llapi_layout_file_create(char *" path ", int " open_flags ", int " mode ,
+.BI "                             const struct llapi_layout *" layout );
+.fi
+.SH DESCRIPTION
+.LP
+The functions
+.B llapi_layout_file_open()
+and
+.B llapi_layout_file_create()
+open and possibly create a file at
+.I path
+with the permissions specified in
+.I mode
+using the Lustre layout attributes in
+.IR layout .
+The returned file descriptor may be used in subsequent system calls
+.RB ( read (2),
+.BR write (2),
+.BR lseek (2),
+etc.), and closed with
+.BR close (2).
+One access mode and zero or more file creation flags and file status
+flags may be bitwise-or'd in
+.IR open_flags .
+See
+.BR open (2).
+If
+.I layout
+is non-NULL and
+.I path
+is not on a Lustre filesystem this function will fail and set
+.B errno
+to
+.BR ENOTTY .
+.PP
+The function call
+.PP
+.B "    llapi_layout_file_create(path, open_flags, mode, layout)"
+.PP
+shall be equivalent to:
+.PP
+.B "    llapi_layout_file_open(path, open_flags|O_CREAT|O_EXCL, mode, layout)"
+.PP
+The
+.I layout
+and
+.I mode
+will not be applied to a file which already exists. Callers requiring a
+guarantee that the opened file is created with the specified
+.I layout
+and
+.I mode
+should use
+.BR llapi_layout_file_create() .
+.PP
+A
+.B NULL
+.I layout
+may be specified, in which case the standard Lustre behavior for
+assigning layouts to newly-created files will apply.
+.PP
+If the pool name attribute of
+.I layout
+has been set, it must refer to an OST pool that exists and contains one
+or more OSTs, otherwise these functions will fail and set
+.B errno
+to
+.BR EINVAL .
+See
+.BR llapi_layout_pool_name_set (3).
+.SH RETURN VALUES
+.LP
+.B llapi_layout_file_open()
+and
+.B llapi_layout_file_create()
+return a new file descriptor, or -1 if an error occurred (in which
+case, errno is set appropriately).
+.SH ERRORS
+.TP 15
+.SM ENOTTY
+.I path
+does not reside on a Lustre filesystem.
+.TP
+.SM EINVAL
+An invalid argument was specified.
+.SH "SEE ALSO"
+.BR llapi_layout (7),
+.BR liblustreapi (7),
+.BR llapi_layout_alloc (3),
+.BR llapi_layout_pool_name_set (3),
+.BR open (2),
+.BR close (2)
diff --git a/lustre/doc/llapi_layout_file_open.3 b/lustre/doc/llapi_layout_file_open.3
new file mode 100644 (file)
index 0000000..3ae8ecd
--- /dev/null
@@ -0,0 +1 @@
+.so man3/llapi_layout_file_create.3
diff --git a/lustre/doc/llapi_layout_free.3 b/lustre/doc/llapi_layout_free.3
new file mode 100644 (file)
index 0000000..8543f03
--- /dev/null
@@ -0,0 +1 @@
+.so man3/llapi_layout_alloc.3
diff --git a/lustre/doc/llapi_layout_get_by_fd.3 b/lustre/doc/llapi_layout_get_by_fd.3
new file mode 100644 (file)
index 0000000..a29e9b0
--- /dev/null
@@ -0,0 +1,142 @@
+.TH llapi_layout_get_by_fd 3 "2013 Oct 31" "Lustre User API"
+.SH NAME
+llapi_layout_get_by_fd, llapi_layout_get_by_fid, llapi_layout_get_by_path, \-
+obtain the layout of a Lustre file
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.PP
+.BI "struct llapi_layout *llapi_layout_get_by_fd(int "fd ", uint32_t " flags );
+.PP
+.BI "struct llapi_layout *llapi_layout_get_by_fid(const char *"lustre_path ,
+.BI "                                             const lustre_fid *"fid ,
+.BI "                                             uint32_t " flags );
+.PP
+.BI "struct llapi_layout *llapi_layout_get_by_path(const char *"path ,
+.BI "                                              uint32_t " flags );
+.fi
+.SH DESCRIPTION
+.PP
+.BR llapi_layout_get_by_fd() ,
+.BR llapi_layout_get_by_fid() ,
+and
+.BR llapi_layout_get_by_path()
+return a pointer to a newly-allocated
+.B struct llapi_layout
+containing the layout information for the file referenced by
+.IR fd ,
+.IR fid ,
+or
+.IR path .
+The
+.B struct llapi_layout
+is an opaque entity containing the layout information for a file in a
+Lustre filesystem.  Its internal structure should not be directly
+accessed by an application.  See
+.BR llapi_layout (7).
+The pointer should be freed with
+.B llapi_layout_free()
+when it is no longer needed.
+.PP
+For
+.BR llapi_layout_get_by_fd() ,
+.I fd
+is a valid open file descriptor for a file or directory in a Lustre
+filesystem.
+.PP
+For
+.BR llapi_layout_get_by_fid() ,
+the path named by
+.I lustre_path
+serves to identify the Lustre filesystem containing the file
+represented by
+.IR fid .
+It is typically the filesystem root, but may also be any path beneath
+the root.  Use the function
+.BR llapi_path2fid (3)
+to obtain a
+.B lustre_fid
+associated with a given path.
+.PP
+The function
+.B llapi_layout_get_by_path()
+accepts a
+.I path
+argument that names a file or directory in a Lustre filesystem.
+.PP
+Zero or more flags may be bitwise-or'd together in
+.I flags
+to control how a layout is retrieved.  Currently
+.B llapi_layout_get_by_path()
+accepts only one flag, and
+.B llapi_layout_get_by_fd()
+and
+.B llapi_layout_get_by_fid()
+do not accept any flags. The list of flags is as follows:
+.TP 5
+.SM LAYOUT_GET_EXPECTED
+Unspecified attribute values are replaced by the literal default values
+that will be assigned when the file is created or first written to.
+A default value is inherited from the parent directory if the attribute
+is specified there, otherwise it is inherited from the filesystem root.
+This flag is only recognized by
+.BR llapi_layout_get_by_path() .
+Unspecified attributes may belong to directories and never-written-to
+files.
+.sp
+By default, layouts report the abstract value
+.B LLAPI_LAYOUT_DEFAULT
+to indicate an unspecified attribute.  Use
+.B LAYOUT_GET_EXPECTED
+to discover the expected literal values for new files in a given
+directory.  Do not use it if you need to distinguish between specified
+and unspecified attributes.  The flag has no effect if
+.I path
+names a file or directory with a fully specified layout.
+.sp
+For concreteness, consider a Lustre filesystem with a default stripe
+size of 1048576 and a default stripe count of 1.  A user sets the stripe
+count for directory D to 2 (thus overriding the filesystem-wide
+default) but leaves the stripe size unspecified.  Newly created files in
+D inherit a stripe count of 2 from D and a stripe size of 1048576 from
+the filesystem default.  The layout of D returned by
+.B llapi_layout_get_by_path(D, 0)
+has the abstract stripe size value
+.BR LLAPI_LAYOUT_DEFAULT ,
+since stripe size is unspecified, while
+.B llapi_layout_get_by_path(D, LAYOUT_GET_EXPECTED)
+reports the literal value 1048576.  Both forms report a stripe count
+of 2, since that attribute is specified.
+.SH RETURN VALUES
+.LP
+.BR llapi_layout_get_by_fd() ,
+.BR llapi_layout_get_by_fid() ,
+and
+.B llapi_layout_get_by_path()
+return a valid pointer on success or
+.B NULL
+on failure with
+.B errno
+set to an approporiate error code.
+.SH ERRORS
+.TP 15
+.SM ENOMEM
+Insufficient storage space is available.
+.TP
+.SM ENOTTY
+File does not reside on a Lustre filesystem.
+.TP
+.SM ENOENT
+.I path
+does not exist.
+.TP
+.SM EINVAL
+An invalid argument was specified.
+.TP
+.SM EINTR
+The kernel returned less than the expected amount of data.
+.SH "SEE ALSO"
+.BR llapi_layout_file_open (3),
+.BR llapi_path2fid (3),
+.BR llapi_layout (7),
+.BR liblustreapi (7)
diff --git a/lustre/doc/llapi_layout_get_by_fid.3 b/lustre/doc/llapi_layout_get_by_fid.3
new file mode 100644 (file)
index 0000000..5a4e926
--- /dev/null
@@ -0,0 +1 @@
+.so man3/llapi_layout_get_by_fd.3
diff --git a/lustre/doc/llapi_layout_get_by_path.3 b/lustre/doc/llapi_layout_get_by_path.3
new file mode 100644 (file)
index 0000000..5a4e926
--- /dev/null
@@ -0,0 +1 @@
+.so man3/llapi_layout_get_by_fd.3
diff --git a/lustre/doc/llapi_layout_ost_index_get.3 b/lustre/doc/llapi_layout_ost_index_get.3
new file mode 100644 (file)
index 0000000..d8742d8
--- /dev/null
@@ -0,0 +1,69 @@
+.TH llapi_layout_ost_index_get 3 "2013 Oct 31" "Lustre User API"
+.SH NAME
+llapi_layout_ost_index_get, llapi_layout_ost_index_set \- get or set the
+OST index of a stripe of a Lustre file
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.PP
+.BI "int llapi_layout_ost_index_get(const struct llapi_layout *" layout ,
+.BI "                               int " stripe_number ", uint64_t *" ost_index );
+.PP
+.BI "int llapi_layout_ost_index_set(struct llapi_layout *" layout ,
+.BI "                               int " stripe_number ", uint64_t " ost_index );
+.fi
+.SH DESCRIPTION
+.PP
+.B llapi_layout_ost_index_get()
+stores into
+.I ost_index
+the index number of the Lustre OST associated with stripe number
+.I stripe_number
+in layout
+.IR layout .
+.PP
+.B llapi_layout_ost_index_set()
+sets the OST index of stripe number
+.I stripe_number
+in layout
+.I layout
+to
+.IR ost_index .
+This allows an application to control which OSTs will be used to
+allocate storage for a file.  Setting the OST index is currently only
+supported for stripe 0.
+.PP
+It is an error to call
+.B llapi_layout_ost_index_get()
+with a
+.I layout
+that was not initialized with with one of
+.BR llapi_layout_get_by_fd() ,
+.BR llapi_layout_get_by_fid() ,
+or
+.BR llapi_layout_get_by_path() .
+.PP
+An
+.I ost_index
+value of
+.B LLAPI_LAYOUT_DEFAULT
+means that an index will be automatically assigned by the filesystem.
+.SH RETURN VALUES
+.LP
+.B llapi_layout_ost_index_get()
+and
+.B llapi_layout_ost_index_set()
+return 0 on success, or -1 if an error occurred (in which case, errno is
+set appropriately).
+.SH ERRORS
+.TP 15
+.SM EINVAL
+An invalid argument was specified.
+.TP 15
+.SM EOPNOTSUPP
+Attempted to set index of a stripe other than stripe 0.
+.SH "SEE ALSO"
+.BR llapi_layout_alloc (3),
+.BR llapi_layout_file_open (3),
+.BR llapi_layout (7),
+.BR liblustreapi (7)
diff --git a/lustre/doc/llapi_layout_ost_index_set.3 b/lustre/doc/llapi_layout_ost_index_set.3
new file mode 100644 (file)
index 0000000..513047c
--- /dev/null
@@ -0,0 +1 @@
+.so man3/llapi_layout_ost_index_get.3
diff --git a/lustre/doc/llapi_layout_pattern_get.3 b/lustre/doc/llapi_layout_pattern_get.3
new file mode 100644 (file)
index 0000000..e446aa7
--- /dev/null
@@ -0,0 +1,61 @@
+.TH llapi_layout_pattern_get 3 "2013 Oct 31" "Lustre User API"
+.SH NAME
+llapi_layout_pattern_get, llapi_layout_pattern_set \- get or set the
+RAID striping pattern of a Lustre file
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.PP
+.BI "int llapi_layout_pattern_get(const struct llapi_layout *" layout ", uint64_t *" pattern );
+.PP
+.BI "int llapi_layout_pattern_set(struct llapi_layout *" layout ", uint64_t " pattern );
+.fi
+.SH DESCRIPTION
+.PP
+.B llapi_layout_pattern_get()
+stores into
+.I pattern
+the RAID striping pattern used by layout
+.IR layout .
+.PP
+.B llapi_layout_pattern_set()
+sets the RAID striping pattern of
+.I layout
+to
+.IR pattern .
+Currently the only supported RAID pattern is RAID0.  If
+.I pattern
+is not a supported RAID pattern the return value will be -1 and errno will
+be set to
+.BR EOPNOTSUPP .
+.PP
+A
+.I pattern
+value of
+.B LLAPI_LAYOUT_DEFAULT
+means that the filesystem default RAID pattern will be used.
+.PP
+A
+.I pattern
+value of
+.B LLAPI_LAYOUT_RAID0
+means that the RAID0 pattern will be used.
+.SH RETURN VALUES
+.LP
+.B llapi_layout_pattern_get()
+and
+.B llapi_layout_pattern_set()
+return 0 on success, or -1 if an error occurred (in which case, errno is
+set appropriately).
+.SH ERRORS
+.TP 15
+.SM EINVAL
+An invalid argument was specified.
+.TP 15
+.SM EOPNOTSUPP
+An unsupported RAID pattern was specified.
+.SH "SEE ALSO"
+.BR llapi_layout_alloc (3),
+.BR llapi_layout_file_open (3),
+.BR llapi_layout (7),
+.BR liblustreapi (7)
diff --git a/lustre/doc/llapi_layout_pattern_set.3 b/lustre/doc/llapi_layout_pattern_set.3
new file mode 100644 (file)
index 0000000..d54a443
--- /dev/null
@@ -0,0 +1 @@
+.so man3/llapi_layout_pattern_get.3
diff --git a/lustre/doc/llapi_layout_pool_name_get.3 b/lustre/doc/llapi_layout_pool_name_get.3
new file mode 100644 (file)
index 0000000..a315d12
--- /dev/null
@@ -0,0 +1,74 @@
+.TH llapi_layout_pool_name_get 3 "2013 Oct 31" "Lustre User API"
+.SH NAME
+llapi_layout_pool_name_get, llapi_layout_pool_name_set \- get or set the
+OST pool name of a Lustre file
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.PP
+.BI "int llapi_layout_pool_name_get(const struct llapi_layout *" layout ",
+.BI "                               char *" pool_name ", size_t " n ");
+.sp
+.BI "int llapi_layout_pool_name_set(struct llapi_layout *" layout ",
+.BI "                               const char *" pool_name );
+.fi
+.SH DESCRIPTION
+.PP
+.B llapi_layout_pool_name_get()
+stores into
+.I pool_name
+up to
+.I n
+characters of the name of the pool of OSTS associated with
+.IR layout .
+.PP
+.B llapi_layout_pool_name_set()
+sets the OST pool name of
+.I  layout
+to
+.IR pool_name .
+If
+.I pool_name
+uses "fsname.pool" notation to qualify the pool name
+with a filesystem name, the "fsname." portion will be silently
+discarded before storing the value.
+.PP
+.B llapi_layout_pool_name_set()
+does not validate that
+.I pool_name
+names an existing non-empty pool, since it is not known a priori which
+filesystem
+.I layout
+will be applied to. However, the function
+.B llapi_layout_file_create()
+will fail if given a
+.I layout
+with a pool that does not exist or contains no OSTs.
+.SH RETURN VALUES
+.LP
+.B llapi_layout_pool_name_get()
+and
+.B llapi_layout_pool_name_set()
+return 0 on success, or -1 if an error occurred (in which case, errno is
+set appropriately).
+.SH ERRORS
+.TP 15
+.SM EINVAL
+An invalid argument was specified.
+.SH NOTES
+.PP
+A pool defines a set of OSTs from which objects may be allocated
+to store a file in a Lustre filesystem.
+Pools are created by the filesystem administrator using the
+.BR lctl (1)
+command.  This API allows an application to create a file within an
+existing pool, or to query the name of a pool that a file was created
+in. It does not provide an interface for creating or destroying pools.
+Refer to the Lustre Operations Manual for detailed background material
+about OST pools.
+.SH "SEE ALSO"
+.BR llapi_layout_alloc (3),
+.BR llapi_layout_file_open (3),
+.BR llapi_layout (7),
+.BR liblustreapi (7),
+.BR lctl (1)
diff --git a/lustre/doc/llapi_layout_pool_name_set.3 b/lustre/doc/llapi_layout_pool_name_set.3
new file mode 100644 (file)
index 0000000..b06a0f2
--- /dev/null
@@ -0,0 +1 @@
+.so man3/llapi_layout_pool_name_get.3
diff --git a/lustre/doc/llapi_layout_stripe_count_get.3 b/lustre/doc/llapi_layout_stripe_count_get.3
new file mode 100644 (file)
index 0000000..f22e663
--- /dev/null
@@ -0,0 +1,57 @@
+.TH llapi_layout_stripe_count_get 3 "2013 Oct 31" "Lustre User API"
+.SH NAME
+llapi_layout_stripe_countget, llapi_layout_stripe_count_set \- get or set the
+stripe size of a Lustre file
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.PP
+.BI "int llapi_layout_stripe_count_get(const struct llapi_layout *" layout ",
+.BI "                                  uint64_t *" stripe_count );
+.PP
+.BI "int llapi_layout_stripe_count_set(struct llapi_layout *" layout ",
+.BI "                                  uint64_t " stripe_count );
+.fi
+.SH DESCRIPTION
+.PP
+The stripe count of a Lustre file defines the number of OSTs used to
+store the file.
+.PP
+.B llapi_layout_stripe_count_get()
+stores into
+.I stripe_count
+the stripe count of
+.IR layout .
+.PP
+.B llapi_layout_stripe_count_get()
+sets the stripe count of
+.I layout
+to
+.IR stripe_count .
+.PP
+A
+.I stripe_count
+value of
+.B LLAPI_LAYOUT_DEFAULT
+means that the default stripe count will be used.
+.PP
+A
+.I stripe_count
+value of
+.B LLAPI_LAYOUT_WIDE
+means that the file will be striped as widely as possible.
+.SH RETURN VALUES
+.B llapi_layout_stripe_count_get()
+and
+.B llapi_layout_stripe_count_set()
+return 0 on success, or -1 if an error occurred (in which case, errno is
+set appropriately).
+.SH ERRORS
+.TP 15
+.SM EINVAL
+An invalid argument was specified.
+.SH "SEE ALSO"
+.BR llapi_layout_alloc (3),
+.BR llapi_layout_file_open (3),
+.BR llapi_layout (7),
+.BR liblustreapi (7)
diff --git a/lustre/doc/llapi_layout_stripe_count_set.3 b/lustre/doc/llapi_layout_stripe_count_set.3
new file mode 100644 (file)
index 0000000..52bf166
--- /dev/null
@@ -0,0 +1 @@
+.so man3/llapi_layout_stripe_count_get.3
diff --git a/lustre/doc/llapi_layout_stripe_size_get.3 b/lustre/doc/llapi_layout_stripe_size_get.3
new file mode 100644 (file)
index 0000000..6e634d0
--- /dev/null
@@ -0,0 +1,51 @@
+.TH llapi_layout_stripe_size_get 3 "2013 Oct 31" "Lustre User API"
+.SH NAME
+llapi_layout_stripe_size_get, llapi_layout_stripe_size_set \- get or set the
+stripe count of a Lustre file
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.PP
+.BI "int llapi_layout_stripe_size_get(const struct llapi_layout *" layout ",
+.BI "                                 uint64_t *" stripe_size );
+.PP
+.BI "int llapi_layout_stripe_size_set(struct llapi_layout *" layout ",
+.BI "                                 uint64_t " stripe_size );
+.fi
+.SH DESCRIPTION
+.PP
+The stripe size indicates how much data to write to one OST before
+moving to the next OST.
+.PP
+.B llapi_layout_stripe_size_get()
+stores into
+.I stripe_size
+the stripe size of
+.IR layout .
+.PP
+.B llapi_layout_stripe_size_get()
+sets the stripe size of
+.I layout
+to
+.IR stripe_size .
+.PP
+A
+.I stripe_size
+value of
+.B LLAPI_LAYOUT_DEFAULT
+means that the default stripe size will be used.
+.SH RETURN VALUES
+.B llapi_layout_stripe_size_get()
+and
+.B llapi_layout_stripe_size_set()
+return 0 on success, or -1 if an error occurred (in which case, errno is
+set appropriately).
+.SH ERRORS
+.TP 15
+.SM EINVAL
+An invalid argument was specified.
+.SH "SEE ALSO"
+.BR llapi_layout_alloc (3),
+.BR llapi_layout_file_open (3),
+.BR llapi_layout (7),
+.BR liblustreapi (7)
diff --git a/lustre/doc/llapi_layout_stripe_size_set.3 b/lustre/doc/llapi_layout_stripe_size_set.3
new file mode 100644 (file)
index 0000000..dccaf4a
--- /dev/null
@@ -0,0 +1 @@
+.so man3/llapi_layout_stripe_size_get.3
diff --git a/lustre/doc/llapi_path2fid.3 b/lustre/doc/llapi_path2fid.3
new file mode 100644 (file)
index 0000000..35cf9ab
--- /dev/null
@@ -0,0 +1,42 @@
+.TH llapi_path2fid 3 "2014 Mar 18" "Lustre User API"
+.SH NAME
+llapi_path2fid \- translate a path name to a Lustre FID
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.PP
+.BI "int llapi_path2fid(const char *"path ", lustre_fid *"fid );
+.fi
+.SH DESCRIPTION
+.PP
+.BR llapi_path2fid()
+stores the Lustre file identifier (FID) for the file or directory named
+by
+.I path
+into
+.IR fid .
+The
+.I fid
+may be then be passed to other llapi functions that expect the
+.B lustre_fid
+data type.
+.SH RETURN VALUES
+.LP
+.B llapi_path2fid()
+returns 0 on success or a negative errno value on failure.
+.SH ERRORS
+.TP 15
+.SM -ENOTTY
+.I path
+does not reside on a Lustre filesystem.
+.TP
+.SM -ENOENT
+.I path
+does not exist.
+.TP
+.SM -EINVAL
+An invalid argument was specified.
+.SH "SEE ALSO"
+.BR llapi_layout_get_by_fid (3),
+.BR llapi_layout (7),
+.BR liblustreapi (7)
index b3fc90a..17eb982 100644 (file)
@@ -8,6 +8,7 @@ The lustreapi library provides functions to access and/or modify settings specif
 .BR llapi_file_create (3),
 .BR llapi_file_open (3),
 .BR llapi_file_get_stripe (3),
+.BR llapi_layout (3),
 .BR llapi_quotactl (3)
 .BR llapi_hsm_state_get (3)
 .BR llapi_hsm_state_set (3)
index 56f6cfb..3507a37 100644 (file)
@@ -1699,7 +1699,6 @@ static inline void lmm_oi_cpu_to_le(struct ost_id *dst_oi,
 #define XATTR_USER_PREFIX       "user."
 #define XATTR_TRUSTED_PREFIX    "trusted."
 #define XATTR_SECURITY_PREFIX   "security."
-#define XATTR_LUSTRE_PREFIX     "lustre."
 
 #define XATTR_NAME_LOV          "trusted.lov"
 #define XATTR_NAME_LMA          "trusted.lma"
index 40dd522..5cccbf4 100644 (file)
@@ -43,6 +43,7 @@
  */
 
 #include <stdarg.h>
+#include <stdint.h>
 #include <lustre/lustre_user.h>
 
 typedef void (*llapi_cb_t)(char *obd_type_name, char *obd_name, char *obd_uuid,
@@ -80,6 +81,7 @@ static inline const char *llapi_msg_level2str(enum llapi_message_level level)
        return levels[level];
 }
 extern void llapi_msg_set_level(int level);
+int llapi_msg_get_level(void);
 extern llapi_log_callback_t llapi_error_callback_set(llapi_log_callback_t cb);
 extern llapi_log_callback_t llapi_info_callback_set(llapi_log_callback_t cb);
 
@@ -275,6 +277,8 @@ extern int llapi_get_mdt_index_by_fid(int fd, const lustre_fid *fid,
                                      int *mdt_index);
 extern int llapi_fd2fid(const int fd, lustre_fid *fid);
 extern int llapi_chomp_string(char *buf);
+extern int llapi_open_by_fid(const char *dir, const lustre_fid *fid,
+                            int open_flags);
 
 extern int llapi_get_version(char *buffer, int buffer_size, char **version);
 extern int llapi_get_data_version(int fd, __u64 *data_version, __u64 flags);
@@ -370,9 +374,272 @@ extern int llapi_json_add_item(struct llapi_json_item_list **item_list,
                               char *key, __u32 type, void *val);
 extern int llapi_json_write_list(struct llapi_json_item_list **item_list,
                                 FILE *fp);
-/** @} llapi */
 
-#endif
+/* llapi_layout user interface */
+
+/** Opaque data type abstracting the layout of a Lustre file. */
+struct llapi_layout;
+
+/*
+ * Flags to control how layouts are retrieved.
+ */
 
+/* Replace non-specified values with expected inherited values. */
+#define LAYOUT_GET_EXPECTED 0x1
 
+/**
+ * Return a pointer to a newly-allocated opaque data structure containing
+ * the layout for the file at \a path.  The pointer should be freed with
+ * llapi_layout_free() when it is no longer needed. Failure is indicated
+ * by a NULL return value and an appropriate error code stored in errno.
+ */
+struct llapi_layout *llapi_layout_get_by_path(const char *path, uint32_t flags);
+
+/**
+ * Return a pointer to a newly-allocated opaque data type containing the
+ * layout for the file referenced by open file descriptor \a fd.  The
+ * pointer should be freed with llapi_layout_free() when it is no longer
+ * needed. Failure is indicated by a NULL return value and an
+ * appropriate error code stored in errno.
+ */
+struct llapi_layout *llapi_layout_get_by_fd(int fd, uint32_t flags);
+
+/**
+ * Return a pointer to a newly-allocated opaque data type containing the
+ * layout for the file associated with Lustre file identifier string
+ * \a fidstr.  The string \a path must name a path within the
+ * filesystem that contains the file being looked up, such as the
+ * filesystem root.  The returned pointer should be freed with
+ * llapi_layout_free() when it is no longer needed.  Failure is
+ * indicated with a NULL return value and an appropriate error code
+ * stored in errno.
+ */
+struct llapi_layout *llapi_layout_get_by_fid(const char *path,
+                                            const lustre_fid *fid,
+                                            uint32_t flags);
+
+/**
+ * Allocate a new layout. Use this when creating a new file with
+ * llapi_layout_file_create().
+ */
+struct llapi_layout *llapi_layout_alloc(void);
 
+/**
+ * Free memory allocated for \a layout.
+ */
+void llapi_layout_free(struct llapi_layout *layout);
+
+/** Not a valid stripe size, offset, or RAID pattern. */
+#define LLAPI_LAYOUT_INVALID   0x1000000000000001ULL
+
+/**
+ * When specified or returned as the value for stripe count,
+ * stripe size, offset, or RAID pattern, the filesystem-wide
+ * default behavior will apply.
+ */
+#define LLAPI_LAYOUT_DEFAULT   (LLAPI_LAYOUT_INVALID + 1)
+
+/**
+ * When specified or returned as the value for stripe count, all
+ * available OSTs will be used.
+ */
+#define LLAPI_LAYOUT_WIDE      (LLAPI_LAYOUT_INVALID + 2)
+
+/**
+ * When specified as the value for layout pattern, file objects will be
+ * stored using RAID0.  That is, data will be split evenly and without
+ * redundancy across all OSTs in the layout.
+ */
+#define LLAPI_LAYOUT_RAID0     0
+
+/**
+ * Flags to modify how layouts are retrieved.
+ */
+/******************** Stripe Count ********************/
+
+/**
+ * Store the stripe count of \a layout in \a count.
+ *
+ * \retval  0 Success
+ * \retval -1 Error with status code in errno.
+ */
+int llapi_layout_stripe_count_get(const struct llapi_layout *layout,
+                                 uint64_t *count);
+
+/**
+ * Set the stripe count of \a layout to \a count.
+ *
+ * \retval  0 Success.
+ * \retval -1 Invalid argument, errno set to EINVAL.
+ */
+int llapi_layout_stripe_count_set(struct llapi_layout *layout, uint64_t count);
+
+/******************** Stripe Size ********************/
+
+/**
+ * Store the stripe size of \a layout in \a size.
+ *
+ * \retval  0 Success.
+ * \retval -1 Invalid argument, errno set to EINVAL.
+ */
+int llapi_layout_stripe_size_get(const struct llapi_layout *layout,
+                                uint64_t *size);
+
+/**
+ * Set the stripe size of \a layout to \a stripe_size.
+ *
+ * \retval  0 Success.
+ * \retval -1 Invalid argument, errno set to EINVAL.
+ */
+int llapi_layout_stripe_size_set(struct llapi_layout *layout, uint64_t size);
+
+/******************** Stripe Pattern ********************/
+
+/**
+ * Store the stripe pattern of \a layout in \a pattern.
+ *
+ * \retval 0  Success.
+ * \retval -1 Error with status code in errno.
+ */
+int llapi_layout_pattern_get(const struct llapi_layout *layout,
+                            uint64_t *pattern);
+
+/**
+ * Set the stripe pattern of \a layout to \a pattern.
+ *
+ * \retval  0 Success.
+ * \retval -1 Invalid argument, errno set to EINVAL.
+ */
+int llapi_layout_pattern_set(struct llapi_layout *layout, uint64_t pattern);
+
+/******************** OST Index ********************/
+
+/**
+ * Store the index of the OST where stripe number \a stripe_number is stored
+ * in \a index.
+ *
+ * An error return value will result from a NULL layout, if \a
+ * stripe_number is out of range, or if \a layout was not initialized
+ * with llapi_layout_lookup_by{path,fd,fid}().
+ *
+ * \retval  0 Success
+ * \retval -1 Invalid argument, errno set to EINVAL.
+ */
+int llapi_layout_ost_index_get(const struct llapi_layout *layout,
+                              uint64_t stripe_number, uint64_t *index);
+
+/**
+ * Set the OST index associated with stripe number \a stripe_number to
+ * \a ost_index.
+ * NB: This is currently supported only for \a stripe_number = 0 and
+ * other usage will return ENOTSUPP in errno.  A NULL \a layout or
+ * out-of-range \a stripe_number will return EINVAL in errno.
+ *
+ * \retval  0 Success.
+ * \retval -1 Error with errno set to non-zero value.
+ */
+int llapi_layout_ost_index_set(struct llapi_layout *layout, int stripe_number,
+                              uint64_t index);
+
+/******************** Pool Name ********************/
+
+/**
+ * Store up to \a pool_name_len characters of the name of the pool of
+ * OSTs associated with \a layout into the buffer pointed to by
+ * \a pool_name.
+ *
+ * The correct calling form is:
+ *
+ *   llapi_layout_pool_name_get(layout, pool_name, sizeof(pool_name));
+ *
+ * A pool defines a set of OSTs from which file objects may be
+ * allocated for a file using \a layout.
+ *
+ * On success, the number of bytes stored is returned, excluding the
+ * terminating '\0' character (zero indicates that \a layout does not
+ * have an associated OST pool).  On error, -1 is returned and errno is
+ * set appropriately. Possible sources of error include a NULL pointer
+ * argument or insufficient space in \a dest to store the pool name,
+ * in which cases errno will be set to EINVAL.
+ *
+ * \retval 0+          The number of bytes stored in \a dest.
+ * \retval -1          Invalid argument, errno set to EINVAL.
+ */
+int llapi_layout_pool_name_get(const struct llapi_layout *layout,
+                             char *pool_name, size_t pool_name_len);
+
+/**
+ * Set the name of the pool of OSTs from which file objects will be
+ * allocated to \a pool_name.
+ *
+ * If the pool name uses "fsname.pool" notation to qualify the pool name
+ * with a filesystem name, the "fsname." portion will be silently
+ * discarded before storing the value. No validation that \a pool_name
+ * is an existing non-empty pool in filesystem \a fsname will be
+ * performed.  Such validation can be performed by the application if
+ * desired using the llapi_search_ost() function.  The maximum length of
+ * the stored value is defined by the constant LOV_MAXPOOLNAME.
+ *
+ * \retval  0  Success.
+ * \retval -1  Invalid argument, errno set to EINVAL.
+ */
+int llapi_layout_pool_name_set(struct llapi_layout *layout,
+                             const char *pool_name);
+
+/******************** File Creation ********************/
+
+/**
+ * Open an existing file at \a path, or create it with the specified
+ * \a layout and \a mode.
+ *
+ * One access mode and zero or more file creation flags and file status
+ * flags May be bitwise-or'd in \a open_flags (see open(2)).  Return an
+ * open file descriptor for the file.  If \a layout is non-NULL and
+ * \a path is not on a Lustre filesystem this function will fail and set
+ * errno to ENOTTY.
+ *
+ * An already existing file may be opened with this function, but
+ * \a layout and \a mode will not be applied to it.  Callers requiring a
+ * guarantee that the opened file is created with the specified
+ * \a layout and \a mode should use llapi_layout_file_create().
+ *
+ * A NULL \a layout may be specified, in which case the standard Lustre
+ * behavior for assigning layouts to newly-created files will apply.
+ *
+ * \retval 0+ An open file descriptor.
+ * \retval -1 Error with status code in errno.
+ */
+int llapi_layout_file_open(const char *path, int open_flags, mode_t mode,
+                          const struct llapi_layout *layout);
+
+/**
+ * Create a new file at \a path with the specified \a layout and \a mode.
+ *
+ * One access mode and zero or more file creation flags and file status
+ * flags May be bitwise-or'd in \a open_flags (see open(2)).  Return an
+ * open file descriptor for the file.  If \a layout is non-NULL and
+ * \a path is not on a Lustre filesystem this function will fail and set
+ * errno to ENOTTY.
+ *
+ * The function call
+ *
+ *   llapi_layout_file_create(path, open_flags, mode, layout)
+ *
+ * shall be equivalent to:
+ *
+ *   llapi_layout_file_open(path, open_flags|O_CREAT|O_EXCL, mode, layout)
+ *
+ * It is an error if \a path specifies an existing file.
+ *
+ * A NULL \a layout may be specified, in which the standard Lustre
+ * behavior for assigning layouts to newly-created files will apply.
+ *
+ * \retval 0+ An open file descriptor.
+ * \retval -1 Error with status code in errno.
+ */
+int llapi_layout_file_create(const char *path, int open_flags, int mode,
+                            const struct llapi_layout *layout);
+
+/** @} llapi */
+
+#endif
index bccb334..795dade 100644 (file)
@@ -10,6 +10,7 @@
 /cmknod
 /copy_attr
 /copytool
+/llapi_layout_test
 /createdestroy
 /createmany
 /createtest
index 7f6c3a4..e75b2b6 100644 (file)
@@ -74,6 +74,7 @@ noinst_PROGRAMS += openfilleddirunlink rename_many memhog
 noinst_PROGRAMS += mmap_sanity writemany reads flocks_test flock_deadlock
 noinst_PROGRAMS += write_time_limit rwv lgetxattr_size_check checkfiemap
 noinst_PROGRAMS += listxattr_size_check check_fhandle_syscalls badarea_io
+noinst_PROGRAMS += llapi_layout_test
 
 bin_PROGRAMS = mcreate munlink
 testdir = $(libdir)/lustre/tests
@@ -87,6 +88,7 @@ mmap_sanity_SOURCES= mmap_sanity.c
 
 LIBLUSTREAPI = $(top_builddir)/lustre/utils/liblustreapi.a
 multiop_LDADD=$(LIBLUSTREAPI) $(PTHREAD_LIBS) $(LIBCFS)
+llapi_layout_test_LDADD=$(LIBLUSTREAPI)
 it_test_LDADD=$(LIBCFS)
 rwv_LDADD=$(LIBCFS)
 
@@ -95,4 +97,3 @@ ll_dirstripe_verify_LDADD= -L$(top_builddir)/lustre/utils $(PTHREAD_LIBS) -llust
 
 flocks_test_SOURCES=flocks_test.c
 flocks_test_LDADD=-lpthread
-
diff --git a/lustre/tests/llapi_layout_test.c b/lustre/tests/llapi_layout_test.c
new file mode 100644 (file)
index 0000000..98a812a
--- /dev/null
@@ -0,0 +1,1353 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+/*
+ * These tests exercise the llapi_layout API which abstracts the layout
+ * of a Lustre file behind an opaque data type.  They assume a Lustre
+ * file system with at least 2 OSTs and a pool containing at least the
+ * first 2 OSTs.  For example,
+ *
+ *  sudo lctl pool_new lustre.testpool
+ *  sudo lctl pool_add lustre.testpool OST[0-1]
+ *  gcc -Wall -g -Werror -o llapi_layout_test llapi_layout_test.c -llustreapi
+ *  sudo ./llapi_layout_test
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <lustre/lustreapi.h>
+#include <pwd.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <inttypes.h>
+
+#define ERROR(fmt, ...)                                                        \
+       fprintf(stderr, "%s: %s:%d: %s: " fmt "\n",                     \
+               program_invocation_short_name, __FILE__, __LINE__,      \
+               __func__, ## __VA_ARGS__);
+
+#define DIE(fmt, ...)                  \
+do {                                   \
+       ERROR(fmt, ## __VA_ARGS__);     \
+       exit(EXIT_FAILURE);             \
+} while (0)
+
+#define ASSERTF(cond, fmt, ...)                                                \
+do {                                                                   \
+       if (!(cond))                                                    \
+               DIE("assertion '%s' failed: "fmt, #cond, ## __VA_ARGS__);\
+} while (0)                                                            \
+
+static char *lustre_dir;
+static char *poolname;
+static int num_osts = -1;
+
+void usage(char *prog)
+{
+       printf("Usage: %s [-d lustre_dir] [-p pool_name] [-o num_osts]\n",
+              prog);
+       exit(0);
+}
+
+#define T0FILE                 "t0"
+#define T0_STRIPE_COUNT                num_osts
+#define T0_STRIPE_SIZE         1048576
+#define T0_OST_OFFSET          (num_osts - 1)
+#define T0_DESC                "Read/write layout attributes then create a file"
+void test0(void)
+{
+       int rc;
+       int fd;
+       uint64_t count;
+       uint64_t size;
+       struct llapi_layout *layout = llapi_layout_alloc();
+       char path[PATH_MAX];
+       char mypool[LOV_MAXPOOLNAME + 1] = { '\0' };
+
+       ASSERTF(layout != NULL, "errno %d", errno);
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T0FILE);
+
+       rc = unlink(path);
+       ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
+
+       /* stripe count */
+       rc = llapi_layout_stripe_count_set(layout, T0_STRIPE_COUNT);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = llapi_layout_stripe_count_get(layout, &count);
+       ASSERTF(rc == 0 && count == T0_STRIPE_COUNT, "%"PRIu64" != %d", count,
+               T0_STRIPE_COUNT);
+
+       /* stripe size */
+       rc = llapi_layout_stripe_size_set(layout, T0_STRIPE_SIZE);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = llapi_layout_stripe_size_get(layout, &size);
+       ASSERTF(rc == 0 && size == T0_STRIPE_SIZE, "%"PRIu64" != %d", size,
+               T0_STRIPE_SIZE);
+
+       /* pool_name */
+       rc = llapi_layout_pool_name_set(layout, poolname);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = llapi_layout_pool_name_get(layout, mypool, sizeof(mypool));
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = strcmp(mypool, poolname);
+       ASSERTF(rc == 0, "%s != %s", mypool, poolname);
+
+       /* ost_index */
+       rc = llapi_layout_ost_index_set(layout, 0, T0_OST_OFFSET);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       /* create */
+       fd = llapi_layout_file_create(path, 0, 0660, layout);
+       ASSERTF(fd >= 0, "path = %s, errno = %d", path, errno);
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       llapi_layout_free(layout);
+}
+
+void __test1_helper(struct llapi_layout *layout)
+{
+       uint64_t ost0;
+       uint64_t ost1;
+       uint64_t size;
+       uint64_t count;
+       int rc;
+       char mypool[LOV_MAXPOOLNAME + 1] = { '\0' };
+
+       rc = llapi_layout_stripe_count_get(layout, &count);
+       ASSERTF(count == T0_STRIPE_COUNT, "%"PRIu64" != %d", count,
+               T0_STRIPE_COUNT);
+
+       rc = llapi_layout_stripe_size_get(layout, &size);
+       ASSERTF(size == T0_STRIPE_SIZE, "%"PRIu64" != %d", size,
+               T0_STRIPE_SIZE);
+
+       rc = llapi_layout_pool_name_get(layout, mypool, sizeof(mypool));
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = strcmp(mypool, poolname);
+       ASSERTF(rc == 0, "%s != %s", mypool, poolname);
+
+       rc = llapi_layout_ost_index_get(layout, 0, &ost0);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = llapi_layout_ost_index_get(layout, 1, &ost1);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       ASSERTF(ost0 == T0_OST_OFFSET, "%"PRIu64" != %d", ost0, T0_OST_OFFSET);
+       ASSERTF(ost1 != ost0, "%"PRIu64" == %"PRIu64, ost0, ost1);
+}
+
+#define T1_DESC                "Read test0 file by path and verify attributes"
+void test1(void)
+{
+       char path[PATH_MAX];
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T0FILE);
+       struct llapi_layout *layout = llapi_layout_get_by_path(path, 0);
+       ASSERTF(layout != NULL, "errno = %d", errno);
+       __test1_helper(layout);
+       llapi_layout_free(layout);
+}
+
+
+#define T2_DESC                "Read test0 file by FD and verify attributes"
+void test2(void)
+{
+       int fd;
+       int rc;
+       char path[PATH_MAX];
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T0FILE);
+
+       fd = open(path, O_RDONLY);
+       ASSERTF(fd >= 0, "open(%s): errno = %d", path, errno);
+
+       struct llapi_layout *layout = llapi_layout_get_by_fd(fd, 0);
+       ASSERTF(layout != NULL, "errno = %d", errno);
+
+       rc = close(fd);
+       ASSERTF(rc == 0, "close(%s): errno = %d", path, errno);
+
+       __test1_helper(layout);
+       llapi_layout_free(layout);
+}
+
+#define T3_DESC                "Read test0 file by FID and verify attributes"
+void test3(void)
+{
+       int rc;
+       struct llapi_layout *layout;
+       lustre_fid fid;
+       char fidstr[4096];
+       char path[PATH_MAX];
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T0FILE);
+
+       rc = llapi_path2fid(path, &fid);
+       ASSERTF(rc == 0, "rc = %d, errno = %d", rc, errno);
+       snprintf(fidstr, sizeof(fidstr), "0x%"PRIx64":0x%x:0x%x",
+                (uint64_t)fid.f_seq, fid.f_oid, fid.f_ver);
+       errno = 0;
+       layout = llapi_layout_get_by_fid(path, &fid, 0);
+       ASSERTF(layout != NULL, "fidstr = %s, errno = %d", fidstr, errno);
+
+       __test1_helper(layout);
+       llapi_layout_free(layout);
+}
+
+
+#define T4FILE                 "t4"
+#define T4_STRIPE_COUNT                2
+#define T4_STRIPE_SIZE         2097152
+#define T4_DESC                "Verify compatibility with 'lfs setstripe'"
+void test4(void)
+{
+       int rc;
+       uint64_t ost0;
+       uint64_t ost1;
+       uint64_t count;
+       uint64_t size;
+       const char *lfs = getenv("LFS");
+       char mypool[LOV_MAXPOOLNAME + 1] = { '\0' };
+       char cmd[4096];
+       char path[PATH_MAX];
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T4FILE);
+
+       if (lfs == NULL)
+               lfs = "/usr/bin/lfs";
+
+       rc = unlink(path);
+       ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);
+
+       snprintf(cmd, sizeof(cmd), "%s setstripe %s %s -c %d -s %d %s", lfs,
+                strlen(poolname) > 0 ? "-p" : "", poolname, T4_STRIPE_COUNT,
+                T4_STRIPE_SIZE, path);
+       rc = system(cmd);
+       ASSERTF(rc == 0, "system(%s): exit status %d", cmd, WEXITSTATUS(rc));
+
+       errno = 0;
+       struct llapi_layout *layout = llapi_layout_get_by_path(path, 0);
+       ASSERTF(layout != NULL, "errno = %d", errno);
+
+       rc = llapi_layout_stripe_count_get(layout, &count);
+       ASSERTF(count == T4_STRIPE_COUNT, "%"PRIu64" != %d", count,
+               T4_STRIPE_COUNT);
+
+       rc = llapi_layout_stripe_size_get(layout, &size);
+       ASSERTF(size == T4_STRIPE_SIZE, "%"PRIu64" != %d", size,
+               T4_STRIPE_SIZE);
+
+       rc = llapi_layout_pool_name_get(layout, mypool, sizeof(mypool));
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = strcmp(mypool, poolname);
+       ASSERTF(rc == 0, "%s != %s", mypool, poolname);
+
+       rc = llapi_layout_ost_index_get(layout, 0, &ost0);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = llapi_layout_ost_index_get(layout, 1, &ost1);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       ASSERTF(ost1 != ost0, "%"PRIu64" == %"PRIu64, ost0, ost1);
+
+       llapi_layout_free(layout);
+}
+
+#define T5FILE         "t5"
+#define T5_DESC                "llapi_layout_get_by_path ENOENT handling"
+void test5(void)
+{
+       int rc;
+       char path[PATH_MAX];
+       struct llapi_layout *layout;
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T5FILE);
+
+       rc = unlink(path);
+       ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);
+
+       errno = 0;
+       layout = llapi_layout_get_by_path(path, 0);
+       ASSERTF(layout == NULL && errno == ENOENT, "errno = %d", errno);
+}
+
+#define T6_DESC                "llapi_layout_get_by_fd EBADF handling"
+void test6(void)
+{
+       errno = 0;
+       struct llapi_layout *layout = llapi_layout_get_by_fd(9999, 0);
+       ASSERTF(layout == NULL && errno == EBADF, "errno = %d", errno);
+}
+
+
+#define T7FILE         "t7"
+#define T7_DESC                "llapi_layout_get_by_path EACCES handling"
+void test7(void)
+{
+       int fd;
+       int rc;
+       uid_t myuid = getuid();
+       char path[PATH_MAX];
+       const char *runas = getenv("RUNAS_ID");
+       struct passwd *pw;
+       uid_t uid;
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T7FILE);
+       ASSERTF(myuid == 0, "myuid = %d", myuid); /* Need root for this test. */
+
+       /* Create file as root */
+       rc = unlink(path);
+       ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);
+
+       fd = open(path, O_CREAT, 0400);
+       ASSERTF(fd > 0, "errno = %d", errno);
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       /* Become unprivileged user */
+       if (runas != NULL) {
+               uid = atoi(runas);
+               ASSERTF(uid != 0, "runas = %s", runas);
+       } else {
+               pw = getpwnam("nobody");
+               ASSERTF(pw != NULL, "errno = %d", errno);
+               uid = pw->pw_uid;
+       }
+       rc = seteuid(uid);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       errno = 0;
+       struct llapi_layout *layout = llapi_layout_get_by_path(path, 0);
+       ASSERTF(layout == NULL && errno == EACCES, "errno = %d", errno);
+       rc = seteuid(myuid);
+       ASSERTF(rc == 0, "errno = %d", errno);
+}
+
+
+/* llapi_layout_get_by_path() returns default layout for file with no
+ * striping attributes. */
+#define T8FILE         "t8"
+#define T8_DESC                "llapi_layout_get_by_path ENODATA handling"
+void test8(void)
+{
+       int fd;
+       int rc;
+       struct llapi_layout *layout;
+       uint64_t count;
+       uint64_t size;
+       uint64_t pattern;
+       char path[PATH_MAX];
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T8FILE);
+
+       rc = unlink(path);
+       ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
+       fd = open(path, O_CREAT, 0640);
+       ASSERTF(fd >= 0, "errno = %d", errno);
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       layout = llapi_layout_get_by_path(path, 0);
+       ASSERTF(layout != NULL, "errno = %d\n", errno);
+
+       rc = llapi_layout_stripe_count_get(layout, &count);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(count == LLAPI_LAYOUT_DEFAULT, "count = %"PRIu64"\n", count);
+
+       rc = llapi_layout_stripe_size_get(layout, &size);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(size == LLAPI_LAYOUT_DEFAULT, "size = %"PRIu64"\n", size);
+
+       rc = llapi_layout_pattern_get(layout, &pattern);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(pattern == LLAPI_LAYOUT_DEFAULT, "pattern = %"PRIu64"\n",
+               pattern);
+
+       llapi_layout_free(layout);
+}
+
+/* Setting pattern > 0 returns EOPNOTSUPP in errno. */
+#define T9_DESC                "llapi_layout_pattern_set() EOPNOTSUPP handling"
+void test9(void)
+{
+       struct llapi_layout *layout;
+       int rc;
+
+       layout = llapi_layout_alloc();
+       ASSERTF(layout != NULL, "errno = %d\n", errno);
+       errno = 0;
+       rc = llapi_layout_pattern_set(layout, 1);
+       ASSERTF(rc == -1 && errno == EOPNOTSUPP, "rc = %d, errno = %d", rc,
+               errno);
+       llapi_layout_free(layout);
+}
+
+
+/* Verify stripe_count interfaces return errors as expected */
+#define T10_DESC       "stripe_count error handling"
+void test10(void)
+{
+       int rc;
+       uint64_t count;
+       struct llapi_layout *layout;
+
+       layout = llapi_layout_alloc();
+       ASSERTF(layout != NULL, "errno = %d", errno);
+
+       /* invalid stripe count */
+       errno = 0;
+       rc = llapi_layout_stripe_count_set(layout, LLAPI_LAYOUT_INVALID);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       errno = 0;
+       rc = llapi_layout_stripe_count_set(layout, -1);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* NULL layout */
+       errno = 0;
+       rc = llapi_layout_stripe_count_set(NULL, 2);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* NULL layout */
+       errno = 0;
+       rc = llapi_layout_stripe_count_get(NULL, &count);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* NULL count */
+       errno = 0;
+       rc = llapi_layout_stripe_count_get(layout, NULL);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* stripe count too large */
+       errno = 0;
+       rc = llapi_layout_stripe_count_set(layout, LOV_MAX_STRIPE_COUNT + 1);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+       llapi_layout_free(layout);
+}
+
+/* Verify stripe_size interfaces return errors as expected */
+#define T11_DESC       "stripe_size error handling"
+void test11(void)
+{
+       int rc;
+       uint64_t size;
+       struct llapi_layout *layout;
+
+       layout = llapi_layout_alloc();
+       ASSERTF(layout != NULL, "errno = %d", errno);
+
+       /* negative stripe size */
+       errno = 0;
+       rc = llapi_layout_stripe_size_set(layout, -1);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* invalid stripe size */
+       errno = 0;
+       rc = llapi_layout_stripe_size_set(layout, LLAPI_LAYOUT_INVALID);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* stripe size too big */
+       errno = 0;
+       rc = llapi_layout_stripe_size_set(layout, (1ULL << 33));
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* NULL layout */
+       errno = 0;
+       rc = llapi_layout_stripe_size_set(NULL, 1048576);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       errno = 0;
+       rc = llapi_layout_stripe_size_get(NULL, &size);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* NULL size */
+       errno = 0;
+       rc = llapi_layout_stripe_size_get(layout, NULL);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       llapi_layout_free(layout);
+}
+
+/* Verify pool_name interfaces return errors as expected */
+#define T12_DESC       "pool_name error handling"
+void test12(void)
+{
+       int rc;
+       struct llapi_layout *layout;
+       char mypool[LOV_MAXPOOLNAME + 1] = { '\0' };
+
+       layout = llapi_layout_alloc();
+       ASSERTF(layout != NULL, "errno = %d", errno);
+
+       /* NULL layout */
+       errno = 0;
+       rc = llapi_layout_pool_name_set(NULL, "foo");
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* NULL pool name */
+       errno = 0;
+       rc = llapi_layout_pool_name_set(layout, NULL);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* NULL layout */
+       errno = 0;
+       rc = llapi_layout_pool_name_get(NULL, mypool, sizeof(mypool));
+       ASSERTF(errno == EINVAL, "poolname = %s, errno = %d", poolname, errno);
+
+       /* NULL buffer */
+       errno = 0;
+       rc = llapi_layout_pool_name_get(layout, NULL, sizeof(mypool));
+       ASSERTF(errno == EINVAL, "poolname = %s, errno = %d", poolname, errno);
+
+       /* Pool name too long*/
+       errno = 0;
+       rc = llapi_layout_pool_name_set(layout, "0123456789abcdef0");
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       llapi_layout_free(layout);
+}
+
+/* Verify ost_index interface returns errors as expected */
+#define T13FILE                        "t13"
+#define T13_STRIPE_COUNT       2
+#define T13_DESC               "ost_index error handling"
+void test13(void)
+{
+       int rc;
+       int fd;
+       uint64_t idx;
+       struct llapi_layout *layout;
+       char path[PATH_MAX];
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T13FILE);
+
+       layout = llapi_layout_alloc();
+       ASSERTF(layout != NULL, "errno = %d", errno);
+
+       /* Only setting OST index for stripe 0 is supported for now. */
+       errno = 0;
+       rc = llapi_layout_ost_index_set(layout, 1, 1);
+       ASSERTF(rc == -1 && errno == EOPNOTSUPP, "rc = %d, errno = %d",
+               rc, errno);
+
+       /* invalid OST index */
+       errno = 0;
+       rc = llapi_layout_ost_index_set(layout, 0, LLAPI_LAYOUT_INVALID);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       errno = 0;
+       rc = llapi_layout_ost_index_set(layout, 0, -1);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* NULL layout */
+       errno = 0;
+       rc = llapi_layout_ost_index_set(NULL, 0, 1);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       errno = 0;
+       rc = llapi_layout_ost_index_get(NULL, 0, &idx);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* NULL index */
+       errno = 0;
+       rc = llapi_layout_ost_index_get(layout, 0, NULL);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* Layout not read from file so has no OST data. */
+       errno = 0;
+       rc = llapi_layout_stripe_count_set(layout, T13_STRIPE_COUNT);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = llapi_layout_ost_index_get(layout, 0, &idx);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       /* n greater than stripe count*/
+       rc = unlink(path);
+       ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
+       rc = llapi_layout_stripe_count_set(layout, T13_STRIPE_COUNT);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       fd = llapi_layout_file_create(path, 0, 0644, layout);
+       ASSERTF(fd >= 0, "errno = %d", errno);
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       llapi_layout_free(layout);
+
+       layout = llapi_layout_get_by_path(path, 0);
+       ASSERTF(layout != NULL, "errno = %d", errno);
+       errno = 0;
+       rc = llapi_layout_ost_index_get(layout, T13_STRIPE_COUNT + 1, &idx);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       llapi_layout_free(layout);
+}
+
+/* Verify llapi_layout_file_create() returns errors as expected */
+#define T14_DESC       "llapi_layout_file_create error handling"
+void test14(void)
+{
+       int rc;
+       struct llapi_layout *layout = llapi_layout_alloc();
+
+       /* NULL path */
+       errno = 0;
+       rc = llapi_layout_file_create(NULL, 0, 0, layout);
+       ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
+
+       llapi_layout_free(layout);
+}
+
+/* Can't change striping attributes of existing file. */
+#define T15FILE                        "t15"
+#define T15_STRIPE_COUNT       2
+#define T15_DESC       "Can't change striping attributes of existing file"
+void test15(void)
+{
+       int rc;
+       int fd;
+       uint64_t count;
+       struct llapi_layout *layout;
+       char path[PATH_MAX];
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T15FILE);
+
+       rc = unlink(path);
+       ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
+
+       layout = llapi_layout_alloc();
+       ASSERTF(layout != NULL, "errno = %d", errno);
+       rc = llapi_layout_stripe_count_set(layout, T15_STRIPE_COUNT);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       errno = 0;
+       fd = llapi_layout_file_create(path, 0, 0640, layout);
+       ASSERTF(fd >= 0, "fd = %d, errno = %d", fd, errno);
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       rc = llapi_layout_stripe_count_set(layout, T15_STRIPE_COUNT - 1);
+       errno = 0;
+       fd = llapi_layout_file_open(path, 0, 0640, layout);
+       ASSERTF(fd >= 0, "fd = %d, errno = %d", fd, errno);
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       llapi_layout_free(layout);
+
+       layout = llapi_layout_get_by_path(path, 0);
+       ASSERTF(layout != NULL, "errno = %d", errno);
+       rc = llapi_layout_stripe_count_get(layout, &count);
+       ASSERTF(rc == 0 && count == T15_STRIPE_COUNT,
+               "rc = %d, %"PRIu64" != %d", rc, count, T15_STRIPE_COUNT);
+       llapi_layout_free(layout);
+}
+
+/* Default stripe attributes are applied as expected. */
+#define T16FILE                "t16"
+#define T16_DESC       "Default stripe attributes are applied as expected"
+void test16(void)
+{
+       int             rc;
+       int             fd;
+       struct llapi_layout     *deflayout;
+       struct llapi_layout     *filelayout;
+       char            path[PATH_MAX];
+       uint64_t        fsize;
+       uint64_t        fcount;
+       uint64_t        dsize;
+       uint64_t        dcount;
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T16FILE);
+
+       rc = unlink(path);
+       ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);
+
+       deflayout = llapi_layout_get_by_path(lustre_dir, LAYOUT_GET_EXPECTED);
+       ASSERTF(deflayout != NULL, "errno = %d", errno);
+       rc = llapi_layout_stripe_size_get(deflayout, &dsize);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = llapi_layout_stripe_count_get(deflayout, &dcount);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       /* First, with a default struct llapi_layout */
+       filelayout = llapi_layout_alloc();
+       ASSERTF(filelayout != NULL, "errno = %d", errno);
+
+       fd = llapi_layout_file_create(path, 0, 0640, filelayout);
+       ASSERTF(fd >= 0, "errno = %d", errno);
+
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       llapi_layout_free(filelayout);
+
+       filelayout = llapi_layout_get_by_path(path, 0);
+       ASSERTF(filelayout != NULL, "errno = %d", errno);
+
+       rc = llapi_layout_stripe_count_get(filelayout, &fcount);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       ASSERTF(fcount == dcount, "%"PRIu64" != %"PRIu64, fcount, dcount);
+
+       rc = llapi_layout_stripe_size_get(filelayout, &fsize);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       ASSERTF(fsize == dsize, "%"PRIu64" != %"PRIu64, fsize, dsize);
+
+       /* NULL layout also implies default layout */
+       rc = unlink(path);
+       ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);
+
+       fd = llapi_layout_file_create(path, 0, 0640, filelayout);
+       ASSERTF(fd >= 0, "errno = %d", errno);
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       filelayout = llapi_layout_get_by_path(path, 0);
+       ASSERTF(filelayout != NULL, "errno = %d", errno);
+
+       rc = llapi_layout_stripe_count_get(filelayout, &fcount);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = llapi_layout_stripe_size_get(filelayout, &fsize);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       ASSERTF(fcount == dcount, "%"PRIu64" != %"PRIu64, fcount, dcount);
+       ASSERTF(fsize == dsize, "%"PRIu64" != %"PRIu64, fsize, dsize);
+
+       llapi_layout_free(filelayout);
+       llapi_layout_free(deflayout);
+}
+
+/* Setting stripe count to LLAPI_LAYOUT_WIDE uses all available OSTs. */
+#define T17FILE                "t17"
+#define T17_DESC       "LLAPI_LAYOUT_WIDE is honored"
+void test17(void)
+{
+       int rc;
+       int fd;
+       int osts_all;
+       uint64_t osts_layout;
+       struct llapi_layout *layout;
+       char path[PATH_MAX];
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T17FILE);
+
+       rc = unlink(path);
+       ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);
+       layout = llapi_layout_alloc();
+       ASSERTF(layout != NULL, "errno = %d", errno);
+       rc = llapi_layout_stripe_count_set(layout, LLAPI_LAYOUT_WIDE);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       fd = llapi_layout_file_create(path, 0, 0640, layout);
+       ASSERTF(fd >= 0, "errno = %d", errno);
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       llapi_layout_free(layout);
+
+       /* Get number of available OSTs */
+       fd = open(path, O_RDONLY);
+       ASSERTF(fd >= 0, "errno = %d", errno);
+       rc = llapi_lov_get_uuids(fd, NULL, &osts_all);
+       ASSERTF(rc == 0, "rc = %d, errno = %d", rc, errno);
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       layout = llapi_layout_get_by_path(path, 0);
+       ASSERTF(layout != NULL, "errno = %d", errno);
+       rc = llapi_layout_stripe_count_get(layout, &osts_layout);
+       ASSERTF(osts_layout == osts_all, "%"PRIu64" != %d", osts_layout,
+               osts_all);
+
+       llapi_layout_free(layout);
+}
+
+/* Setting pool with "fsname.pool" notation. */
+#define T18FILE                "t18"
+#define T18_DESC       "Setting pool with fsname.pool notation"
+void test18(void)
+{
+       int rc;
+       int fd;
+       struct llapi_layout *layout = llapi_layout_alloc();
+       char path[PATH_MAX];
+       char pool[LOV_MAXPOOLNAME*2 + 1];
+       char mypool[LOV_MAXPOOLNAME + 1] = { '\0' };
+
+       snprintf(pool, sizeof(pool), "lustre.%s", poolname);
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T18FILE);
+
+       ASSERTF(layout != NULL, "errno = %d", errno);
+
+       rc = unlink(path);
+       ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);
+
+       rc = llapi_layout_pool_name_set(layout, pool);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       rc = llapi_layout_pool_name_get(layout, mypool, sizeof(mypool));
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = strcmp(mypool, poolname);
+       ASSERTF(rc == 0, "%s != %s", mypool, poolname);
+       fd = llapi_layout_file_create(path, 0, 0640, layout);
+       ASSERTF(fd >= 0, "errno = %d", errno);
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       llapi_layout_free(layout);
+
+       layout = llapi_layout_get_by_path(path, 0);
+       ASSERTF(layout != NULL, "errno = %d", errno);
+       rc = llapi_layout_pool_name_get(layout, mypool, sizeof(mypool));
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = strcmp(mypool, poolname);
+       ASSERTF(rc == 0, "%s != %s", mypool, poolname);
+       llapi_layout_free(layout);
+}
+
+#define T19_DESC       "Maximum length pool name is NULL-terminated"
+void test19(void)
+{
+       struct llapi_layout *layout;
+       char *name = "0123456789abcdef";
+       char mypool[LOV_MAXPOOLNAME + 1] = { '\0' };
+       int rc;
+
+       layout = llapi_layout_alloc();
+       ASSERTF(layout != NULL, "errno = %d", errno);
+       rc = llapi_layout_pool_name_set(layout, name);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = llapi_layout_pool_name_get(layout, mypool, sizeof(mypool));
+       ASSERTF(strlen(name) == strlen(mypool), "name = %s, str = %s", name,
+               mypool);
+       llapi_layout_free(layout);
+}
+
+#define T20FILE                "t20"
+#define T20_DESC       "LLAPI_LAYOUT_DEFAULT is honored"
+void test20(void)
+{
+       int             rc;
+       int             fd;
+       struct llapi_layout     *deflayout;
+       struct llapi_layout     *filelayout;
+       char            path[PATH_MAX];
+       uint64_t        fsize;
+       uint64_t        fcount;
+       uint64_t        dsize;
+       uint64_t        dcount;
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T20FILE);
+
+       rc = unlink(path);
+       ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);
+
+       filelayout = llapi_layout_alloc();
+       ASSERTF(filelayout != NULL, "errno = %d", errno);
+
+       rc = llapi_layout_stripe_size_set(filelayout, LLAPI_LAYOUT_DEFAULT);
+       ASSERTF(rc == 0, "rc = %d, errno = %d", rc, errno);
+
+       rc = llapi_layout_stripe_count_set(filelayout, LLAPI_LAYOUT_DEFAULT);
+       ASSERTF(rc == 0, "rc = %d, errno = %d", rc, errno);
+
+       fd = llapi_layout_file_create(path, 0, 0640, filelayout);
+       ASSERTF(fd >= 0, "errno = %d", errno);
+
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       llapi_layout_free(filelayout);
+
+       deflayout = llapi_layout_get_by_path(lustre_dir, LAYOUT_GET_EXPECTED);
+       ASSERTF(deflayout != NULL, "errno = %d", errno);
+
+       filelayout = llapi_layout_get_by_path(path, 0);
+       ASSERTF(filelayout != NULL, "errno = %d", errno);
+
+       rc = llapi_layout_stripe_count_get(filelayout, &fcount);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = llapi_layout_stripe_count_get(deflayout, &dcount);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       ASSERTF(fcount == dcount, "%"PRIu64" != %"PRIu64, fcount, dcount);
+
+       rc = llapi_layout_stripe_size_get(filelayout, &fsize);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       rc = llapi_layout_stripe_size_get(deflayout, &dsize);
+       ASSERTF(rc == 0, "errno = %d", errno);
+       ASSERTF(fsize == dsize, "%"PRIu64" != %"PRIu64, fsize, dsize);
+
+       llapi_layout_free(filelayout);
+       llapi_layout_free(deflayout);
+}
+
+#define T21_DESC       "llapi_layout_file_create fails for non-Lustre file"
+void test21(void)
+{
+       struct llapi_layout *layout;
+       char template[PATH_MAX];
+       int fd;
+       int rc;
+
+       snprintf(template, sizeof(template), "%s/XXXXXX", P_tmpdir);
+       fd = mkstemp(template);
+       ASSERTF(fd >= 0, "template = %s, errno = %d", template, errno);
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", fd);
+       rc = unlink(template);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       layout = llapi_layout_alloc();
+       ASSERTF(layout != NULL, "errno = %d", errno);
+
+       fd = llapi_layout_file_create(template, 0, 0640, layout);
+       ASSERTF(fd == -1 && errno == ENOTTY,
+               "fd = %d, errno = %d, template = %s", fd, errno, template);
+       llapi_layout_free(layout);
+}
+
+#define T22FILE                "t22"
+#define T22_DESC       "llapi_layout_file_create applied mode correctly"
+void test22(void)
+{
+       int             rc;
+       int             fd;
+       char            path[PATH_MAX];
+       struct stat     st;
+       mode_t          mode_in = 0640;
+       mode_t          mode_out;
+       mode_t          umask_orig;
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T22FILE);
+
+       rc = unlink(path);
+       ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);
+
+       umask_orig = umask(S_IWGRP | S_IWOTH);
+
+       fd = llapi_layout_file_create(path, 0, mode_in, NULL);
+       ASSERTF(fd >= 0, "errno = %d", errno);
+
+       (void) umask(umask_orig);
+
+       rc = fstat(fd, &st);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", fd);
+
+       mode_out = st.st_mode & ~S_IFMT;
+       ASSERTF(mode_in == mode_out, "%o != %o", mode_in, mode_out);
+}
+
+#define T23_DESC       "llapi_layout_get_by_path fails for non-Lustre file"
+void test23(void)
+{
+       struct llapi_layout *layout;
+       char template[PATH_MAX];
+       int fd;
+       int rc;
+
+       snprintf(template, sizeof(template), "%s/XXXXXX", P_tmpdir);
+       fd = mkstemp(template);
+       ASSERTF(fd >= 0, "template = %s, errno = %d", template, errno);
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", fd);
+
+       layout = llapi_layout_get_by_path(template, 0);
+       ASSERTF(layout == NULL && errno == ENOTTY,
+               "errno = %d, template = %s", errno, template);
+
+       rc = unlink(template);
+       ASSERTF(rc == 0, "errno = %d", errno);
+}
+
+/* llapi_layout_get_by_path(path, LAYOUT_GET_EXPECTED) returns expected layout
+ * for file with unspecified layout. */
+#define T24FILE                "t24"
+#define T24_DESC       "LAYOUT_GET_EXPECTED works with existing file"
+void test24(void)
+{
+       int fd;
+       int rc;
+       struct llapi_layout *layout;
+       uint64_t count;
+       uint64_t size;
+       uint64_t pattern;
+       char path[PATH_MAX];
+
+       snprintf(path, sizeof(path), "%s/%s", lustre_dir, T24FILE);
+
+       rc = unlink(path);
+       ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
+       fd = open(path, O_CREAT, 0640);
+       ASSERTF(fd >= 0, "errno = %d", errno);
+       rc = close(fd);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       layout = llapi_layout_get_by_path(path, LAYOUT_GET_EXPECTED);
+       ASSERTF(layout != NULL, "errno = %d\n", errno);
+
+       rc = llapi_layout_stripe_count_get(layout, &count);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(count != LLAPI_LAYOUT_DEFAULT, "expected literal value");
+
+       rc = llapi_layout_stripe_size_get(layout, &size);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(size != LLAPI_LAYOUT_DEFAULT, "expected literal value");
+
+       rc = llapi_layout_pattern_get(layout, &pattern);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(pattern != LLAPI_LAYOUT_DEFAULT, "expected literal value");
+
+       llapi_layout_free(layout);
+}
+
+/* llapi_layout_get_by_path(path, LAYOUT_GET_EXPECTED) returns expected layout
+ * for directory with unspecified layout. */
+#define T25DIR         "d25"
+#define T25_DESC       "LAYOUT_GET_EXPECTED works with directory"
+void test25(void)
+{
+       int rc;
+       struct llapi_layout *layout;
+       uint64_t count;
+       uint64_t size;
+       uint64_t pattern;
+       char dir[PATH_MAX];
+
+       snprintf(dir, sizeof(dir), "%s/%s", lustre_dir, T25DIR);
+
+       rc = rmdir(dir);
+       ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
+       rc = mkdir(dir, 0750);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       layout = llapi_layout_get_by_path(dir, LAYOUT_GET_EXPECTED);
+       ASSERTF(layout != NULL, "errno = %d\n", errno);
+
+       rc = llapi_layout_stripe_count_get(layout, &count);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(count != LLAPI_LAYOUT_DEFAULT, "expected literal value");
+
+       rc = llapi_layout_stripe_size_get(layout, &size);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(size != LLAPI_LAYOUT_DEFAULT, "expected literal value");
+
+       rc = llapi_layout_pattern_get(layout, &pattern);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(pattern != LLAPI_LAYOUT_DEFAULT, "expected literal value");
+
+       llapi_layout_free(layout);
+}
+
+/* llapi_layout_get_by_path(path, LAYOUT_GET_EXPECTED) correctly combines
+ * specified attributes of parent directory with attributes filesystem root. */
+#define T26DIR         "d26"
+#define T26_DESC       "LAYOUT_GET_EXPECTED partially specified parent"
+#define T26_STRIPE_SIZE        (1048576 * 4)
+void test26(void)
+{
+       int rc;
+       struct llapi_layout *layout;
+       const char *lfs = getenv("LFS");
+       uint64_t count;
+       uint64_t size;
+       uint64_t pattern;
+       char dir[PATH_MAX];
+       char cmd[4096];
+
+       snprintf(dir, sizeof(dir), "%s/%s", lustre_dir, T26DIR);
+       rc = rmdir(dir);
+       ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
+       rc = mkdir(dir, 0750);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       if (lfs == NULL)
+               lfs = "/usr/bin/lfs";
+
+       snprintf(cmd, sizeof(cmd), "%s setstripe -s %d %s", lfs,
+                T26_STRIPE_SIZE, dir);
+       rc = system(cmd);
+       ASSERTF(rc == 0, "system(%s): exit status %d", cmd, WEXITSTATUS(rc));
+
+       layout = llapi_layout_get_by_path(dir, LAYOUT_GET_EXPECTED);
+       ASSERTF(layout != NULL, "errno = %d\n", errno);
+
+       rc = llapi_layout_stripe_count_get(layout, &count);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(count != LLAPI_LAYOUT_DEFAULT, "expected literal value");
+
+       rc = llapi_layout_stripe_size_get(layout, &size);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(size == T26_STRIPE_SIZE, "size = %"PRIu64, size);
+
+       rc = llapi_layout_pattern_get(layout, &pattern);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(pattern != LLAPI_LAYOUT_DEFAULT, "expected literal value");
+
+       llapi_layout_free(layout);
+}
+
+/* llapi_layout_get_by_path(path, LAYOUT_GET_EXPECTED) work with
+ * non existing file. */
+#define T27DIR         "d27"
+#define T27_DESC       "LAYOUT_GET_EXPECTED with non existing file"
+#define T27_STRIPE_SIZE        (1048576 * 3)
+void test27(void)
+{
+       int rc;
+       struct llapi_layout *layout;
+       const char *lfs = getenv("LFS");
+       uint64_t count;
+       uint64_t size;
+       uint64_t pattern;
+       char dirpath[PATH_MAX];
+       char filepath[PATH_MAX];
+       char cmd[4096];
+
+       snprintf(dirpath, sizeof(dirpath), "%s/%s", lustre_dir, T27DIR);
+       snprintf(filepath, sizeof(filepath), "%s/nonesuch", dirpath);
+
+       rc = rmdir(dirpath);
+       ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
+       rc = mkdir(dirpath, 0750);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       if (lfs == NULL)
+               lfs = "/usr/bin/lfs";
+
+       snprintf(cmd, sizeof(cmd), "%s setstripe -s %d %s", lfs,
+                T27_STRIPE_SIZE, dirpath);
+       rc = system(cmd);
+       ASSERTF(rc == 0, "system(%s): exit status %d", cmd, WEXITSTATUS(rc));
+
+       layout = llapi_layout_get_by_path(filepath, LAYOUT_GET_EXPECTED);
+       ASSERTF(layout != NULL, "errno = %d\n", errno);
+
+       rc = llapi_layout_stripe_count_get(layout, &count);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(count != LLAPI_LAYOUT_DEFAULT, "expected literal value");
+
+       rc = llapi_layout_stripe_size_get(layout, &size);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(size == T27_STRIPE_SIZE, "size = %"PRIu64, size);
+
+       rc = llapi_layout_pattern_get(layout, &pattern);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(pattern != LLAPI_LAYOUT_DEFAULT, "expected literal value");
+
+       llapi_layout_free(layout);
+}
+
+/* llapi_layout_stripe_count_get returns LLAPI_LAYOUT_WIDE for a directory
+ * with a stripe_count of -1. */
+#define T28DIR         "d28"
+#define T28_DESC       "LLAPI_LAYOUT_WIDE returned as expected"
+void test28(void)
+{
+       int rc;
+       struct llapi_layout *layout;
+       const char *lfs = getenv("LFS");
+       uint64_t count;
+       char dirpath[PATH_MAX];
+       char cmd[4096];
+
+       snprintf(dirpath, sizeof(dirpath), "%s/%s", lustre_dir, T28DIR);
+
+       rc = rmdir(dirpath);
+       ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
+       rc = mkdir(dirpath, 0750);
+       ASSERTF(rc == 0, "errno = %d", errno);
+
+       if (lfs == NULL)
+               lfs = "/usr/bin/lfs";
+
+       snprintf(cmd, sizeof(cmd), "%s setstripe -c -1 %s", lfs, dirpath);
+       rc = system(cmd);
+       ASSERTF(rc == 0, "system(%s): exit status %d", cmd, WEXITSTATUS(rc));
+
+       layout = llapi_layout_get_by_path(dirpath, 0);
+       ASSERTF(layout != NULL, "errno = %d\n", errno);
+
+       rc = llapi_layout_stripe_count_get(layout, &count);
+       ASSERTF(rc == 0, "errno = %d\n", errno);
+       ASSERTF(count == LLAPI_LAYOUT_WIDE, "count = %"PRIu64"\n", count);
+
+       llapi_layout_free(layout);
+}
+
+#define TEST_DESC_LEN  50
+struct test_tbl_entry {
+       void (*tte_fn)(void);
+       char tte_desc[TEST_DESC_LEN];
+       bool tte_skip;
+};
+
+static struct test_tbl_entry test_tbl[] = {
+       { &test0,  T0_DESC, false },
+       { &test1,  T1_DESC, false },
+       { &test2,  T2_DESC, false },
+       { &test3,  T3_DESC, false },
+       { &test4,  T4_DESC, false },
+       { &test5,  T5_DESC, false },
+       { &test6,  T6_DESC, false },
+       { &test7,  T7_DESC, false },
+       { &test8,  T8_DESC, false },
+       { &test9,  T9_DESC, false },
+       { &test10, T10_DESC, false },
+       { &test11, T11_DESC, false },
+       { &test12, T12_DESC, false },
+       { &test13, T13_DESC, false },
+       { &test14, T14_DESC, false },
+       { &test15, T15_DESC, false },
+       { &test16, T16_DESC, false },
+       { &test17, T17_DESC, false },
+       { &test18, T18_DESC, false },
+       { &test19, T19_DESC, false },
+       { &test20, T20_DESC, false },
+       { &test21, T21_DESC, false },
+       { &test22, T22_DESC, false },
+       { &test23, T23_DESC, false },
+       { &test24, T24_DESC, false },
+       { &test25, T25_DESC, false },
+       { &test26, T26_DESC, false },
+       { &test27, T27_DESC, false },
+       { &test28, T28_DESC, false },
+};
+#define NUM_TESTS      (sizeof(test_tbl) / sizeof(struct test_tbl_entry))
+
+void print_test_desc(int test_num, const char *test_desc, const char *status)
+{
+       int i;
+
+       printf(" test %2d: %s ", test_num, test_desc);
+       for (i = 0; i < TEST_DESC_LEN - strlen(test_desc); i++)
+               printf(".");
+       printf(" %s\n", status);
+}
+
+/* This function runs a single test by forking the process.  This way,
+ * if there is a segfault during a test, the test program won't crash. */
+int test(void (*test_fn)(), const char *test_desc, bool test_skip, int test_num)
+{
+       int rc = 0;
+       pid_t pid;
+       char status_buf[128];
+
+       if (test_skip) {
+               print_test_desc(test_num, test_desc, "skip");
+               return 0;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               ERROR("cannot fork: %s", strerror(errno));
+       } else if (pid > 0) {
+               int status = 0;
+
+               /* Non-zero value indicates failure. */
+               wait(&status);
+               if (status == 0) {
+                       strncpy(status_buf, "pass", sizeof(status_buf));
+               } else if WIFSIGNALED(status) {
+                       snprintf(status_buf, sizeof(status_buf),
+                                "fail (exit status %d, killed by SIG%d)",
+                                WEXITSTATUS(status), WTERMSIG(status));
+                       rc = -1;
+               } else {
+                       snprintf(status_buf, sizeof(status_buf),
+                                "fail (exit status %d)", WEXITSTATUS(status));
+                       rc = -1;
+               }
+               print_test_desc(test_num, test_desc, status_buf);
+       } else if (pid == 0) {
+               /* Run the test in the child process.  Exit with 0 for success,
+                * non-zero for failure */
+               test_fn();
+               exit(0);
+       }
+
+       return rc;
+}
+
+static void process_args(int argc, char *argv[])
+{
+       int c;
+
+       while ((c = getopt(argc, argv, "d:p:o:")) != -1) {
+               switch (c) {
+               case 'd':
+                       lustre_dir = optarg;
+                       break;
+               case 'p':
+                       poolname = optarg;
+                       break;
+               case 'o':
+                       num_osts = atoi(optarg);
+                       break;
+               case '?':
+                       fprintf(stderr, "Unknown option '%c'\n", optopt);
+                       usage(argv[0]);
+               }
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       int rc = 0;
+       int i;
+       struct stat s;
+       char fsname[8];
+
+       llapi_msg_set_level(LLAPI_MSG_OFF);
+
+       process_args(argc, argv);
+       if (lustre_dir == NULL)
+               lustre_dir = "/mnt/lustre";
+       if (poolname == NULL)
+               poolname = "testpool";
+       if (num_osts == -1)
+               num_osts = 2;
+
+       if (num_osts < 2)
+               DIE("Error: at least 2 OSTS are required\n");
+
+       if (stat(lustre_dir, &s) < 0)
+               DIE("cannot stat %s: %s\n", lustre_dir, strerror(errno));
+       else if (!S_ISDIR(s.st_mode))
+               DIE("%s: not a directory\n", lustre_dir);
+
+       rc = llapi_search_fsname(lustre_dir, fsname);
+       if (rc != 0) {
+               fprintf(stderr, "Error: %s: not a Lustre filesystem\n",
+                       lustre_dir);
+               exit(EXIT_FAILURE);
+       }
+
+       /* Play nice with Lustre test scripts. Non-line buffered output
+        * stream under I/O redirection may appear incorrectly. */
+       setvbuf(stdout, NULL, _IOLBF, 0);
+
+       for (i = 0; i < NUM_TESTS; i++) {
+               struct test_tbl_entry *tst = &test_tbl[i];
+               if (test(tst->tte_fn, tst->tte_desc, tst->tte_skip, i) != 0)
+                       rc++;
+       }
+       return rc;
+}
index fe5e72d..d13d7b4 100644 (file)
@@ -1991,6 +1991,24 @@ test_27C() { #LU-2871
 }
 run_test 27C "check full striping across all OSTs"
 
+test_27D() {
+       [ $OSTCOUNT -lt 2 ] && skip "needs >= 2 OSTs" && return
+       local POOL=${POOL:-testpool}
+       local first_ost=0
+       local last_ost=$(($OSTCOUNT - 1))
+       local ost_step=1
+       local ost_list=$(seq $first_ost $ost_step $last_ost)
+       local ost_range="$first_ost $last_ost $ost_step"
+
+       mkdir -p $DIR/$tdir
+       pool_add $POOL || error "pool_add failed"
+       pool_add_targets $POOL $ost_range || error "pool_add_targets failed"
+       llapi_layout_test -d$DIR/$tdir -p$POOL -o$OSTCOUNT ||
+               error "llapi_layout_test failed"
+       cleanup_pools || error "cleanup_pools failed"
+}
+run_test 27D "validate llapi_layout API"
+
 # createtest also checks that device nodes are created and
 # then visible correctly (#2091)
 test_28() { # bug 2091
@@ -10951,251 +10969,6 @@ test_187b() {
 }
 run_test 187b "Test data version change on volatile file"
 
-# OST pools tests
-check_file_in_pool()
-{
-       local file=$1
-       local pool=$2
-       local tlist="$3"
-       local res=$($GETSTRIPE $file | grep 0x | cut -f2)
-       for i in $res
-       do
-               for t in $tlist ; do
-                       [ "$i" -eq "$t" ] && continue 2
-               done
-
-               echo "pool list: $tlist"
-               echo "striping: $res"
-               error_noexit "$file not allocated in $pool"
-               return 1
-       done
-       return 0
-}
-
-pool_add() {
-       echo "Creating new pool"
-       local pool=$1
-
-       create_pool $FSNAME.$pool ||
-               { error_noexit "No pool created, result code $?"; return 1; }
-       [ $($LFS pool_list $FSNAME | grep -c $pool) -eq 1 ] ||
-               { error_noexit "$pool not in lfs pool_list"; return 2; }
-}
-
-pool_add_targets() {
-       echo "Adding targets to pool"
-       local pool=$1
-       local first=$2
-       local last=$3
-       local step=${4:-1}
-
-       local list=$(seq $first $step $last)
-
-       local t=$(for i in $list; do printf "$FSNAME-OST%04x_UUID " $i; done)
-       do_facet mgs $LCTL pool_add \
-                       $FSNAME.$pool $FSNAME-OST[$first-$last/$step]
-       wait_update $HOSTNAME "lctl get_param -n lov.$FSNAME-*.pools.$pool \
-                       | sort -u | tr '\n' ' ' " "$t" || { 
-               error_noexit "Add to pool failed"
-               return 1
-       }
-       local lfscount=$($LFS pool_list $FSNAME.$pool | grep -c "\-OST")
-       local addcount=$(((last - first) / step + 1))
-       [ $lfscount -eq $addcount ] || {
-               error_noexit "lfs pool_list bad ost count" \
-                                               "$lfscount != $addcount"
-               return 2
-       }
-}
-
-pool_set_dir() {
-       local pool=$1
-       local tdir=$2
-       echo "Setting pool on directory $tdir"
-
-       $SETSTRIPE -c 2 -p $pool $tdir && return 0
-
-       error_noexit "Cannot set pool $pool to $tdir"
-       return 1
-}
-
-pool_check_dir() {
-       local pool=$1
-       local tdir=$2
-       echo "Checking pool on directory $tdir"
-
-       local res=$($GETSTRIPE --pool $tdir | sed "s/\s*$//")
-       [ "$res" = "$pool" ] && return 0
-
-       error_noexit "Pool on '$tdir' is '$res', not '$pool'"
-       return 1
-}
-
-pool_dir_rel_path() {
-       echo "Testing relative path works well"
-       local pool=$1
-       local tdir=$2
-       local root=$3
-
-       mkdir -p $root/$tdir/$tdir
-       cd $root/$tdir
-       pool_set_dir $pool $tdir          || return 1
-       pool_set_dir $pool ./$tdir        || return 2
-       pool_set_dir $pool ../$tdir       || return 3
-       pool_set_dir $pool ../$tdir/$tdir || return 4
-       rm -rf $tdir; cd - > /dev/null
-}
-
-pool_alloc_files() {
-       echo "Checking files allocation from directory pool"
-       local pool=$1
-       local tdir=$2
-       local count=$3
-       local tlist="$4"
-
-       local failed=0
-       for i in $(seq -w 1 $count)
-       do
-               local file=$tdir/file-$i
-               touch $file
-               check_file_in_pool $file $pool "$tlist" || \
-                       failed=$((failed + 1))
-       done
-       [ "$failed" = 0 ] && return 0
-
-       error_noexit "$failed files not allocated in $pool"
-       return 1
-}
-
-pool_create_files() {
-       echo "Creating files in pool"
-       local pool=$1
-       local tdir=$2
-       local count=$3
-       local tlist="$4"
-
-       mkdir -p $tdir
-       local failed=0
-       for i in $(seq -w 1 $count)
-       do
-               local file=$tdir/spoo-$i
-               $SETSTRIPE -p $pool $file
-               check_file_in_pool $file $pool "$tlist" || \
-                       failed=$((failed + 1))
-       done
-       [ "$failed" = 0 ] && return 0
-
-       error_noexit "$failed files not allocated in $pool"
-       return 1
-}
-
-pool_lfs_df() {
-       echo "Checking 'lfs df' output"
-       local pool=$1
-
-       local t=$($LCTL get_param -n lov.$FSNAME-clilov-*.pools.$pool |
-                       tr '\n' ' ')
-       local res=$($LFS df --pool $FSNAME.$pool |
-                       awk '{print $1}' |
-                       grep "$FSNAME-OST" |
-                       tr '\n' ' ')
-       [ "$res" = "$t" ] && return 0
-
-       error_noexit "Pools OSTs '$t' is not '$res' that lfs df reports"
-       return 1
-}
-
-pool_file_rel_path() {
-       echo "Creating files in a pool with relative pathname"
-       local pool=$1
-       local tdir=$2
-
-       mkdir -p $tdir ||
-               { error_noexit "unable to create $tdir"; return 1 ; }
-       local file="/..$tdir/$tfile-1"
-       $SETSTRIPE -p $pool $file ||
-               { error_noexit "unable to create $file" ; return 2 ; }
-
-       cd $tdir
-       $SETSTRIPE -p $pool $tfile-2 || {
-               error_noexit "unable to create $tfile-2 in $tdir"
-               return 3
-       }
-}
-
-pool_remove_first_target() {
-       echo "Removing first target from a pool"
-       local pool=$1
-
-       local pname="lov.$FSNAME-*.pools.$pool"
-       local t=$($LCTL get_param -n $pname | head -n1)
-       do_facet mgs $LCTL pool_remove $FSNAME.$pool $t
-       wait_update $HOSTNAME "lctl get_param -n $pname | grep $t" "" || {
-               error_noexit "$t not removed from $FSNAME.$pool"
-               return 1
-       }
-}
-
-pool_remove_all_targets() {
-       echo "Removing all targets from pool"
-       local pool=$1
-       local file=$2
-       local pname="lov.$FSNAME-*.pools.$pool"
-       for t in $($LCTL get_param -n $pname | sort -u)
-       do
-               do_facet mgs $LCTL pool_remove $FSNAME.$pool $t
-       done
-       wait_update $HOSTNAME "lctl get_param -n $pname" "" || {
-               error_noexit "Pool $FSNAME.$pool cannot be drained"
-               return 1
-       }
-       # striping on an empty/nonexistant pool should fall back
-       # to "pool of everything"
-       touch $file || {
-               error_noexit "failed to use fallback striping for empty pool"
-               return 2
-       }
-       # setstripe on an empty pool should fail
-       $SETSTRIPE -p $pool $file 2>/dev/null && {
-               error_noexit "expected failure when creating file" \
-                                                       "with empty pool"
-               return 3
-       }
-       return 0
-}
-
-pool_remove() {
-       echo "Destroying pool"
-       local pool=$1
-       local file=$2
-
-       do_facet mgs $LCTL pool_destroy $FSNAME.$pool
-
-       sleep 2
-       # striping on an empty/nonexistant pool should fall back 
-       # to "pool of everything"
-       touch $file || {
-               error_noexit "failed to use fallback striping for missing pool"
-               return 1
-       }
-       # setstripe on an empty pool should fail
-       $SETSTRIPE -p $pool $file 2>/dev/null && {
-               error_noexit "expected failure when creating file" \
-                                                       "with missing pool"
-               return 2
-       }
-
-       # get param should return err once pool is gone
-       if wait_update $HOSTNAME "lctl get_param -n \
-               lov.$FSNAME-*.pools.$pool 2>/dev/null || echo foo" "foo"
-       then
-               remove_pool_from_list $FSNAME.$pool
-               return 0
-       fi
-       error_noexit "Pool $FSNAME.$pool is not destroyed"
-       return 3
-}
-
 test_200() {
        [ $PARALLEL == "yes" ] && skip "skip parallel run" && return
        remote_mgs_nodsh && skip "remote MGS with nodsh" && return
index 3df2e4c..95b4c1b 100755 (executable)
@@ -6857,3 +6857,247 @@ precreated_ost_obj_count()
 
        echo $((last_id - next_id + 1))
 }
+
+check_file_in_pool()
+{
+       local file=$1
+       local pool=$2
+       local tlist="$3"
+       local res=$($GETSTRIPE $file | grep 0x | cut -f2)
+       for i in $res
+       do
+               for t in $tlist ; do
+                       [ "$i" -eq "$t" ] && continue 2
+               done
+
+               echo "pool list: $tlist"
+               echo "striping: $res"
+               error_noexit "$file not allocated in $pool"
+               return 1
+       done
+       return 0
+}
+
+pool_add() {
+       echo "Creating new pool"
+       local pool=$1
+
+       create_pool $FSNAME.$pool ||
+               { error_noexit "No pool created, result code $?"; return 1; }
+       [ $($LFS pool_list $FSNAME | grep -c $pool) -eq 1 ] ||
+               { error_noexit "$pool not in lfs pool_list"; return 2; }
+}
+
+pool_add_targets() {
+       echo "Adding targets to pool"
+       local pool=$1
+       local first=$2
+       local last=$3
+       local step=${4:-1}
+
+       local list=$(seq $first $step $last)
+
+       local t=$(for i in $list; do printf "$FSNAME-OST%04x_UUID " $i; done)
+       do_facet mgs $LCTL pool_add \
+                       $FSNAME.$pool $FSNAME-OST[$first-$last/$step]
+       wait_update $HOSTNAME "lctl get_param -n lov.$FSNAME-*.pools.$pool \
+                       | sort -u | tr '\n' ' ' " "$t" || {
+               error_noexit "Add to pool failed"
+               return 1
+       }
+       local lfscount=$($LFS pool_list $FSNAME.$pool | grep -c "\-OST")
+       local addcount=$(((last - first) / step + 1))
+       [ $lfscount -eq $addcount ] || {
+               error_noexit "lfs pool_list bad ost count" \
+                                               "$lfscount != $addcount"
+               return 2
+       }
+}
+
+pool_set_dir() {
+       local pool=$1
+       local tdir=$2
+       echo "Setting pool on directory $tdir"
+
+       $SETSTRIPE -c 2 -p $pool $tdir && return 0
+
+       error_noexit "Cannot set pool $pool to $tdir"
+       return 1
+}
+
+pool_check_dir() {
+       local pool=$1
+       local tdir=$2
+       echo "Checking pool on directory $tdir"
+
+       local res=$($GETSTRIPE --pool $tdir | sed "s/\s*$//")
+       [ "$res" = "$pool" ] && return 0
+
+       error_noexit "Pool on '$tdir' is '$res', not '$pool'"
+       return 1
+}
+
+pool_dir_rel_path() {
+       echo "Testing relative path works well"
+       local pool=$1
+       local tdir=$2
+       local root=$3
+
+       mkdir -p $root/$tdir/$tdir
+       cd $root/$tdir
+       pool_set_dir $pool $tdir          || return 1
+       pool_set_dir $pool ./$tdir        || return 2
+       pool_set_dir $pool ../$tdir       || return 3
+       pool_set_dir $pool ../$tdir/$tdir || return 4
+       rm -rf $tdir; cd - > /dev/null
+}
+
+pool_alloc_files() {
+       echo "Checking files allocation from directory pool"
+       local pool=$1
+       local tdir=$2
+       local count=$3
+       local tlist="$4"
+
+       local failed=0
+       for i in $(seq -w 1 $count)
+       do
+               local file=$tdir/file-$i
+               touch $file
+               check_file_in_pool $file $pool "$tlist" || \
+                       failed=$((failed + 1))
+       done
+       [ "$failed" = 0 ] && return 0
+
+       error_noexit "$failed files not allocated in $pool"
+       return 1
+}
+
+pool_create_files() {
+       echo "Creating files in pool"
+       local pool=$1
+       local tdir=$2
+       local count=$3
+       local tlist="$4"
+
+       mkdir -p $tdir
+       local failed=0
+       for i in $(seq -w 1 $count)
+       do
+               local file=$tdir/spoo-$i
+               $SETSTRIPE -p $pool $file
+               check_file_in_pool $file $pool "$tlist" || \
+                       failed=$((failed + 1))
+       done
+       [ "$failed" = 0 ] && return 0
+
+       error_noexit "$failed files not allocated in $pool"
+       return 1
+}
+
+pool_lfs_df() {
+       echo "Checking 'lfs df' output"
+       local pool=$1
+
+       local t=$($LCTL get_param -n lov.$FSNAME-clilov-*.pools.$pool |
+                       tr '\n' ' ')
+       local res=$($LFS df --pool $FSNAME.$pool |
+                       awk '{print $1}' |
+                       grep "$FSNAME-OST" |
+                       tr '\n' ' ')
+       [ "$res" = "$t" ] && return 0
+
+       error_noexit "Pools OSTs '$t' is not '$res' that lfs df reports"
+       return 1
+}
+
+pool_file_rel_path() {
+       echo "Creating files in a pool with relative pathname"
+       local pool=$1
+       local tdir=$2
+
+       mkdir -p $tdir ||
+               { error_noexit "unable to create $tdir"; return 1 ; }
+       local file="/..$tdir/$tfile-1"
+       $SETSTRIPE -p $pool $file ||
+               { error_noexit "unable to create $file" ; return 2 ; }
+
+       cd $tdir
+       $SETSTRIPE -p $pool $tfile-2 || {
+               error_noexit "unable to create $tfile-2 in $tdir"
+               return 3
+       }
+}
+
+pool_remove_first_target() {
+       echo "Removing first target from a pool"
+       local pool=$1
+
+       local pname="lov.$FSNAME-*.pools.$pool"
+       local t=$($LCTL get_param -n $pname | head -1)
+       do_facet mgs $LCTL pool_remove $FSNAME.$pool $t
+       wait_update $HOSTNAME "lctl get_param -n $pname | grep $t" "" || {
+               error_noexit "$t not removed from $FSNAME.$pool"
+               return 1
+       }
+}
+
+pool_remove_all_targets() {
+       echo "Removing all targets from pool"
+       local pool=$1
+       local file=$2
+       local pname="lov.$FSNAME-*.pools.$pool"
+       for t in $($LCTL get_param -n $pname | sort -u)
+       do
+               do_facet mgs $LCTL pool_remove $FSNAME.$pool $t
+       done
+       wait_update $HOSTNAME "lctl get_param -n $pname" "" || {
+               error_noexit "Pool $FSNAME.$pool cannot be drained"
+               return 1
+       }
+       # striping on an empty/nonexistant pool should fall back
+       # to "pool of everything"
+       touch $file || {
+               error_noexit "failed to use fallback striping for empty pool"
+               return 2
+       }
+       # setstripe on an empty pool should fail
+       $SETSTRIPE -p $pool $file 2>/dev/null && {
+               error_noexit "expected failure when creating file" \
+                                                       "with empty pool"
+               return 3
+       }
+       return 0
+}
+
+pool_remove() {
+       echo "Destroying pool"
+       local pool=$1
+       local file=$2
+
+       do_facet mgs $LCTL pool_destroy $FSNAME.$pool
+
+       sleep 2
+       # striping on an empty/nonexistant pool should fall back
+       # to "pool of everything"
+       touch $file || {
+               error_noexit "failed to use fallback striping for missing pool"
+               return 1
+       }
+       # setstripe on an empty pool should fail
+       $SETSTRIPE -p $pool $file 2>/dev/null && {
+               error_noexit "expected failure when creating file" \
+                                                       "with missing pool"
+               return 2
+       }
+
+       # get param should return err once pool is gone
+       if wait_update $HOSTNAME "lctl get_param -n \
+               lov.$FSNAME-*.pools.$pool 2>/dev/null || echo foo" "foo"
+       then
+               remove_pool_from_list $FSNAME.$pool
+               return 0
+       fi
+       error_noexit "Pool $FSNAME.$pool is not destroyed"
+       return 3
+}
index 542b54b..f63120d 100644 (file)
@@ -1,7 +1,8 @@
 This licence file applies to new library files:
 lustre/utils/liblustreapi_hsm.c
+lustre/utils/liblustreapi_layout.c
 lustre/utils/lustreapi_internal.h
-------------------------------------------------------------------------------- 
+-------------------------------------------------------------------------------
                  GNU LESSER GENERAL PUBLIC LICENSE
                        Version 2.1, February 1999
 
index 1c1bb4c..f56f1f7 100644 (file)
@@ -86,8 +86,8 @@ L_IOCTL := $(top_builddir)/libcfs/libcfs/util/l_ioctl.c
 L_KERNELCOMM := $(top_builddir)/libcfs/libcfs/kernel_user_comm.c
 liblustreapitmp_a_SOURCES = liblustreapi.c liblustreapi_hsm.c \
                            liblustreapi_nodemap.c lustreapi_internal.h \
-                           liblustreapi_json.c $(L_IOCTL) $(L_KERNELCOMM) \
-                           $(L_STRING)
+                           liblustreapi_json.c liblustreapi_layout.c \
+                           $(L_IOCTL) $(L_KERNELCOMM) $(L_STRING)
 
 # build static and shared lib lustreapi
 liblustreapi.a : liblustreapitmp.a
index cc3c97e..2cf1001 100644 (file)
@@ -56,6 +56,7 @@
 #include <dirent.h>
 #include <stdarg.h>
 #include <sys/stat.h>
+#include <sys/statfs.h>
 #include <sys/types.h>
 #include <sys/syscall.h>
 #include <sys/xattr.h>
@@ -88,6 +89,11 @@ void llapi_msg_set_level(int level)
                 llapi_msg_level = level;
 }
 
+int llapi_msg_get_level(void)
+{
+       return llapi_msg_level;
+}
+
 static void error_callback_default(enum llapi_message_level level, int err,
                                   const char *fmt, va_list ap)
 {
@@ -248,26 +254,26 @@ int llapi_stripe_limit_check(unsigned long long stripe_size, int stripe_offset,
                                "larger than expected (%u)", page_size,
                                LOV_MIN_STRIPE_SIZE);
        }
-       if ((stripe_size & (LOV_MIN_STRIPE_SIZE - 1))) {
+       if (!llapi_stripe_size_is_aligned(stripe_size)) {
                rc = -EINVAL;
                llapi_error(LLAPI_MSG_ERROR, rc, "error: bad stripe_size %llu, "
                                "must be an even multiple of %d bytes",
                                stripe_size, page_size);
                return rc;
        }
-       if (stripe_offset < -1) {
+       if (!llapi_stripe_offset_is_valid(stripe_offset)) {
                rc = -EINVAL;
                llapi_error(LLAPI_MSG_ERROR, rc, "error: bad stripe offset %d",
                                stripe_offset);
                return rc;
        }
-       if (stripe_count < -1 || stripe_count > LOV_MAX_STRIPE_COUNT) {
+       if (!llapi_stripe_count_is_valid(stripe_count)) {
                rc = -EINVAL;
                llapi_error(LLAPI_MSG_ERROR, rc, "error: bad stripe count %d",
                                stripe_count);
                return rc;
        }
-       if (stripe_size >= (1ULL << 32)) {
+       if (llapi_stripe_size_is_too_big(stripe_size)) {
                rc = -EINVAL;
                llapi_error(LLAPI_MSG_ERROR, rc,
                                "warning: stripe size 4G or larger "
@@ -4622,3 +4628,28 @@ out_close:
 out:
        return rc;
 }
+
+/**
+ * Attempt to open a file with Lustre file identifier \a fid
+ * and return an open file descriptor.
+ *
+ * \param[in] lustre_dir       path within Lustre filesystem containing \a fid
+ * \param[in] fid              Lustre file identifier of file to open
+ * \param[in] flags            open() flags
+ *
+ * \retval                     non-negative file descriptor on successful open
+ * \retval                     -1 if an error occurred
+ */
+int llapi_open_by_fid(const char *lustre_dir, const lustre_fid *fid, int flags)
+{
+       char mntdir[PATH_MAX];
+       char path[PATH_MAX];
+       int rc;
+
+       rc = llapi_search_mounts(lustre_dir, 0, mntdir, NULL);
+       if (rc != 0)
+               return -1;
+
+       snprintf(path, sizeof(path), "%s/.lustre/fid/"DFID, mntdir, PFID(fid));
+       return open(path, flags);
+}
diff --git a/lustre/utils/liblustreapi_layout.c b/lustre/utils/liblustreapi_layout.c
new file mode 100644 (file)
index 0000000..eff35e2
--- /dev/null
@@ -0,0 +1,998 @@
+/*
+ * LGPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 or (at your discretion) any later version.
+ * (LGPL) version 2.1 accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl-2.1.html
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * LGPL HEADER END
+ */
+/*
+ * lustre/utils/liblustreapi_layout.c
+ *
+ * lustreapi library for layout calls for interacting with the layout of
+ * Lustre files while hiding details of the internal data structures
+ * from the user.
+ *
+ * Author: Ned Bass <bass6@llnl.gov>
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/xattr.h>
+
+#include <lustre/lustreapi.h>
+#include <lustre/lustre_idl.h>
+#include "lustreapi_internal.h"
+
+/**
+ * An Opaque data type abstracting the layout of a Lustre file.
+ *
+ * Duplicate the fields we care about from struct lov_user_md_v3.
+ * Deal with v1 versus v3 format issues only when we read or write
+ * files.
+ */
+struct llapi_layout {
+       uint32_t        llot_magic;
+       uint64_t        llot_pattern;
+       uint64_t        llot_stripe_size;
+       uint64_t        llot_stripe_count;
+       uint64_t        llot_stripe_offset;
+       /** Indicates if llot_objects array has been initialized. */
+       bool            llot_objects_are_valid;
+       /* Add 1 so user always gets back a null terminated string. */
+       char            llot_pool_name[LOV_MAXPOOLNAME + 1];
+       struct          lov_user_ost_data_v1 llot_objects[0];
+};
+
+/**
+ * Byte-swap the fields of struct lov_user_md.
+ *
+ * XXX Rather than duplicating swabbing code here, we should eventually
+ * refactor the needed functions in lustre/ptlrpc/pack_generic.c
+ * into a library that can be shared between kernel and user code.
+ */
+static void
+llapi_layout_swab_lov_user_md(struct lov_user_md *lum, int object_count)
+{
+       int i;
+       struct lov_user_md_v3 *lumv3 = (struct lov_user_md_v3 *)lum;
+       struct lov_user_ost_data *lod;
+
+       __swab32s(&lum->lmm_magic);
+       __swab32s(&lum->lmm_pattern);
+       __swab32s(&lum->lmm_stripe_size);
+       __swab16s(&lum->lmm_stripe_count);
+       __swab16s(&lum->lmm_stripe_offset);
+
+       if (lum->lmm_magic != LOV_MAGIC_V1)
+               lod = lumv3->lmm_objects;
+       else
+               lod = lum->lmm_objects;
+
+       for (i = 0; i < object_count; i++)
+               __swab32s(&lod[i].l_ost_idx);
+}
+
+/**
+ * Allocate storage for a llapi_layout with \a num_stripes stripes.
+ *
+ * \param[in] num_stripes      number of stripes in new layout
+ *
+ * \retval     valid pointer if allocation succeeds
+ * \retval     NULL if allocation fails
+ */
+static struct llapi_layout *__llapi_layout_alloc(unsigned int num_stripes)
+{
+       struct llapi_layout *layout = NULL;
+       size_t size = sizeof(*layout) +
+               (num_stripes * sizeof(layout->llot_objects[0]));
+
+       if (num_stripes > LOV_MAX_STRIPE_COUNT)
+               errno = EINVAL;
+       else
+               layout = calloc(1, size);
+
+       return layout;
+}
+
+/**
+ * Copy the data from a lov_user_md to a newly allocated llapi_layout.
+ *
+ * The caller is responsible for freeing the returned pointer.
+ *
+ * \param[in] lum      LOV user metadata structure to copy data from
+ *
+ * \retval             valid llapi_layout pointer on success
+ * \retval             NULL if memory allocation fails
+ */
+static struct llapi_layout *
+llapi_layout_from_lum(const struct lov_user_md *lum, size_t object_count)
+{
+       struct llapi_layout *layout;
+       size_t objects_sz;
+
+       objects_sz = object_count * sizeof(lum->lmm_objects[0]);
+
+       layout = __llapi_layout_alloc(object_count);
+       if (layout == NULL)
+               return NULL;
+
+       layout->llot_magic = LLAPI_LAYOUT_MAGIC;
+
+       if (lum->lmm_pattern == LOV_PATTERN_RAID0)
+               layout->llot_pattern = LLAPI_LAYOUT_RAID0;
+       else
+               /* Lustre only supports RAID0 for now. */
+               layout->llot_pattern = lum->lmm_pattern;
+
+       if (lum->lmm_stripe_size == 0)
+               layout->llot_stripe_size = LLAPI_LAYOUT_DEFAULT;
+       else
+               layout->llot_stripe_size = lum->lmm_stripe_size;
+
+       if (lum->lmm_stripe_count == (typeof(lum->lmm_stripe_count))-1)
+               layout->llot_stripe_count = LLAPI_LAYOUT_WIDE;
+       else if (lum->lmm_stripe_count == 0)
+               layout->llot_stripe_count = LLAPI_LAYOUT_DEFAULT;
+       else
+               layout->llot_stripe_count = lum->lmm_stripe_count;
+
+       /* Don't copy lmm_stripe_offset: it is always zero
+        * when reading attributes. */
+
+       if (lum->lmm_magic != LOV_USER_MAGIC_V1) {
+               const struct lov_user_md_v3 *lumv3;
+               lumv3 = (struct lov_user_md_v3 *)lum;
+               snprintf(layout->llot_pool_name, sizeof(layout->llot_pool_name),
+                        "%s", lumv3->lmm_pool_name);
+               memcpy(layout->llot_objects, lumv3->lmm_objects, objects_sz);
+       } else {
+               const struct lov_user_md_v1 *lumv1;
+               lumv1 = (struct lov_user_md_v1 *)lum;
+               memcpy(layout->llot_objects, lumv1->lmm_objects, objects_sz);
+       }
+       if (object_count > 0)
+               layout->llot_objects_are_valid = true;
+
+       return layout;
+}
+
+/**
+ * Copy the data from a llapi_layout to a newly allocated lov_user_md.
+ *
+ * The caller is responsible for freeing the returned pointer.
+ *
+ * The current version of this API doesn't support specifying the OST
+ * index of arbitrary stripes, only stripe 0 via lmm_stripe_offset.
+ * There is therefore no need to copy the lmm_objects array.
+ *
+ * \param[in] layout   the layout to copy from
+ *
+ * \retval     valid lov_user_md pointer on success
+ * \retval     NULL if memory allocation fails
+ */
+static struct lov_user_md *
+llapi_layout_to_lum(const struct llapi_layout *layout)
+{
+       struct lov_user_md *lum;
+       size_t lum_size;
+       uint32_t magic = LOV_USER_MAGIC_V1;
+
+       if (strlen(layout->llot_pool_name) != 0)
+               magic = LOV_USER_MAGIC_V3;
+
+       /* The lum->lmm_objects array won't be
+        * sent to the kernel when we write the lum, so
+        * we don't allocate storage for it.
+        */
+       lum_size = lov_user_md_size(0, magic);
+       lum = malloc(lum_size);
+       if (lum == NULL)
+               return NULL;
+
+       lum->lmm_magic = magic;
+
+       if (layout->llot_pattern == LLAPI_LAYOUT_DEFAULT)
+               lum->lmm_pattern = 0;
+       else if (layout->llot_pattern == LLAPI_LAYOUT_RAID0)
+               lum->lmm_pattern = 1;
+       else
+               lum->lmm_pattern = layout->llot_pattern;
+
+       if (layout->llot_stripe_size == LLAPI_LAYOUT_DEFAULT)
+               lum->lmm_stripe_size = 0;
+       else
+               lum->lmm_stripe_size = layout->llot_stripe_size;
+
+       if (layout->llot_stripe_count == LLAPI_LAYOUT_DEFAULT)
+               lum->lmm_stripe_count = 0;
+       else if (layout->llot_stripe_count == LLAPI_LAYOUT_WIDE)
+               lum->lmm_stripe_count = -1;
+       else
+               lum->lmm_stripe_count = layout->llot_stripe_count;
+
+       if (layout->llot_stripe_offset == LLAPI_LAYOUT_DEFAULT)
+               lum->lmm_stripe_offset = -1;
+       else
+               lum->lmm_stripe_offset = layout->llot_stripe_offset;
+
+       if (lum->lmm_magic != LOV_USER_MAGIC_V1) {
+               struct lov_user_md_v3 *lumv3 = (struct lov_user_md_v3 *)lum;
+
+               strncpy(lumv3->lmm_pool_name, layout->llot_pool_name,
+                       sizeof(lumv3->lmm_pool_name));
+       }
+
+       return lum;
+}
+
+/**
+ * Get the parent directory of a path.
+ *
+ * \param[in] path     path to get parent of
+ * \param[out] buf     buffer in which to store parent path
+ * \param[in] size     size in bytes of buffer \a buf
+ */
+static void get_parent_dir(const char *path, char *buf, size_t size)
+{
+       char *p;
+
+       strncpy(buf, path, size);
+       p = strrchr(buf, '/');
+
+       if (p != NULL)
+               *p = '\0';
+       else if (size >= 2)
+               strncpy(buf, ".", 2);
+}
+
+/**
+ * Substitute unspecified attribute values in \a dest with
+ * values from \a src.
+ *
+ * \param[in] src      layout to inherit values from
+ * \param[in] dest     layout to receive inherited values
+ */
+static void inherit_layout_attributes(const struct llapi_layout *src,
+                                       struct llapi_layout *dest)
+{
+       if (dest->llot_pattern == LLAPI_LAYOUT_DEFAULT)
+               dest->llot_pattern = src->llot_pattern;
+
+       if (dest->llot_stripe_size == LLAPI_LAYOUT_DEFAULT)
+               dest->llot_stripe_size = src->llot_stripe_size;
+
+       if (dest->llot_stripe_count == LLAPI_LAYOUT_DEFAULT)
+               dest->llot_stripe_count = src->llot_stripe_count;
+}
+
+/**
+ * Test if all attributes of \a layout are specified.
+ *
+ * \param[in] layout   the layout to check
+ *
+ * \retval true                all attributes are specified
+ * \retval false       at least one attribute is unspecified
+ */
+static bool is_fully_specified(const struct llapi_layout *layout)
+{
+       return  layout->llot_pattern != LLAPI_LAYOUT_DEFAULT &&
+               layout->llot_stripe_size != LLAPI_LAYOUT_DEFAULT &&
+               layout->llot_stripe_count != LLAPI_LAYOUT_DEFAULT;
+}
+
+/**
+ * Allocate and initialize a new layout.
+ *
+ * \retval     valid llapi_layout pointer on success
+ * \retval     NULL if memory allocation fails
+ */
+struct llapi_layout *llapi_layout_alloc(void)
+{
+       struct llapi_layout *layout;
+
+       layout = __llapi_layout_alloc(0);
+       if (layout == NULL)
+               return layout;
+
+       /* Set defaults. */
+       layout->llot_magic = LLAPI_LAYOUT_MAGIC;
+       layout->llot_pattern = LLAPI_LAYOUT_DEFAULT;
+       layout->llot_stripe_size = LLAPI_LAYOUT_DEFAULT;
+       layout->llot_stripe_count = LLAPI_LAYOUT_DEFAULT;
+       layout->llot_stripe_offset = LLAPI_LAYOUT_DEFAULT;
+       layout->llot_objects_are_valid = false;
+       layout->llot_pool_name[0] = '\0';
+
+       return layout;
+}
+
+/**
+ * Check if the given \a lum_size is large enough to hold the required
+ * fields in \a lum.
+ *
+ * \param[in] lum      the struct lov_user_md to check
+ * \param[in] lum_size the number of bytes in \a lum
+ *
+ * \retval true                the \a lum_size is too small
+ * \retval false       the \a lum_size is large enough
+ */
+static bool llapi_layout_lum_truncated(struct lov_user_md *lum, size_t lum_size)
+{
+       uint32_t magic;
+
+       if (lum_size < lov_user_md_size(0, LOV_MAGIC_V1))
+               return false;
+
+       if (lum->lmm_magic == __swab32(LOV_MAGIC_V1) ||
+           lum->lmm_magic == __swab32(LOV_MAGIC_V3))
+               magic = __swab32(lum->lmm_magic);
+       else
+               magic = lum->lmm_magic;
+
+       return lum_size < lov_user_md_size(0, magic);
+}
+
+/**
+ * Compute the number of elements in the lmm_objects array of \a lum
+ * with size \a lum_size.
+ *
+ * \param[in] lum      the struct lov_user_md to check
+ * \param[in] lum_size the number of bytes in \a lum
+ *
+ * \retval             number of elements in array lum->lmm_objects
+ */
+static int llapi_layout_objects_in_lum(struct lov_user_md *lum, size_t lum_size)
+{
+       uint32_t magic;
+       size_t base_size;
+
+       if (lum_size < lov_user_md_size(0, LOV_MAGIC_V1))
+               return 0;
+
+       if (lum->lmm_magic == __swab32(LOV_MAGIC_V1) ||
+           lum->lmm_magic == __swab32(LOV_MAGIC_V3))
+               magic = __swab32(lum->lmm_magic);
+       else
+               magic = lum->lmm_magic;
+
+       base_size = lov_user_md_size(0, magic);
+
+       if (lum_size <= base_size)
+               return 0;
+       else
+               return (lum_size - base_size) / sizeof(lum->lmm_objects[0]);
+}
+
+/**
+ * Get the striping layout for the file referenced by file descriptor \a fd.
+ *
+ * If the filesystem does not support the "lustre." xattr namespace, the
+ * file must be on a non-Lustre filesystem, so set errno to ENOTTY per
+ * convention.  If the file has no "lustre.lov" data, the file will
+ * inherit default values, so return a default layout.
+ *
+ * If the kernel gives us back less than the expected amount of data,
+ * we fail with errno set to EINTR.
+ *
+ * \param[in] fd       open file descriptor
+ * \param[in] flags    open file descriptor
+ *
+ * \retval     valid llapi_layout pointer on success
+ * \retval     NULL if an error occurs
+ */
+struct llapi_layout *llapi_layout_get_by_fd(int fd, uint32_t flags)
+{
+       size_t lum_len;
+       struct lov_user_md *lum;
+       struct llapi_layout *layout = NULL;
+       ssize_t bytes_read;
+       int object_count;
+       struct stat st;
+
+       lum_len = XATTR_SIZE_MAX;
+       lum = malloc(lum_len);
+       if (lum == NULL)
+               return NULL;
+
+       bytes_read = fgetxattr(fd, XATTR_LUSTRE_LOV, lum, lum_len);
+       if (bytes_read < 0) {
+               if (errno == EOPNOTSUPP)
+                       errno = ENOTTY;
+               else if (errno == ENODATA)
+                       layout = llapi_layout_alloc();
+               goto out;
+       }
+
+       /* Return an error if we got back a partial layout. */
+       if (llapi_layout_lum_truncated(lum, bytes_read)) {
+               errno = EINTR;
+               goto out;
+       }
+
+       object_count = llapi_layout_objects_in_lum(lum, bytes_read);
+
+       /* Directories may have a positive non-zero lum->lmm_stripe_count
+        * yet have an empty lum->lmm_objects array. For non-directories the
+        * amount of data returned from the kernel must be consistent
+        * with the stripe count. */
+       if (fstat(fd, &st) < 0)
+               goto out;
+
+       if (!S_ISDIR(st.st_mode) && object_count != lum->lmm_stripe_count) {
+               errno = EINTR;
+               goto out;
+       }
+
+       if (lum->lmm_magic == __swab32(LOV_MAGIC_V1) ||
+           lum->lmm_magic == __swab32(LOV_MAGIC_V3))
+               llapi_layout_swab_lov_user_md(lum, object_count);
+
+       layout = llapi_layout_from_lum(lum, object_count);
+
+out:
+       free(lum);
+       return layout;
+}
+
+/**
+ * Get the expected striping layout for a file at \a path.
+ *
+ * Substitute expected inherited attribute values for unspecified
+ * attributes.  Unspecified attributes may belong to directories and
+ * never-written-to files, and indicate that default values will be
+ * assigned when files are created or first written to.  A default value
+ * is inherited from the parent directory if the attribute is specified
+ * there, otherwise it is inherited from the filesystem root.
+ * Unspecified attributes normally have the value LLAPI_LAYOUT_DEFAULT.
+ *
+ * The complete \a path need not refer to an existing file or directory,
+ * but some leading portion of it must reside within a lustre filesystem.
+ * A use case for this interface would be to obtain the literal striping
+ * values that would be assigned to a new file in a given directory.
+ *
+ * \param[in] path     path for which to get the expected layout
+ *
+ * \retval     valid llapi_layout pointer on success
+ * \retval     NULL if an error occurs
+ */
+static struct llapi_layout *llapi_layout_expected(const char *path)
+{
+       struct llapi_layout     *path_layout = NULL;
+       struct llapi_layout     *donor_layout;
+       char                    donor_path[PATH_MAX];
+       struct stat st;
+       int fd;
+       int rc;
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0 && errno != ENOENT)
+               return NULL;
+
+       if (fd >= 0) {
+               int tmp;
+
+               path_layout = llapi_layout_get_by_fd(fd, 0);
+               tmp = errno;
+               close(fd);
+               errno = tmp;
+       }
+
+       if (path_layout == NULL) {
+               if (errno != ENODATA && errno != ENOENT)
+                       return NULL;
+
+               path_layout = llapi_layout_alloc();
+               if (path_layout == NULL)
+                       return NULL;
+       }
+
+       if (is_fully_specified(path_layout))
+               return path_layout;
+
+       rc = stat(path, &st);
+       if (rc < 0 && errno != ENOENT) {
+               llapi_layout_free(path_layout);
+               return NULL;
+       }
+
+       /* If path is a not a directory or doesn't exist, inherit unspecified
+        * attributes from parent directory. */
+       if ((rc == 0 && !S_ISDIR(st.st_mode)) ||
+           (rc < 0 && errno == ENOENT)) {
+               get_parent_dir(path, donor_path, sizeof(donor_path));
+               donor_layout = llapi_layout_get_by_path(donor_path, 0);
+               if (donor_layout != NULL) {
+                       inherit_layout_attributes(donor_layout, path_layout);
+                       llapi_layout_free(donor_layout);
+                       if (is_fully_specified(path_layout))
+                               return path_layout;
+               }
+       }
+
+       /* Inherit remaining unspecified attributes from the filesystem root. */
+       rc = llapi_search_mounts(path, 0, donor_path, NULL);
+       if (rc < 0) {
+               llapi_layout_free(path_layout);
+               return NULL;
+       }
+       donor_layout = llapi_layout_get_by_path(donor_path, 0);
+       if (donor_layout == NULL) {
+               llapi_layout_free(path_layout);
+               return NULL;
+       }
+
+       inherit_layout_attributes(donor_layout, path_layout);
+       llapi_layout_free(donor_layout);
+
+       return path_layout;
+}
+
+/**
+ * Get the striping layout for the file at \a path.
+ *
+ * If \a flags contains LAYOUT_GET_EXPECTED, substitute
+ * expected inherited attribute values for unspecified attributes. See
+ * llapi_layout_expected().
+ *
+ * \param[in] path     path for which to get the layout
+ * \param[in] flags    flags to control how layout is retrieved
+ *
+ * \retval     valid llapi_layout pointer on success
+ * \retval     NULL if an error occurs
+ */
+struct llapi_layout *llapi_layout_get_by_path(const char *path, uint32_t flags)
+{
+       struct llapi_layout *layout = NULL;
+       int fd;
+       int tmp;
+
+       if (flags & LAYOUT_GET_EXPECTED)
+               return llapi_layout_expected(path);
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               return layout;
+
+       layout = llapi_layout_get_by_fd(fd, flags);
+       tmp = errno;
+       close(fd);
+       errno = tmp;
+
+       return layout;
+}
+
+/**
+ * Get the layout for the file with FID \a fidstr in filesystem \a lustre_dir.
+ *
+ * \param[in] lustre_dir       path within Lustre filesystem containing \a fid
+ * \param[in] fid              Lustre identifier of file to get layout for
+ *
+ * \retval     valid llapi_layout pointer on success
+ * \retval     NULL if an error occurs
+ */
+struct llapi_layout *llapi_layout_get_by_fid(const char *lustre_dir,
+                                            const lustre_fid *fid,
+                                            uint32_t flags)
+{
+       int fd;
+       int tmp;
+       int saved_msg_level = llapi_msg_get_level();
+       struct llapi_layout *layout = NULL;
+
+       /* Prevent llapi internal routines from writing to console
+        * while executing this function, then restore previous message
+        * level. */
+       llapi_msg_set_level(LLAPI_MSG_OFF);
+       fd = llapi_open_by_fid(lustre_dir, fid, O_RDONLY);
+       llapi_msg_set_level(saved_msg_level);
+
+       if (fd < 0)
+               return NULL;
+
+       layout = llapi_layout_get_by_fd(fd, flags);
+       tmp = errno;
+       close(fd);
+       errno = tmp;
+
+       return layout;
+}
+
+/** * Free memory allocated for \a layout. */
+void llapi_layout_free(struct llapi_layout *layout)
+{
+       free(layout);
+}
+
+/**
+ * Get the stripe count of \a layout.
+ *
+ * \param[in] layout   layout to get stripe count from
+ * \param[out] count   integer to store stripe count in
+ *
+ * \retval     0 on success
+ * \retval     -1 if arguments are invalid
+ */
+int llapi_layout_stripe_count_get(const struct llapi_layout *layout,
+                                 uint64_t *count)
+{
+       if (layout == NULL || count == NULL ||
+           layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
+               errno = EINVAL;
+               return -1;
+       }
+       *count = layout->llot_stripe_count;
+       return 0;
+}
+
+/*
+ * The llapi_layout API functions have these extra validity checks since
+ * they use intuitively named macros to denote special behavior, whereas
+ * the old API uses 0 and -1.
+ */
+
+static bool llapi_layout_stripe_count_is_valid(int64_t stripe_count)
+{
+       return stripe_count == LLAPI_LAYOUT_DEFAULT ||
+               stripe_count == LLAPI_LAYOUT_WIDE ||
+               (stripe_count != 0 && stripe_count != -1 &&
+                llapi_stripe_count_is_valid(stripe_count));
+}
+
+static bool llapi_layout_stripe_size_is_valid(uint64_t stripe_size)
+{
+       return stripe_size == LLAPI_LAYOUT_DEFAULT ||
+               (stripe_size != 0 &&
+                llapi_stripe_size_is_aligned(stripe_size) &&
+                !llapi_stripe_size_is_too_big(stripe_size));
+}
+
+static bool llapi_layout_stripe_index_is_valid(int64_t stripe_index)
+{
+       return stripe_index == LLAPI_LAYOUT_DEFAULT ||
+               (stripe_index >= 0 &&
+               llapi_stripe_index_is_valid(stripe_index));
+}
+
+/**
+ * Set the stripe count of \a layout.
+ *
+ * \param[in] layout   layout to set stripe count in
+ * \param[in] count    value to be set
+ *
+ * \retval     0 on success
+ * \retval     -1 if arguments are invalid
+ */
+int llapi_layout_stripe_count_set(struct llapi_layout *layout,
+                                 uint64_t count)
+{
+       if (layout == NULL || layout->llot_magic != LLAPI_LAYOUT_MAGIC ||
+           !llapi_layout_stripe_count_is_valid(count)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       layout->llot_stripe_count = count;
+
+       return 0;
+}
+
+/**
+ * Get the stripe size of \a layout.
+ *
+ * \param[in] layout   layout to get stripe size from
+ * \param[out] size    integer to store stripe size in
+ *
+ * \retval     0 on success
+ * \retval     -1 if arguments are invalid
+ */
+int llapi_layout_stripe_size_get(const struct llapi_layout *layout,
+                                uint64_t *size)
+{
+       if (layout == NULL || size == NULL ||
+           layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       *size = layout->llot_stripe_size;
+
+       return 0;
+}
+
+/**
+ * Set the stripe size of \a layout.
+ *
+ * \param[in] layout   layout to set stripe size in
+ * \param[in] size     value to be set
+ *
+ * \retval     0 on success
+ * \retval     -1 if arguments are invalid
+ */
+int llapi_layout_stripe_size_set(struct llapi_layout *layout,
+                                uint64_t size)
+{
+       if (layout == NULL || layout->llot_magic != LLAPI_LAYOUT_MAGIC ||
+           !llapi_layout_stripe_size_is_valid(size)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       layout->llot_stripe_size = size;
+
+       return 0;
+}
+
+/**
+ * Get the RAID pattern of \a layout.
+ *
+ * \param[in] layout   layout to get pattern from
+ * \param[out] pattern integer to store pattern in
+ *
+ * \retval     0 on success
+ * \retval     -1 if arguments are invalid
+ */
+int llapi_layout_pattern_get(const struct llapi_layout *layout,
+                            uint64_t *pattern)
+{
+       if (layout == NULL || pattern == NULL ||
+           layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       *pattern = layout->llot_pattern;
+
+       return 0;
+}
+
+/**
+ * Set the RAID pattern of \a layout.
+ *
+ * \param[in] layout   layout to set pattern in
+ * \param[in] pattern  value to be set
+ *
+ * \retval     0 on success
+ * \retval     -1 if arguments are invalid or RAID pattern
+ *             is unsupported
+ */
+int llapi_layout_pattern_set(struct llapi_layout *layout, uint64_t pattern)
+{
+       if (layout == NULL || layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (pattern != LLAPI_LAYOUT_DEFAULT ||
+           pattern != LLAPI_LAYOUT_RAID0) {
+               errno = EOPNOTSUPP;
+               return -1;
+       }
+
+       layout->llot_pattern = pattern;
+
+       return 0;
+}
+
+/**
+ * Set the OST index of stripe number \a stripe_number to \a ost_index.
+ *
+ * The index may only be set for stripe number 0 for now.
+ *
+ * \param[in] layout           layout to set OST index in
+ * \param[in] stripe_number    stripe number to set index for
+ * \param[in] ost_index                the index to set
+ *
+ * \retval     0 on success
+ * \retval     -1 if arguments are invalid or an unsupported stripe number
+ *             was specified
+ */
+int llapi_layout_ost_index_set(struct llapi_layout *layout, int stripe_number,
+                              uint64_t ost_index)
+{
+       if (layout == NULL || layout->llot_magic != LLAPI_LAYOUT_MAGIC ||
+           !llapi_layout_stripe_index_is_valid(ost_index)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (stripe_number != 0) {
+               errno = EOPNOTSUPP;
+               return -1;
+       }
+
+       layout->llot_stripe_offset = ost_index;
+
+       return 0;
+}
+
+/**
+ * Get the OST index associated with stripe \a stripe_number.
+ *
+ * Stripes are indexed starting from zero.
+ *
+ * \param[in] layout           layout to get index from
+ * \param[in] stripe_number    stripe number to get index for
+ * \param[out] index           integer to store index in
+ *
+ * \retval     0 on success
+ * \retval     -1 if arguments are invalid
+ */
+int llapi_layout_ost_index_get(const struct llapi_layout *layout,
+                              uint64_t stripe_number, uint64_t *index)
+{
+       if (layout == NULL || layout->llot_magic != LLAPI_LAYOUT_MAGIC ||
+           stripe_number >= layout->llot_stripe_count ||
+           index == NULL  || layout->llot_objects_are_valid == 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (layout->llot_objects[stripe_number].l_ost_idx == -1)
+               *index = LLAPI_LAYOUT_DEFAULT;
+       else
+               *index = layout->llot_objects[stripe_number].l_ost_idx;
+
+       return 0;
+}
+
+/**
+ *
+ * Get the pool name of layout \a layout.
+ *
+ * \param[in] layout   layout to get pool name from
+ * \param[out] dest    buffer to store pool name in
+ * \param[in] n                size in bytes of buffer \a dest
+ *
+ * \retval     0 on success
+ * \retval     -1 if arguments are invalid
+ */
+int llapi_layout_pool_name_get(const struct llapi_layout *layout, char *dest,
+                              size_t n)
+{
+       if (layout == NULL || layout->llot_magic != LLAPI_LAYOUT_MAGIC ||
+           dest == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       strncpy(dest, layout->llot_pool_name, n);
+
+       return 0;
+}
+
+/**
+ * Set the name of the pool of layout \a layout.
+ *
+ * \param[in] layout   layout to set pool name in
+ * \param[in] pool_name        pool name to set
+ *
+ * \retval     0 on success
+ * \retval     -1 if arguments are invalid or pool name is too long
+ */
+int llapi_layout_pool_name_set(struct llapi_layout *layout,
+                              const char *pool_name)
+{
+       char *ptr;
+
+       if (layout == NULL || layout->llot_magic != LLAPI_LAYOUT_MAGIC ||
+           pool_name == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       /* Strip off any 'fsname.' portion. */
+       ptr = strchr(pool_name, '.');
+       if (ptr != NULL)
+               pool_name = ptr + 1;
+
+       if (strlen(pool_name) > LOV_MAXPOOLNAME) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       strncpy(layout->llot_pool_name, pool_name,
+               sizeof(layout->llot_pool_name));
+
+       return 0;
+}
+
+/**
+ * Open and possibly create a file with a given \a layout.
+ *
+ * If \a layout is NULL this function acts as a simple wrapper for
+ * open().  By convention, ENOTTY is returned in errno if \a path
+ * refers to a non-Lustre file.
+ *
+ * \param[in] path             name of the file to open
+ * \param[in] open_flags       open() flags
+ * \param[in] mode             permissions to create new file with
+ * \param[in] layout           layout to create new file with
+ *
+ * \retval             non-negative file descriptor on successful open
+ * \retval             -1 if an error occurred
+ */
+int llapi_layout_file_open(const char *path, int open_flags, mode_t mode,
+                          const struct llapi_layout *layout)
+{
+       int fd;
+       int rc;
+       int tmp;
+       struct lov_user_md *lum;
+       size_t lum_size;
+
+       if (path == NULL ||
+           (layout != NULL && layout->llot_magic != LLAPI_LAYOUT_MAGIC)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       /* Object creation must be postponed until after layout attributes
+        * have been applied. */
+       if (layout != NULL && (open_flags & O_CREAT))
+               open_flags |= O_LOV_DELAY_CREATE;
+
+       fd = open(path, open_flags, mode);
+
+       if (layout == NULL || fd < 0)
+               return fd;
+
+       lum = llapi_layout_to_lum(layout);
+
+       if (lum == NULL) {
+               tmp = errno;
+               close(fd);
+               errno = tmp;
+               return -1;
+       }
+
+       lum_size = lov_user_md_size(0, lum->lmm_magic);
+
+       rc = fsetxattr(fd, XATTR_LUSTRE_LOV, lum, lum_size, 0);
+       if (rc < 0) {
+               tmp = errno;
+               close(fd);
+               errno = tmp;
+               fd = -1;
+       }
+
+       free(lum);
+       errno = errno == EOPNOTSUPP ? ENOTTY : errno;
+
+       return fd;
+}
+
+/**
+ * Create a file with a given \a layout.
+ *
+ * Force O_CREAT and O_EXCL flags on so caller is assured that file was
+ * created with the given \a layout on successful function return.
+ *
+ * \param[in] path             name of the file to open
+ * \param[in] open_flags       open() flags
+ * \param[in] mode             permissions to create new file with
+ * \param[in] layout           layout to create new file with
+ *
+ * \retval             non-negative file descriptor on successful open
+ * \retval             -1 if an error occurred
+ */
+int llapi_layout_file_create(const char *path, int open_flags, int mode,
+                            const struct llapi_layout *layout)
+{
+       return llapi_layout_file_open(path, open_flags|O_CREAT|O_EXCL, mode,
+                                     layout);
+}
index 207a5a0..646d440 100644 (file)
@@ -46,4 +46,32 @@ int root_ioctl(const char *mdtname, int opc, void *data, int *mdtidxp,
 int get_param(const char *param_path, char *result,
              unsigned int result_size);
 
+#define LLAPI_LAYOUT_MAGIC 0x11AD1107 /* LLAPILOT */
+
+/* Helper functions for testing validity of stripe attributes. */
+
+static inline bool llapi_stripe_size_is_aligned(uint64_t size)
+{
+       return (size & (LOV_MIN_STRIPE_SIZE - 1)) == 0;
+}
+
+static inline bool llapi_stripe_size_is_too_big(uint64_t size)
+{
+       return size >= (1ULL << 32);
+}
+
+static inline bool llapi_stripe_count_is_valid(int64_t count)
+{
+       return count >= -1 && count <= LOV_MAX_STRIPE_COUNT;
+}
+
+static inline bool llapi_stripe_index_is_valid(int64_t index)
+{
+       return index >= -1 && index <= LOV_V1_INSANE_STRIPE_COUNT;
+}
+
+/* Compatibility macro for legacy llapi functions that use "offset"
+ * terminology instead of the preferred "index". */
+#define llapi_stripe_offset_is_valid(os) llapi_stripe_index_is_valid(os)
+
 #endif /* _LUSTREAPI_INTERNAL_H_ */