--- /dev/null
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <linux/loop.h>
+
+/* LO_FLAGS_BLOCKSIZE is an enum so we can't ifdef. */
+#define LO_FLAGS_BLOCKSIZE_ 32
+
+#ifndef LO_INFO_BLOCKSIZE
+#define LO_INFO_BLOCKSIZE(l) (l)->lo_init[0]
+#endif
+
+int main(int argc, char **argv)
+{
+ struct loop_info64 lo;
+ unsigned long long blksize;
+ char *end;
+ int fd = -1;
+ bool set = argc >= 3;
+ int status = EXIT_FAILURE;
+
+ if (argc != 2 && argc != 3) {
+ fprintf(stderr, "usage: %s DEV [BLKSIZE]\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ if (set) {
+ errno = 0;
+ blksize = strtoull(argv[2], &end, 0);
+ if (errno || *end) {
+ fprintf(stderr, "invalid block size\n");
+ return EXIT_FAILURE;
+ }
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd == -1) {
+ perror("open");
+ return EXIT_FAILURE;
+ }
+
+ if (ioctl(fd, LOOP_GET_STATUS64, &lo) == -1) {
+ perror("LOOP_GET_STATUS64");
+ goto out;
+ }
+
+ if (!(lo.lo_flags & LO_FLAGS_BLOCKSIZE_)) {
+ fprintf(stderr, "LO_FLAGS_BLOCKSIZE not supported");
+ goto out;
+ }
+
+ if (set) {
+ lo.lo_flags |= LO_FLAGS_BLOCKSIZE_;
+ LO_INFO_BLOCKSIZE(&lo) = blksize;
+ if (ioctl(fd, LOOP_SET_STATUS64, &lo) == -1) {
+ perror("LOOP_SET_STATUS64");
+ goto out;
+ }
+ } else {
+ printf("%llu\n", LO_INFO_BLOCKSIZE(&lo));
+ }
+
+ status = EXIT_SUCCESS;
+out:
+ close(fd);
+ return status;
+}
--- /dev/null
+#!/bin/bash
+#
+# Tests the LO_FLAGS_BLOCKSIZE flag for LOOP_{GET,SET}_STATUS, introduced in
+# commit f2c6df7dbf9a ("loop: support 4k physical blocksize"). Also a
+# regression test for patches "loop: always return block size in
+# LOOP_GET_STATUS" and "loop: fix hang if LOOP_SET_STATUS gets invalid
+# blocksize or encrypt type".
+#
+# Copyright (C) 2017 Omar Sandoval
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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 for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+DESCRIPTION="try various loop device block sizes"
+QUICK=1
+
+requires() {
+ local tmpfile loop_dev
+
+ if ! _have_src_program loblksize; then
+ return 1
+ fi
+
+ # XXX: $TMPDIR isn't set up for requires(), should it be?
+ if ! tmpfile="$(mktemp "blktests.${TEST_NAME//\//.}.XXX")" ||
+ ! truncate -s 1M "$tmpfile"; then
+ SKIP_REASON="could not create temporary file"
+ return 1
+ fi
+
+ if ! loop_dev="$(losetup -f --show "$tmpfile" 2>/dev/null)"; then
+ SKIP_REASON="could not get loop device"
+ rm -f "$tmpfile"
+ return 1
+ fi
+
+ if ! src/loblksize "$loop_dev" >/dev/null 2>&1; then
+ SKIP_REASON="kernel does not support LO_FLAGS_BLOCKSIZE"
+ losetup -d "$loop_dev"
+ rm -f "$tmpfile"
+ return 1
+ fi
+
+ losetup -d "$loop_dev"
+ rm -f "$tmpfile"
+ return 0
+}
+
+test() {
+ local loop_dev
+ echo "Running ${TEST_NAME}"
+
+ truncate -s 1M "$TMPDIR/file"
+ if ! loop_dev="$(losetup -f --show "$TMPDIR/file")"; then
+ return 1
+ fi
+
+ for blksize in "" 4096 2048 1234 1024 512; do
+ if [[ -z $blksize ]]; then
+ echo "Checking default block size"
+ else
+ echo "Setting block size to $blksize"
+ if src/loblksize "$loop_dev" "$blksize"; then
+ dd if=/dev/zero of="$loop_dev" oflag=direct bs="$blksize" count=1 status=none
+ fi
+ fi
+ echo "LOOP_GET_STATUS says" "$(src/loblksize "$loop_dev")"
+ echo "sysfs says" "$(cat "/sys/block/${loop_dev#/dev/}/queue/logical_block_size")"
+ done
+
+ losetup -d "$loop_dev"
+
+ echo "Test complete"
+}
--- /dev/null
+Running loop/002
+Checking default block size
+LOOP_GET_STATUS says 512
+sysfs says 512
+Setting block size to 4096
+LOOP_GET_STATUS says 4096
+sysfs says 4096
+Setting block size to 2048
+LOOP_GET_STATUS says 2048
+sysfs says 2048
+Setting block size to 1234
+LOOP_SET_STATUS64: Invalid argument
+LOOP_GET_STATUS says 2048
+sysfs says 2048
+Setting block size to 1024
+LOOP_GET_STATUS says 1024
+sysfs says 1024
+Setting block size to 512
+LOOP_GET_STATUS says 512
+sysfs says 512
+Test complete