/* Checks that locking a dangling symlink works OK. (It used to hang.) */ static void run_lock_symlink(void) { struct lockfile *a, *b, *dummy; struct stat s; /* Create a symlink .a.~lock~ pointing to .b.~lock~. */ CHECK(symlink(".b.~lock~", ".a.~lock~"), 0); CHECK(lstat(".a.~lock~", &s), 0); CHECK(S_ISLNK(s.st_mode) != 0, 1); CHECK(stat(".a.~lock~", &s), -1); CHECK(errno, ENOENT); CHECK(stat(".b.~lock~", &s), -1); CHECK(errno, ENOENT); CHECK(lockfile_lock("a", &a), 0); CHECK(lockfile_lock("a", &dummy), EDEADLK); CHECK(lockfile_lock("b", &dummy), EDEADLK); lockfile_unlock(a); CHECK(lockfile_lock("b", &b), 0); CHECK(lockfile_lock("b", &dummy), EDEADLK); CHECK(lockfile_lock("a", &dummy), EDEADLK); lockfile_unlock(b); CHECK(lstat(".a.~lock~", &s), 0); CHECK(S_ISLNK(s.st_mode) != 0, 1); CHECK(stat(".a.~lock~", &s), 0); CHECK(S_ISREG(s.st_mode) != 0, 1); CHECK(stat(".b.~lock~", &s), 0); CHECK(S_ISREG(s.st_mode) != 0, 1); }
static void run_lock_and_unlock_twice(void) { struct lockfile *lockfile; CHECK(lockfile_lock("file", &lockfile), 0); lockfile_unlock(lockfile); CHECK(lockfile_lock("file", &lockfile), 0); lockfile_unlock(lockfile); }
static void run_lock_blocks_same_process(void) { struct lockfile *lockfile; CHECK(lockfile_lock("file", &lockfile), 0); CHECK(lockfile_lock("file", &lockfile), EDEADLK); lockfile_unlock(lockfile); }
static void run_lock_multiple(void) { struct lockfile *a, *b, *c, *dummy; CHECK(lockfile_lock("a", &a), 0); CHECK(lockfile_lock("b", &b), 0); CHECK(lockfile_lock("c", &c), 0); lockfile_unlock(a); CHECK(lockfile_lock("a", &a), 0); CHECK(lockfile_lock("a", &dummy), EDEADLK); lockfile_unlock(a); lockfile_unlock(b); CHECK(lockfile_lock("a", &a), 0); lockfile_unlock(c); lockfile_unlock(a); }
void ovsdb_log_close(struct ovsdb_log *file) { if (file) { free(file->name); fclose(file->stream); lockfile_unlock(file->lockfile); ovsdb_error_destroy(file->read_error); free(file); } }
static void run_lock_and_unlock_allows_other_process(void) { struct lockfile *lockfile; CHECK(lockfile_lock("file", &lockfile), 0); lockfile_unlock(lockfile); if (do_fork() == CHILD) { CHECK(lockfile_lock("file", &lockfile), 0); exit(11); } }
static void run_lock_blocks_other_process(void) { /* Making this static prevents a memory leak warning from valgrind for the * parent process, which cannot easily unlock (and free) 'lockfile' because * it can only do so after the child has exited, and it's the caller of * this function that does the wait() call. */ static struct lockfile *lockfile; CHECK(lockfile_lock("file", &lockfile), 0); if (do_fork() == CHILD) { lockfile_unlock(lockfile); CHECK(lockfile_lock("file", &lockfile), EAGAIN); exit(11); } }
/* Checks that locking a file that is itself a symlink yields a lockfile in the * directory that the symlink points to, named for the target of the * symlink. * * (That is, if "a" is a symlink to "dir/b", then "a"'s lockfile is named * "dir/.b.~lock".) */ static void run_lock_symlink_to_dir(void) { struct lockfile *a, *dummy; struct stat s; /* Create a symlink "a" pointing to "dir/b". */ CHECK(mkdir("dir", 0700), 0); CHECK(symlink("dir/b", "a"), 0); CHECK(lstat("a", &s), 0); CHECK(S_ISLNK(s.st_mode) != 0, 1); /* Lock 'a'. */ CHECK(lockfile_lock("a", &a), 0); CHECK(lstat("dir/.b.~lock~", &s), 0); CHECK(S_ISREG(s.st_mode) != 0, 1); CHECK(lstat(".a.~lock~", &s), -1); CHECK(errno, ENOENT); CHECK(lockfile_lock("dir/b", &dummy), EDEADLK); lockfile_unlock(a); }
/* Attempts to open 'name' with the specified 'open_mode'. On success, stores * the new log into '*filep' and returns NULL; otherwise returns NULL and * stores NULL into '*filep'. * * Whether the file will be locked using lockfile_lock() depends on 'locking': * use true to lock it, false not to lock it, or -1 to lock it only if * 'open_mode' is a mode that allows writing. */ struct ovsdb_error * ovsdb_log_open(const char *name, enum ovsdb_log_open_mode open_mode, int locking, struct ovsdb_log **filep) { struct lockfile *lockfile; struct ovsdb_error *error; struct ovsdb_log *file; struct stat s; FILE *stream; int flags; int fd; *filep = NULL; ovs_assert(locking == -1 || locking == false || locking == true); if (locking < 0) { locking = open_mode != OVSDB_LOG_READ_ONLY; } if (locking) { int retval = lockfile_lock(name, &lockfile); if (retval) { error = ovsdb_io_error(retval, "%s: failed to lock lockfile", name); goto error; } } else { lockfile = NULL; } if (open_mode == OVSDB_LOG_READ_ONLY) { flags = O_RDONLY; } else if (open_mode == OVSDB_LOG_READ_WRITE) { flags = O_RDWR; } else if (open_mode == OVSDB_LOG_CREATE) { #ifndef _WIN32 if (stat(name, &s) == -1 && errno == ENOENT && lstat(name, &s) == 0 && S_ISLNK(s.st_mode)) { /* 'name' is a dangling symlink. We want to create the file that * the symlink points to, but POSIX says that open() with O_EXCL * must fail with EEXIST if the named file is a symlink. So, we * have to leave off O_EXCL and accept the race. */ flags = O_RDWR | O_CREAT; } else { flags = O_RDWR | O_CREAT | O_EXCL; } #else flags = O_RDWR | O_CREAT | O_EXCL; #endif } else { OVS_NOT_REACHED(); } #ifdef _WIN32 flags = flags | O_BINARY; #endif fd = open(name, flags, 0666); if (fd < 0) { const char *op = open_mode == OVSDB_LOG_CREATE ? "create" : "open"; error = ovsdb_io_error(errno, "%s: %s failed", op, name); goto error_unlock; } if (!fstat(fd, &s) && s.st_size == 0) { /* It's (probably) a new file so fsync() its parent directory to ensure * that its directory entry is committed to disk. */ fsync_parent_dir(name); } stream = fdopen(fd, open_mode == OVSDB_LOG_READ_ONLY ? "rb" : "w+b"); if (!stream) { error = ovsdb_io_error(errno, "%s: fdopen failed", name); goto error_close; } file = xmalloc(sizeof *file); file->name = xstrdup(name); file->lockfile = lockfile; file->stream = stream; file->prev_offset = 0; file->offset = 0; file->read_error = NULL; file->write_error = false; file->mode = OVSDB_LOG_READ; *filep = file; return NULL; error_close: close(fd); error_unlock: lockfile_unlock(lockfile); error: return error; }