1 /* 2 * Copyright (C) 2017 Red Hat, Inc. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 12 * the GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 * 18 * AUTHOR: Zorro Lang <zlang@redhat.com> 19 * 20 * DESCRIPTION 21 * This case does functional SEEK_HOLE and SEEK_DATA of lseek(2) testing. 22 * 23 * Since version 3.1, Linux supports the following additional values for 24 * whence: 25 * 26 * SEEK_DATA 27 * Adjust the file offset to the next location in the file greater than 28 * or equal to offset containing data. If offset points to data, 29 * then the file offset is set to offset. 30 * 31 * SEEK_HOLE 32 * Adjust the file offset to the next hole in the file greater than or 33 * equal to offset. If offset points into the middle of a hole, then 34 * the file offset is set to offset. If there is no hole past offset, 35 * then the file offset is adjusted to the end of the file (i.e., there 36 * is an implicit hole at the end of any file). 37 */ 38 39 #define _GNU_SOURCE 40 #include <sys/types.h> 41 #include <unistd.h> 42 #include <fcntl.h> 43 #include <stdio.h> 44 #include <string.h> 45 #include <errno.h> 46 47 #include "tst_test.h" 48 #include "tst_safe_prw.h" 49 #include "lapi/seek.h" 50 51 /* 52 * This case create 3 holes and 4 data fields, every (data) is 12 bytes, 53 * every UNIT has UNIT_BLOCKS * block_size bytes. The structure as below: 54 * 55 * ---------------------------------------------------------------------------------------------- 56 * data01suffix (hole) data02suffix (hole) data03suffix (hole) data04sufix 57 * ---------------------------------------------------------------------------------------------- 58 * |<--- UNIT_BLOCKS blocks --->||<--- UNIT_BLOCKS blocks --->||<--- UNIT_BLOCKS blocks --->| 59 * 60 */ 61 #define UNIT_COUNT 3 62 #define UNIT_BLOCKS 10 63 #define FILE_BLOCKS (UNIT_BLOCKS * UNIT_COUNT) 64 65 static int fd; 66 static blksize_t block_size; 67 68 /* 69 * SEEK from "startblock * block_size - offset", "whence" as the directive 70 * whence. 71 * startblock * block_size - offset: as offset of lseek() 72 * whence: as whence of lseek() 73 * data: as the expected result read from file offset. NULL means expect 74 * the end of file. 75 * count: as the count read from file 76 */ 77 static struct tparam { 78 off_t startblock; 79 off_t offset; 80 int whence; 81 char *data; 82 size_t count; 83 } tparams[] = { 84 {0, 0, SEEK_DATA, "data01", 6}, /* SEEK_DATA from starting of file*/ 85 {0, 4, SEEK_DATA, "01suffix", 8}, /* SEEK_DATA from maddle of the first data */ 86 {0, 0, SEEK_HOLE, "", 1023}, /* SEEK_HOLE from starting of file */ 87 {0, 4, SEEK_HOLE, "", 1023}, /* SEEK_HOLE from maddle of the first data */ 88 {1, 0, SEEK_HOLE, "", 1023}, /* SEEK_HOLE from the starting of the first hole */ 89 {1, 128, SEEK_HOLE, "", 1023}, /* SEEK_HOLE from maddle of the first hole */ 90 {1, 0, SEEK_DATA, "data02", 6}, /* SEEK_DATA from the starting of the first hole */ 91 {UNIT_BLOCKS, -1, SEEK_DATA, "data02", 6}, /* SEEK_DATA from the tail of the first hole */ 92 {UNIT_BLOCKS, 0, SEEK_DATA, "data02", 6}, /* SEEK_DATA from the starting of the second data */ 93 {UNIT_BLOCKS, 4, SEEK_DATA, "02suffix", 8}, /* SEEK_DATA from middle of the second data */ 94 {UNIT_BLOCKS, 0, SEEK_HOLE, "", 1023}, /* SEEK_HOLE from the starting of the second data */ 95 {UNIT_BLOCKS, 4, SEEK_HOLE, "", 1023}, /* SEEK_HOLE from middle of the second data */ 96 {UNIT_BLOCKS + 1, 128, SEEK_HOLE, "", 1023}, /* SEEK_HOLE from middle of the second hole */ 97 {UNIT_BLOCKS + 1, 128, SEEK_DATA, "data03", 6}, /* SEEK_DATA from middle of the second hole */ 98 {FILE_BLOCKS, -128, SEEK_HOLE, NULL, 0}, /* SEEK_HOLE from no hole pass offset*/ 99 }; 100 101 static void cleanup(void) 102 { 103 SAFE_CLOSE(fd); 104 } 105 106 static void get_blocksize(void) 107 { 108 off_t pos = 0, offset = 128; 109 int shift; 110 struct stat st; 111 112 SAFE_FSTAT(fd, &st); 113 114 /* try to discover the actual alloc size */ 115 while (pos == 0 && offset < (st.st_blksize * 2)) { 116 offset <<= 1; 117 SAFE_FTRUNCATE(fd, 0); 118 SAFE_PWRITE(1, fd, "a", 1, offset); 119 SAFE_FSYNC(fd); 120 pos = lseek(fd, 0, SEEK_DATA); 121 if (pos == -1) { 122 if (errno == EINVAL || errno == EOPNOTSUPP) { 123 tst_brk(TCONF | TERRNO, "SEEK_DATA " 124 "and SEEK_HOLE not implemented"); 125 } 126 tst_brk(TBROK | TERRNO, "SEEK_DATA failed"); 127 } 128 } 129 130 /* bisect for double check */ 131 shift = offset >> 2; 132 while (shift && offset < (st.st_blksize * 2)) { 133 SAFE_FTRUNCATE(fd, 0); 134 SAFE_PWRITE(1, fd, "a", 1, offset); 135 SAFE_FSYNC(fd); 136 pos = SAFE_LSEEK(fd, 0, SEEK_DATA); 137 offset += pos ? -shift : shift; 138 shift >>= 1; 139 } 140 141 if (!shift) 142 offset += pos ? 0 : 1; 143 block_size = offset; 144 145 /* 146 * Due to some filesystems use generic_file_llseek(), e.g: CIFS, 147 * it thinks the entire file is data, only a virtual hole at the end 148 * of the file. This case can't test this situation, so if the minimum 149 * alloc size we got bigger then st.st_blksize, we think it's not 150 * a valid value. 151 */ 152 if (block_size > st.st_blksize) { 153 tst_brk(TCONF, 154 "filesystem maybe use generic_file_llseek(), not support real SEEK_DATA/SEEK_HOLE"); 155 } 156 } 157 158 static void write_data(int fd, int num) 159 { 160 char buf[64]; 161 162 sprintf(buf, "data%02dsuffix", num); 163 SAFE_WRITE(1, fd, buf, strlen(buf)); 164 } 165 166 static void setup(void) 167 { 168 int i; 169 off_t offset = 0; 170 char fname[255]; 171 172 sprintf(fname, "tfile_lseek_%d", getpid()); 173 174 fd = SAFE_OPEN(fname, O_RDWR | O_CREAT, 0666); 175 176 get_blocksize(); 177 tst_res(TINFO, "The block size is %lu", block_size); 178 179 /* 180 * truncate to the expected file size directly, to keep away the effect 181 * of speculative preallocation of some filesystems (e.g. XFS) 182 */ 183 SAFE_FTRUNCATE(fd, FILE_BLOCKS * block_size); 184 185 SAFE_LSEEK(fd, 0, SEEK_HOLE); 186 187 for (i = 0; i < UNIT_COUNT; i++) { 188 offset = UNIT_BLOCKS * block_size * i; 189 SAFE_LSEEK(fd, offset, SEEK_SET); 190 write_data(fd, i + 1); 191 } 192 193 SAFE_LSEEK(fd, -128, SEEK_END); 194 write_data(fd, i + 1); 195 196 SAFE_FSYNC(fd); 197 SAFE_LSEEK(fd, 0, SEEK_SET); 198 } 199 200 static void test_lseek(unsigned int n) 201 { 202 struct tparam *tp = &tparams[n]; 203 off_t offset; 204 char buf[1024]; 205 int rc = 0; 206 207 memset(buf, 0, sizeof(buf)); 208 offset = (tp->startblock * block_size) + tp->offset; 209 offset = SAFE_LSEEK(fd, offset, tp->whence); 210 if (tp->data) { 211 SAFE_READ(1, fd, buf, tp->count); 212 rc = strcmp(buf, tp->data); 213 } else { 214 if (offset != SAFE_LSEEK(fd, 0, SEEK_END)) 215 rc = 1; 216 } 217 218 if (rc != 0) { 219 tst_res(TFAIL, 220 "The %uth test failed: %s from startblock %ld offset %ld, expect \'%s\' return \'%s\'", 221 n, (tp->whence == SEEK_DATA) ? "SEEK_DATA" : "SEEK_HOLE", 222 tp->startblock, tp->offset, tp->data ? tp->data : "", buf); 223 } else { 224 tst_res(TPASS, 225 "The %uth test passed: %s from startblock %ld offset %ld", 226 n, (tp->whence == SEEK_DATA) ? "SEEK_DATA" : "SEEK_HOLE", 227 tp->startblock, tp->offset); 228 } 229 } 230 231 static struct tst_test test = { 232 .tcnt = ARRAY_SIZE(tparams), 233 .test = test_lseek, 234 .setup = setup, 235 .cleanup = cleanup, 236 .needs_tmpdir = 1, 237 }; 238