1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2020 FUJITSU LIMITED. All rights reserved. 4 * Copyright (c) Linux Test Project, 2022 5 * Author: Yang Xu <xuyang2018.jy@cn.jujitsu.com> 6 */ 7 8/*\ 9 * [Description] 10 * 11 * Tests ioctl() on loopdevice with LO_FLAGS_READ_ONLY (similar as losetup -r) and 12 * LOOP_CHANGE_FD flags. 13 * 14 * For LOOP_CHANGE_FD, this operation is possible only if the loop device 15 * is read-only and the new backing store is the same size and type as the 16 * old backing store. 17 * 18 * When using LOOP_CONFIGURE ioctl(), it can set LO_FLAGS_READ_ONLY 19 * flag even though backing file with write mode. 20 */ 21 22#include <stdio.h> 23#include <unistd.h> 24#include <string.h> 25#include <stdlib.h> 26#include "lapi/loop.h" 27#include "tst_test.h" 28 29static int file_fd, file_change_fd, file_fd_invalid; 30static char backing_path[1024], backing_file_path[1024], backing_file_change_path[1024]; 31static int attach_flag, dev_fd, loop_configure_sup = 1; 32static char loop_ro_path[1024], dev_path[1024]; 33static struct loop_config loopconfig; 34 35static struct tcase { 36 int mode; 37 int ioctl; 38 char *message; 39} tcases[] = { 40 {O_RDONLY, LOOP_SET_FD, "Using LOOP_SET_FD to setup loopdevice"}, 41 {O_RDWR, LOOP_CONFIGURE, "Using LOOP_CONFIGURE with read_only flag"}, 42}; 43 44static void verify_ioctl_loop(unsigned int n) 45{ 46 struct tcase *tc = &tcases[n]; 47 struct loop_info loopinfoget; 48 49 if (tc->ioctl == LOOP_CONFIGURE && !loop_configure_sup) { 50 tst_res(TCONF, "LOOP_CONFIGURE ioctl not supported"); 51 return; 52 } 53 54 tst_res(TINFO, "%s", tc->message); 55 file_fd = SAFE_OPEN("test.img", tc->mode); 56 57 if (tc->ioctl == LOOP_SET_FD) { 58 SAFE_IOCTL(dev_fd, LOOP_SET_FD, file_fd); 59 } else { 60 loopconfig.fd = file_fd; 61 SAFE_IOCTL(dev_fd, LOOP_CONFIGURE, &loopconfig); 62 } 63 attach_flag = 1; 64 65 TST_ASSERT_INT(loop_ro_path, 1); 66 TST_ASSERT_STR(backing_path, backing_file_path); 67 68 memset(&loopinfoget, 0, sizeof(loopinfoget)); 69 70 SAFE_IOCTL(dev_fd, LOOP_GET_STATUS, &loopinfoget); 71 72 if (loopinfoget.lo_flags & ~LO_FLAGS_READ_ONLY) 73 tst_res(TFAIL, "lo_flags has unexpected %d flag", loopinfoget.lo_flags); 74 else 75 tst_res(TPASS, "lo_flags only has default LO_FLAGS_READ_ONLY flag"); 76 77 TEST(write(dev_fd, "xx", 2)); 78 if (TST_RET != -1) 79 tst_res(TFAIL, "write succeed unexpectedly"); 80 else 81 tst_res(TPASS | TTERRNO, "Can not write data in RO mode"); 82 83 TEST(ioctl(dev_fd, LOOP_CHANGE_FD, file_change_fd)); 84 if (TST_RET) { 85 tst_res(TFAIL | TTERRNO, "LOOP_CHANGE_FD failed"); 86 } else { 87 tst_res(TPASS, "LOOP_CHANGE_FD succeeded"); 88 TST_ASSERT_INT(loop_ro_path, 1); 89 TST_ASSERT_STR(backing_path, backing_file_change_path); 90 } 91 92 TEST(ioctl(dev_fd, LOOP_CHANGE_FD, file_fd_invalid)); 93 if (TST_RET) { 94 if (TST_ERR == EINVAL) 95 tst_res(TPASS | TTERRNO, "LOOP_CHANGE_FD failed as expected"); 96 else 97 tst_res(TFAIL | TTERRNO, "LOOP_CHANGE_FD failed expected EINVAL got"); 98 } else { 99 tst_res(TFAIL, "LOOP_CHANGE_FD succeeded"); 100 } 101 102 SAFE_CLOSE(file_fd); 103 tst_detach_device_by_fd(dev_path, dev_fd); 104 attach_flag = 0; 105} 106 107static void setup(void) 108{ 109 int dev_num; 110 int ret; 111 112 char *tmpdir = tst_get_tmpdir(); 113 dev_num = tst_find_free_loopdev(dev_path, sizeof(dev_path)); 114 if (dev_num < 0) 115 tst_brk(TBROK, "Failed to find free loop device"); 116 117 tst_fill_file("test.img", 0, 1024, 10); 118 tst_fill_file("test1.img", 0, 1024, 10); 119 tst_fill_file("test2.img", 0, 2048, 20); 120 121 sprintf(backing_path, "/sys/block/loop%d/loop/backing_file", dev_num); 122 sprintf(backing_file_path, "%s/test.img", tmpdir); 123 sprintf(backing_file_change_path, "%s/test1.img", tmpdir); 124 sprintf(loop_ro_path, "/sys/block/loop%d/ro", dev_num); 125 126 free(tmpdir); 127 128 file_change_fd = SAFE_OPEN("test1.img", O_RDWR); 129 file_fd_invalid = SAFE_OPEN("test2.img", O_RDWR); 130 131 dev_fd = SAFE_OPEN(dev_path, O_RDWR); 132 loopconfig.fd = -1; 133 ret = ioctl(dev_fd, LOOP_CONFIGURE, &loopconfig); 134 135 if (ret && errno != EBADF) { 136 tst_res(TINFO | TERRNO, "LOOP_CONFIGURE is not supported"); 137 loop_configure_sup = 0; 138 } 139 loopconfig.info.lo_flags = LO_FLAGS_READ_ONLY; 140} 141 142static void cleanup(void) 143{ 144 if (dev_fd > 0) 145 SAFE_CLOSE(dev_fd); 146 if (file_fd > 0) 147 SAFE_CLOSE(file_fd); 148 if (file_change_fd > 0) 149 SAFE_CLOSE(file_change_fd); 150 if (file_fd_invalid > 0) 151 SAFE_CLOSE(file_fd_invalid); 152 if (attach_flag) 153 tst_detach_device(dev_path); 154} 155 156static struct tst_test test = { 157 .setup = setup, 158 .cleanup = cleanup, 159 .tcnt = ARRAY_SIZE(tcases), 160 .test = verify_ioctl_loop, 161 .needs_root = 1, 162 .needs_tmpdir = 1, 163 .needs_drivers = (const char *const []) { 164 "loop", 165 NULL 166 } 167}; 168