int *start_stripe)
{
struct lov_stripe_md_entry *lsme = lsm->lsm_entries[index];
- u64 local_end = fiemap->fm_extents[0].fe_logical;
+ u64 local_end;
u64 lun_end;
u64 fm_end_offset;
int stripe_no = -1;
fiemap->fm_extents[0].fe_logical == 0)
return 0;
+ local_end = fiemap->fm_extents[0].fe_logical;
stripe_no = *start_stripe;
if (stripe_no == -1)
GOTO(obj_put, rc = -EINVAL);
/* If OST is inactive, return extent with UNKNOWN flag. */
if (!lov->lov_tgts[ost_index]->ltd_active) {
- fs->fs_fm->fm_flags |= FIEMAP_EXTENT_LAST;
+
fs->fs_fm->fm_mapped_extents = 1;
+ if (fs->fs_fm->fm_extent_count == 0)
+ goto inactive_tgt;
fm_ext[0].fe_logical = obd_start;
fm_ext[0].fe_length = obd_end - obd_start + 1;
- fm_ext[0].fe_flags |= FIEMAP_EXTENT_UNKNOWN;
+ fm_ext[0].fe_flags |=
+ FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_LAST;
goto inactive_tgt;
}
* request fits in file-size.
*/
fiemap->fm_mapped_extents = 1;
+ if (fiemap->fm_extent_count == 0)
+ GOTO(out_lsm, rc = 0);
+
fiemap->fm_extents[0].fe_logical = fiemap->fm_start;
if (fiemap->fm_start + fiemap->fm_length <
fmkey->lfik_oa.o_size)
else
cur_ext = 0;
- /* done all the processing */
- if (entry > end_entry ||
- (fs.fs_enough && fs.fs_finish_stripe && entry == end_entry))
- fiemap->fm_extents[cur_ext].fe_flags |= FIEMAP_EXTENT_LAST;
-
/* Indicate that we are returning device offsets unless file just has
* single stripe */
if (lsm->lsm_entry_count > 1 ||
if (fiemap->fm_extent_count == 0)
goto skip_last_device_calc;
+ /* done all the processing */
+ if (entry > end_entry ||
+ (fs.fs_enough && fs.fs_finish_stripe && entry == end_entry))
+ fiemap->fm_extents[cur_ext].fe_flags |= FIEMAP_EXTENT_LAST;
+
skip_last_device_calc:
fiemap->fm_mapped_extents = fs.fs_cur_extent;
out_fm_local:
#include <errno.h>
#include <unistd.h>
#include <getopt.h>
+#include <pthread.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/lustre/lustre_user.h>
#define ONEMB 1048576
+static inline void print_extent_flags(unsigned int flags) {
+ if (!flags)
+ return;
+
+ printf("flags (0x%x):", flags);
+ if (flags & FIEMAP_EXTENT_LAST)
+ printf(" LAST");
+ if (flags & FIEMAP_EXTENT_UNKNOWN)
+ printf(" UNKNOWN");
+ if (flags & FIEMAP_EXTENT_DELALLOC)
+ printf(" DELALLOC");
+ if (flags & FIEMAP_EXTENT_ENCODED)
+ printf(" ENCODED");
+ if (flags & FIEMAP_EXTENT_DATA_ENCRYPTED)
+ printf(" DATA_ENCRYPTED");
+ if (flags & FIEMAP_EXTENT_NOT_ALIGNED)
+ printf(" NOT_ALIGNED");
+ if (flags & FIEMAP_EXTENT_DATA_INLINE)
+ printf(" DATA_INLINE");
+ if (flags & FIEMAP_EXTENT_DATA_TAIL)
+ printf(" DATA_TAIL");
+ if (flags & FIEMAP_EXTENT_UNWRITTEN)
+ printf(" UNWRITTEN");
+ if (flags & FIEMAP_EXTENT_MERGED)
+ printf(" MERGED");
+ if (flags & FIEMAP_EXTENT_SHARED)
+ printf(" SHARED");
+ if (flags & FIEMAP_EXTENT_NET)
+ printf(" NET");
+ printf("\n");
+}
+
+
/* This test executes fiemap ioctl and check
* a) there are no file ranges marked with FIEMAP_EXTENT_UNWRITTEN
* b) data ranges sizes sum is equal to given in second param */
-static int check_fiemap(int fd, long long orig_size)
+static int check_fiemap(int fd, long long expected_sum,
+ unsigned int *mapped_extents)
{
/* This buffer is enougth for 1MB length file */
union { struct fiemap f; char c[4096]; } fiemap_buf;
unsigned int count = (sizeof(fiemap_buf) - sizeof(*fiemap)) /
sizeof(*fm_extents);
unsigned int i = 0;
- long long file_size = 0;
+ long long ext_len_sum = 0;
memset(&fiemap_buf, 0, sizeof(fiemap_buf));
}
for (i = 0; i < fiemap->fm_mapped_extents; i++) {
- printf("extent in "
- "offset %lu, length %lu\n"
- "flags: %x\n",
- (unsigned long)fm_extents[i].fe_logical,
- (unsigned long)fm_extents[i].fe_length,
- fm_extents[i].fe_flags);
+ printf("extent %d in offset %lu, length %lu\n",
+ i, (unsigned long)fm_extents[i].fe_logical,
+ (unsigned long)fm_extents[i].fe_length);
+
+ print_extent_flags(fm_extents[i].fe_flags);
if (fm_extents[i].fe_flags & FIEMAP_EXTENT_UNWRITTEN) {
fprintf(stderr, "Unwritten extent\n");
return -2;
} else {
- file_size += fm_extents[i].fe_length;
+ ext_len_sum += fm_extents[i].fe_length;
}
}
- printf("No unwritten extents, extents number %u, "
- "file size %lli, original size %lli\n",
+ printf("No unwritten extents, extents number %u, sum of lengths %lli, expected sum %lli\n",
fiemap->fm_mapped_extents,
- file_size, orig_size);
- return file_size != orig_size;
+ ext_len_sum, expected_sum);
+
+ *mapped_extents = fiemap->fm_mapped_extents;
+ return ext_len_sum != expected_sum || (expected_sum && !*mapped_extents);
+}
+
+/**
+ * LU-17110
+ * When userspace uses fiemap with fm_extent_count=0, it means that kernelspace
+ * should return only the number of extents. So we should always check
+ * fm_extent_count before accessing to fm_extents array. Otherwise this could
+ * lead to buffer overflow and slab memory corruption.
+ */
+struct th_args {
+ int fd;
+ int iter_nbr;
+ int expected_mapped;
+};
+
+static void *corruption_th(void *args)
+{
+ int i;
+ struct th_args *ta = args;
+ struct fiemap fiemap = {
+ .fm_start = 0,
+ .fm_flags = (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_DEVICE_ORDER),
+ .fm_extent_count = 0,
+ .fm_length = FIEMAP_MAX_OFFSET };
+
+ for (i = 0; i < ta->iter_nbr; i++) {
+ if (ioctl(ta->fd, FS_IOC_FIEMAP, &fiemap) < 0) {
+ fprintf(stderr, "error while ioctl: %s\n",
+ strerror(errno));
+ return (void *) (long long) -errno;
+ }
+ if (ta->expected_mapped != fiemap.fm_mapped_extents) {
+ fprintf(stderr, "mapped extents mismatch: expected=%d, returned=%d\n",
+ ta->expected_mapped, fiemap.fm_mapped_extents);
+ return (void *) -EINVAL;
+ }
+ }
+
+ return NULL;
}
int main(int argc, char **argv)
int c;
struct option long_opts[] = {
{ .name = "test", .has_arg = no_argument, .val = 't' },
+ { .name = "corruption_test", .has_arg = no_argument, .val = 'c' },
{ .name = NULL }
};
int fd;
int rc;
+ unsigned int mapped_extents = 0;
+ bool corruption_test = false;
optind = 0;
- while ((c = getopt_long(argc, argv, "t", long_opts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "tc", long_opts, NULL)) != -1) {
switch (c) {
case 't':
return 0;
+ case 'c':
+ corruption_test = true;
+ break;
default:
fprintf(stderr, "error: %s: option '%s' unrecognized\n",
argv[0], argv[optind - 1]);
return -1;
}
- fprintf(stderr, "fd: %i\n", fd);
-
- rc = check_fiemap(fd, atoll(argv[optind + 1]));
-
+ rc = check_fiemap(fd, atoll(argv[optind + 1]), &mapped_extents);
+ if (rc)
+ goto close;
+
+ if (corruption_test) {
+ pthread_t th[200];
+ int i;
+ void *rval = NULL;
+ struct th_args args = {
+ .fd = fd,
+ .expected_mapped = mapped_extents,
+ .iter_nbr = 500
+ };
+
+ for (i = 0; i < 200; i++) {
+ rc = pthread_create(&th[i], NULL, corruption_th, &args);
+ if (rc)
+ goto close;
+ }
+ for (i = 0; i < 200; i++) {
+ rc = pthread_join(th[i], &rval);
+ if (rc || rval) {
+ rc = 1;
+ goto close;
+ }
+ }
+ }
+close:
if (close(fd) < 0)
fprintf(stderr, "closing %s, error %i", argv[optind], errno);