test-move-data-extents.c [plain text]
#include <TargetConditionals.h>
#if !TARGET_OS_EMBEDDED
#include <sys/fcntl.h>
#include <sys/types.h>
#include <limits.h>
#include <unistd.h>
#include <sys/stat.h>
#include <pthread.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <copyfile.h>
#include "hfs-tests.h"
#include "test-utils.h"
#include "disk-image.h"
TEST(move_data_extents)
static disk_image_t *di;
static char *file1, *file2;
#define TEST_FILE_1 "/tmp/move_data_extents.1"
#define TEST_FILE_2 "/tmp/move_data_extents.2"
static volatile bool run_thread;
static void *write_thread(void *param)
{
int fd = (int)(uintptr_t)param;
while (run_thread)
write(fd, &fd, 4);
return NULL;
}
static int make_frag_file(int blocks_per_extent)
{
int fd;
asprintf(&file1, "%s/move_data_extents.1", di->mount_point);
assert_with_errno((fd = open(file1,
O_CREAT | O_RDWR | O_TRUNC | O_EVTONLY, 0666)) >= 0);
struct statfs sfs;
assert_no_err(statfs(file1, &sfs));
fstore_t fstore = {
.fst_flags = F_ALLOCATECONTIG | F_ALLOCATEALL,
.fst_posmode = F_PEOFPOSMODE,
};
off_t len = 0;
for (int i = 0; i < 9; ++i) {
if (len) {
struct log2phys l2p = {
.l2p_contigbytes = 1024 * 1024,
.l2p_devoffset = len - 1,
};
if (fcntl(fd, F_LOG2PHYS_EXT, &l2p)) {
if (errno == ERANGE)
break;
assert_fail("fcntl failed: %s", strerror(errno));
}
fstore.fst_posmode = F_VOLPOSMODE;
fstore.fst_offset = l2p.l2p_devoffset + 1 + sfs.f_bsize;
}
len += blocks_per_extent * sfs.f_bsize;
fstore.fst_length = len;
assert_no_err(fcntl(fd, F_PREALLOCATE, &fstore));
}
assert_no_err(ftruncate(fd, len));
return fd;
}
int run_move_data_extents(__unused test_ctx_t *ctx)
{
di = disk_image_get();
int frag_fd = make_frag_file(1);
int fd;
int fd2;
struct stat sb;
fstat(frag_fd, &sb);
asprintf(&file2, "%s/move_data_extents.2", di->mount_point);
assert_with_errno((fd2 = open(file2,
O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
#define F_MOVEDATAEXTENTS 69
assert_no_err(fcntl(frag_fd, F_MOVEDATAEXTENTS, fd2));
char buf[4096];
check_io(pread(fd2, buf, 4096, sb.st_size - 4096), 4096);
check_io(pwrite(fd2, buf, 100, sb.st_size), 100);
close(fd2);
assert_with_errno((fd = open(file1,
O_APPEND | O_RDWR)) >= 0);
run_thread = true;
pthread_t thread;
pthread_create(&thread, NULL, write_thread, (void *)(uintptr_t)fd);
for (int i = 0; i < 500; ++i) {
assert_with_errno((fd2 = open(file2,
O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
assert(fcntl(fd, F_MOVEDATAEXTENTS, fd2) == -1 && errno == EBUSY);
assert_no_err(close(fd2));
}
run_thread = false;
pthread_join(thread, NULL);
close(fd);
close(frag_fd);
fd = make_frag_file(2);
assert_with_errno((fd2 = open(file2,
O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
assert_no_err(fcntl(fd, F_MOVEDATAEXTENTS, fd2));
close(fd);
close(fd2);
fd = make_frag_file(1);
assert_with_errno((fd2 = open(file2,
O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
assert_no_err(fcntl(fd, F_MOVEDATAEXTENTS, fd2));
assert_no_err(unlink(file1));
assert_no_err(unlink(file2));
assert_no_err(close(fd));
assert_no_err(close(fd2));
assert_no_err(copyfile("/bin/bash", file1, NULL, COPYFILE_ALL));
assert_no_err(chmod(file1, 0777));
assert_no_err(stat(file1, &sb));
assert(sb.st_flags & UF_COMPRESSED);
assert_with_errno((fd = open(file1, O_RDONLY | O_EVTONLY)) >= 0);
assert_with_errno((fd2 = open(file2,
O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
int fd3;
char *file3;
asprintf(&file3, "%s/..namedfork/rsrc", file2);
assert_with_errno((fd3 = open(file3,
O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
assert_no_err(fcntl(fd, F_MOVEDATAEXTENTS, fd2));
assert_no_err(unlink(file1));
assert_no_err(close(fd));
assert_no_err(chflags(file2, 0));
assert_with_errno((fd = open(file3,
O_RDWR)) >= 0);
check_io(pwrite(fd, "append", 6, sb.st_size), 6);
assert_no_err(unlink(file2));
assert_no_err (close(fd));
assert_no_err (close(fd2));
assert_no_err (close(fd3));
free(file1);
free(file2);
free(file3);
return 0;
}
#endif