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