test-map-private.m [plain text]
#include <TargetConditionals.h>
#if !TARGET_OS_EMBEDDED
#include <sys/mman.h>
#include <unistd.h>
#include <spawn.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <Foundation/Foundation.h>
#include "hfs-tests.h"
#include "test-utils.h"
#include "systemx.h"
#include "disk-image.h"
TEST(map_private)
#define DISK_IMAGE "/tmp/map-private.sparseimage"
static char zero[65536];
static jmp_buf jmp_env;
static void handle_sigbus(int signal)
{
assert(signal == SIGBUS);
longjmp(jmp_env, 1);
}
int run_map_private(__unused test_ctx_t *ctx)
{
disk_image_t *di = disk_image_create(DISK_IMAGE, &(disk_image_opts_t){
.size = 64 * 1024 * 1024
});
char *path;
asprintf(&path, "
int fd;
void *p;
assert_with_errno((fd = open(path, O_RDWR | O_CREAT, 0666)) >= 0);
assert_no_err(ftruncate(fd, 65536));
assert_no_err(fcntl(fd, F_FULLFSYNC));
assert_with_errno((p = mmap(NULL, 65536, PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd, 0)) != MAP_FAILED);
assert_no_err(close(fd));
assert_no_err(unlink(path));
// Create a second file that we keep open via a file descriptor
char *path2;
asprintf(&path2, "
assert_with_errno((fd = open(path2, O_RDWR | O_CREAT, 0666)) >= 0);
assert_no_err(ftruncate(fd, 65536));
assert_no_err(fcntl(fd, F_FULLFSYNC));
assert_no_err(unlink(path2));
assert(!systemx("/usr/sbin/diskutil", SYSTEMX_QUIET, "unmount", "force", di->mount_point, NULL));
/*
* Forcibly unmounting should not have caused all the data
* to be paged in so this should result in a fault.
*/
// Set things up to catch the SIGBUS
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGBUS);
sigprocmask(SIG_BLOCK, &set, NULL);
struct sigaction sa = {
.sa_handler = handle_sigbus,
.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO,
};
sigaction(SIGBUS, &sa, NULL);
pthread_sigmask(SIG_UNBLOCK, &set, NULL);
if (!setjmp(jmp_env)) {
if(memcmp(p, zero, 65536)) // Need this if statement so the memcmp isn't optimized out
assert_fail("memcmp should have faulted");
else
assert_fail("... memcmp should have faulted");
}
// Now remount the volume and make sure file has been deleted
assert(!systemx("/usr/sbin/diskutil", SYSTEMX_QUIET, "mount", di->disk, NULL));
/*
* We assume that it will get mounted at the same place, which
* is reasonable given the environment this should be running
* in.
*/
struct stat sb;
assert(stat(path, &sb) == -1 && errno == ENOENT);
// Now check that common open unlink behaviour isn't broken
// First check disk space
struct statfs sfs;
assert_no_err(statfs(di->mount_point, &sfs));
// Should be at least 7 MB
uint64_t space = sfs.f_bfree * sfs.f_bsize;
#define MB * 1024 * 1024
assert(space > 7 MB);
assert_with_errno((fd = open(path, O_RDWR | O_CREAT, 0666)) >= 0);
assert_no_err(ftruncate(fd, 7 MB));
assert_no_err(statfs(di->mount_point, &sfs));
// Space should have dropped by at least 5 MB
assert(sfs.f_bfree * sfs.f_bsize < space - 5 MB);
assert_no_err(unlink(path));
// Map the file
assert_with_errno((p = mmap(NULL, 65536, PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd, 0)) != MAP_FAILED);
assert_no_err(close(fd));
// File is still in use, so space should not have changed
assert_no_err(statfs(di->mount_point, &sfs));
assert(sfs.f_bfree * sfs.f_bsize < space - 5 MB);
// Get rid of the last reference
assert_no_err(munmap(p, 65536));
// Just in case we collide with sync
sleep(1);
// Space should be back up to at least 7 MB free
assert_no_err(statfs(di->mount_point, &sfs));
assert(sfs.f_bfree * sfs.f_bsize > 7 MB);
return 0;
}
#endif