From 3d3a37c9c8daefb18fe7b9c2e83d8d2ef2cdf98b Mon Sep 17 00:00:00 2001 From: Ned Bass Date: Mon, 7 Jul 2014 17:51:37 -0400 Subject: [PATCH] LU-2182 llapi: implementation of new llapi_layout API 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 Signed-off-by: James Simmons Change-Id: I35fb51055b6438ef3090f43c28a4083a66eaa907 Reviewed-on: http://review.whamcloud.com/5302 Tested-by: Jenkins Tested-by: Maloo Reviewed-by: John L. Hammond Reviewed-by: Jinshan Xiong Reviewed-by: Oleg Drokin --- lustre/doc/Makefile.am | 11 +- lustre/doc/llapi_layout.7 | 124 +++ lustre/doc/llapi_layout_alloc.3 | 64 ++ lustre/doc/llapi_layout_file_create.3 | 102 +++ lustre/doc/llapi_layout_file_open.3 | 1 + lustre/doc/llapi_layout_free.3 | 1 + lustre/doc/llapi_layout_get_by_fd.3 | 142 +++ lustre/doc/llapi_layout_get_by_fid.3 | 1 + lustre/doc/llapi_layout_get_by_path.3 | 1 + lustre/doc/llapi_layout_ost_index_get.3 | 69 ++ lustre/doc/llapi_layout_ost_index_set.3 | 1 + lustre/doc/llapi_layout_pattern_get.3 | 61 ++ lustre/doc/llapi_layout_pattern_set.3 | 1 + lustre/doc/llapi_layout_pool_name_get.3 | 74 ++ lustre/doc/llapi_layout_pool_name_set.3 | 1 + lustre/doc/llapi_layout_stripe_count_get.3 | 57 ++ lustre/doc/llapi_layout_stripe_count_set.3 | 1 + lustre/doc/llapi_layout_stripe_size_get.3 | 51 ++ lustre/doc/llapi_layout_stripe_size_set.3 | 1 + lustre/doc/llapi_path2fid.3 | 42 + lustre/doc/lustreapi.7 | 1 + lustre/include/lustre/lustre_idl.h | 1 - lustre/include/lustre/lustreapi.h | 271 +++++- lustre/tests/.gitignore | 1 + lustre/tests/Makefile.am | 3 +- lustre/tests/llapi_layout_test.c | 1353 ++++++++++++++++++++++++++++ lustre/tests/sanity.sh | 263 +----- lustre/tests/test-framework.sh | 244 +++++ lustre/utils/LCOPYING | 3 +- lustre/utils/Makefile.am | 4 +- lustre/utils/liblustreapi.c | 39 +- lustre/utils/liblustreapi_layout.c | 998 ++++++++++++++++++++ lustre/utils/lustreapi_internal.h | 28 + 33 files changed, 3758 insertions(+), 257 deletions(-) create mode 100644 lustre/doc/llapi_layout.7 create mode 100644 lustre/doc/llapi_layout_alloc.3 create mode 100644 lustre/doc/llapi_layout_file_create.3 create mode 100644 lustre/doc/llapi_layout_file_open.3 create mode 100644 lustre/doc/llapi_layout_free.3 create mode 100644 lustre/doc/llapi_layout_get_by_fd.3 create mode 100644 lustre/doc/llapi_layout_get_by_fid.3 create mode 100644 lustre/doc/llapi_layout_get_by_path.3 create mode 100644 lustre/doc/llapi_layout_ost_index_get.3 create mode 100644 lustre/doc/llapi_layout_ost_index_set.3 create mode 100644 lustre/doc/llapi_layout_pattern_get.3 create mode 100644 lustre/doc/llapi_layout_pattern_set.3 create mode 100644 lustre/doc/llapi_layout_pool_name_get.3 create mode 100644 lustre/doc/llapi_layout_pool_name_set.3 create mode 100644 lustre/doc/llapi_layout_stripe_count_get.3 create mode 100644 lustre/doc/llapi_layout_stripe_count_set.3 create mode 100644 lustre/doc/llapi_layout_stripe_size_get.3 create mode 100644 lustre/doc/llapi_layout_stripe_size_set.3 create mode 100644 lustre/doc/llapi_path2fid.3 create mode 100644 lustre/tests/llapi_layout_test.c create mode 100644 lustre/utils/liblustreapi_layout.c diff --git a/lustre/doc/Makefile.am b/lustre/doc/Makefile.am index 3dd6a9a..ad96c11 100644 --- a/lustre/doc/Makefile.am +++ b/lustre/doc/Makefile.am @@ -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 index 0000000..045d742 --- /dev/null +++ b/lustre/doc/llapi_layout.7 @@ -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 +.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 +#include +#include +#include +#include +#include + +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 index 0000000..571ba55 --- /dev/null +++ b/lustre/doc/llapi_layout_alloc.3 @@ -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 +.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 index 0000000..7f828f8 --- /dev/null +++ b/lustre/doc/llapi_layout_file_create.3 @@ -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 +.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 index 0000000..3ae8ecd --- /dev/null +++ b/lustre/doc/llapi_layout_file_open.3 @@ -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 index 0000000..8543f03 --- /dev/null +++ b/lustre/doc/llapi_layout_free.3 @@ -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 index 0000000..a29e9b0 --- /dev/null +++ b/lustre/doc/llapi_layout_get_by_fd.3 @@ -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 +.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 index 0000000..5a4e926 --- /dev/null +++ b/lustre/doc/llapi_layout_get_by_fid.3 @@ -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 index 0000000..5a4e926 --- /dev/null +++ b/lustre/doc/llapi_layout_get_by_path.3 @@ -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 index 0000000..d8742d8 --- /dev/null +++ b/lustre/doc/llapi_layout_ost_index_get.3 @@ -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 +.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 index 0000000..513047c --- /dev/null +++ b/lustre/doc/llapi_layout_ost_index_set.3 @@ -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 index 0000000..e446aa7 --- /dev/null +++ b/lustre/doc/llapi_layout_pattern_get.3 @@ -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 +.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 index 0000000..d54a443 --- /dev/null +++ b/lustre/doc/llapi_layout_pattern_set.3 @@ -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 index 0000000..a315d12 --- /dev/null +++ b/lustre/doc/llapi_layout_pool_name_get.3 @@ -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 +.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 index 0000000..b06a0f2 --- /dev/null +++ b/lustre/doc/llapi_layout_pool_name_set.3 @@ -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 index 0000000..f22e663 --- /dev/null +++ b/lustre/doc/llapi_layout_stripe_count_get.3 @@ -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 +.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 index 0000000..52bf166 --- /dev/null +++ b/lustre/doc/llapi_layout_stripe_count_set.3 @@ -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 index 0000000..6e634d0 --- /dev/null +++ b/lustre/doc/llapi_layout_stripe_size_get.3 @@ -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 +.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 index 0000000..dccaf4a --- /dev/null +++ b/lustre/doc/llapi_layout_stripe_size_set.3 @@ -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 index 0000000..35cf9ab --- /dev/null +++ b/lustre/doc/llapi_path2fid.3 @@ -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 +.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) diff --git a/lustre/doc/lustreapi.7 b/lustre/doc/lustreapi.7 index b3fc90a..17eb982 100644 --- a/lustre/doc/lustreapi.7 +++ b/lustre/doc/lustreapi.7 @@ -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) diff --git a/lustre/include/lustre/lustre_idl.h b/lustre/include/lustre/lustre_idl.h index 56f6cfb..3507a37 100644 --- a/lustre/include/lustre/lustre_idl.h +++ b/lustre/include/lustre/lustre_idl.h @@ -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" diff --git a/lustre/include/lustre/lustreapi.h b/lustre/include/lustre/lustreapi.h index 40dd522..5cccbf4 100644 --- a/lustre/include/lustre/lustreapi.h +++ b/lustre/include/lustre/lustreapi.h @@ -43,6 +43,7 @@ */ #include +#include #include 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 diff --git a/lustre/tests/.gitignore b/lustre/tests/.gitignore index bccb334..795dade 100644 --- a/lustre/tests/.gitignore +++ b/lustre/tests/.gitignore @@ -10,6 +10,7 @@ /cmknod /copy_attr /copytool +/llapi_layout_test /createdestroy /createmany /createtest diff --git a/lustre/tests/Makefile.am b/lustre/tests/Makefile.am index 7f6c3a4..e75b2b6 100644 --- a/lustre/tests/Makefile.am +++ b/lustre/tests/Makefile.am @@ -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 index 0000000..98a812a --- /dev/null +++ b/lustre/tests/llapi_layout_test.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh index fe5e72d..d13d7b4 100644 --- a/lustre/tests/sanity.sh +++ b/lustre/tests/sanity.sh @@ -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 diff --git a/lustre/tests/test-framework.sh b/lustre/tests/test-framework.sh index 3df2e4c..95b4c1b 100755 --- a/lustre/tests/test-framework.sh +++ b/lustre/tests/test-framework.sh @@ -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 +} diff --git a/lustre/utils/LCOPYING b/lustre/utils/LCOPYING index 542b54b..f63120d 100644 --- a/lustre/utils/LCOPYING +++ b/lustre/utils/LCOPYING @@ -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 diff --git a/lustre/utils/Makefile.am b/lustre/utils/Makefile.am index 1c1bb4c..f56f1f7 100644 --- a/lustre/utils/Makefile.am +++ b/lustre/utils/Makefile.am @@ -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 diff --git a/lustre/utils/liblustreapi.c b/lustre/utils/liblustreapi.c index cc3c97e..2cf1001 100644 --- a/lustre/utils/liblustreapi.c +++ b/lustre/utils/liblustreapi.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -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 index 0000000..eff35e2 --- /dev/null +++ b/lustre/utils/liblustreapi_layout.c @@ -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 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#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); +} diff --git a/lustre/utils/lustreapi_internal.h b/lustre/utils/lustreapi_internal.h index 207a5a0..646d440 100644 --- a/lustre/utils/lustreapi_internal.h +++ b/lustre/utils/lustreapi_internal.h @@ -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_ */ -- 1.8.3.1