/*-1* Copyright (c) 2008 Isilon Inc http://www.isilon.com/2* Authors: Doug Rabson <[email protected]>3* Developed with Red Inc: Alfred Perlstein <[email protected]>4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/2627#include <sys/param.h>28#include <sys/file.h>29#include <sys/time.h>30#ifdef __FreeBSD__31#include <sys/mount.h>32#endif33#include <sys/stat.h>34#include <sys/wait.h>3536#include <err.h>37#include <errno.h>38#include <fcntl.h>39#include <pthread.h>40#include <signal.h>41#include <stdint.h>42#include <stdio.h>43#include <stdlib.h>44#include <string.h>45#include <unistd.h>4647#ifdef __FreeBSD__48#if __FreeBSD_version >= 80002849#define HAVE_SYSID50#endif51#include <sys/cdefs.h>52#else53#ifndef nitems54#define nitems(x) (sizeof((x)) / sizeof((x)[0]))55#endif5657#ifndef __unused58#ifdef __GNUC__59#define __unused __attribute__((__unused__))60#else61#define __unused62#endif63#endif64#endif6566static int verbose = 0;6768static int69make_file(const char *pathname, off_t sz)70{71struct stat st;72const char *template = "/flocktempXXXXXX";73size_t len;74char *filename;75int fd;7677if (stat(pathname, &st) == 0) {78if (S_ISREG(st.st_mode)) {79fd = open(pathname, O_RDWR);80if (fd < 0)81err(1, "open(%s)", pathname);82if (ftruncate(fd, sz) < 0)83err(1, "ftruncate");84return (fd);85}86}8788len = strlen(pathname) + strlen(template) + 1;89filename = malloc(len);90strcpy(filename, pathname);91strcat(filename, template);92fd = mkstemp(filename);93if (fd < 0)94err(1, "mkstemp");95if (ftruncate(fd, sz) < 0)96err(1, "ftruncate");97if (unlink(filename) < 0)98err(1, "unlink");99free(filename);100101return (fd);102}103104static void105ignore_alarm(int __unused sig)106{107}108109static int110safe_waitpid(pid_t pid)111{112int save_errno;113int status;114115save_errno = errno;116errno = 0;117while (waitpid(pid, &status, 0) != pid) {118if (errno == EINTR)119continue;120err(1, "waitpid");121}122errno = save_errno;123124return (status);125}126127#define FAIL(test) \128do { \129if (test) { \130printf("FAIL (%s)\n", #test); \131return -1; \132} \133} while (0)134135#define SUCCEED \136do { printf("SUCCEED\n"); return 0; } while (0)137138/*139* Test 1 - F_GETLK on unlocked region140*141* If no lock is found that would prevent this lock from being142* created, the structure is left unchanged by this function call143* except for the lock type which is set to F_UNLCK.144*/145static int146test1(int fd, __unused int argc, const __unused char **argv)147{148struct flock fl1, fl2;149150memset(&fl1, 1, sizeof(fl1));151fl1.l_type = F_WRLCK;152fl1.l_whence = SEEK_SET;153fl2 = fl1;154155if (fcntl(fd, F_GETLK, &fl1) < 0)156err(1, "F_GETLK");157158printf("1 - F_GETLK on unlocked region: ");159FAIL(fl1.l_start != fl2.l_start);160FAIL(fl1.l_len != fl2.l_len);161FAIL(fl1.l_pid != fl2.l_pid);162FAIL(fl1.l_type != F_UNLCK);163FAIL(fl1.l_whence != fl2.l_whence);164#ifdef HAVE_SYSID165FAIL(fl1.l_sysid != fl2.l_sysid);166#endif167168SUCCEED;169}170171/*172* Test 2 - F_SETLK on locked region173*174* If a shared or exclusive lock cannot be set, fcntl returns175* immediately with EACCES or EAGAIN.176*/177static int178test2(int fd, __unused int argc, const __unused char **argv)179{180/*181* We create a child process to hold the lock which we will182* test. We use a pipe to communicate with the child.183*/184int pid;185int pfd[2];186struct flock fl;187char ch;188int res;189190if (pipe(pfd) < 0)191err(1, "pipe");192193fl.l_start = 0;194fl.l_len = 0;195fl.l_type = F_WRLCK;196fl.l_whence = SEEK_SET;197198pid = fork();199if (pid < 0)200err(1, "fork");201202if (pid == 0) {203/*204* We are the child. We set a write lock and then205* write one byte back to the parent to tell it. The206* parent will kill us when its done.207*/208if (fcntl(fd, F_SETLK, &fl) < 0)209err(1, "F_SETLK (child)");210if (write(pfd[1], "a", 1) < 0)211err(1, "writing to pipe (child)");212pause();213exit(0);214}215216/*217* Wait until the child has set its lock and then perform the218* test.219*/220if (read(pfd[0], &ch, 1) != 1)221err(1, "reading from pipe (child)");222223/*224* fcntl should return -1 with errno set to either EACCES or225* EAGAIN.226*/227printf("2 - F_SETLK on locked region: ");228res = fcntl(fd, F_SETLK, &fl);229kill(pid, SIGTERM);230safe_waitpid(pid);231close(pfd[0]);232close(pfd[1]);233FAIL(res == 0);234FAIL(errno != EACCES && errno != EAGAIN);235236SUCCEED;237}238239/*240* Test 3 - F_SETLKW on locked region241*242* If a shared or exclusive lock is blocked by other locks, the243* process waits until the request can be satisfied.244*245* XXX this test hangs on FreeBSD NFS filesystems due to limitations246* in FreeBSD's client (and server) lockd implementation.247*/248static int249test3(int fd, __unused int argc, const __unused char **argv)250{251/*252* We create a child process to hold the lock which we will253* test. We use a pipe to communicate with the child.254*/255int pid;256int pfd[2];257struct flock fl;258char ch;259int res;260261if (pipe(pfd) < 0)262err(1, "pipe");263264fl.l_start = 0;265fl.l_len = 0;266fl.l_type = F_WRLCK;267fl.l_whence = SEEK_SET;268269pid = fork();270if (pid < 0)271err(1, "fork");272273if (pid == 0) {274/*275* We are the child. We set a write lock and then276* write one byte back to the parent to tell it. The277* parent will kill us when its done.278*/279if (fcntl(fd, F_SETLK, &fl) < 0)280err(1, "F_SETLK (child)");281if (write(pfd[1], "a", 1) < 0)282err(1, "writing to pipe (child)");283pause();284exit(0);285}286287/*288* Wait until the child has set its lock and then perform the289* test.290*/291if (read(pfd[0], &ch, 1) != 1)292err(1, "reading from pipe (child)");293294/*295* fcntl should wait until the alarm and then return -1 with296* errno set to EINTR.297*/298printf("3 - F_SETLKW on locked region: ");299300alarm(1);301302res = fcntl(fd, F_SETLKW, &fl);303kill(pid, SIGTERM);304safe_waitpid(pid);305close(pfd[0]);306close(pfd[1]);307FAIL(res == 0);308FAIL(errno != EINTR);309310SUCCEED;311}312313/*314* Test 4 - F_GETLK on locked region315*316* Get the first lock that blocks the lock.317*/318static int319test4(int fd, __unused int argc, const __unused char **argv)320{321/*322* We create a child process to hold the lock which we will323* test. We use a pipe to communicate with the child.324*/325int pid;326int pfd[2];327struct flock fl;328char ch;329330if (pipe(pfd) < 0)331err(1, "pipe");332333fl.l_start = 0;334fl.l_len = 99;335fl.l_type = F_WRLCK;336fl.l_whence = SEEK_SET;337338pid = fork();339if (pid < 0)340err(1, "fork");341342if (pid == 0) {343/*344* We are the child. We set a write lock and then345* write one byte back to the parent to tell it. The346* parent will kill us when its done.347*/348if (fcntl(fd, F_SETLK, &fl) < 0)349err(1, "F_SETLK (child)");350if (write(pfd[1], "a", 1) < 0)351err(1, "writing to pipe (child)");352pause();353exit(0);354}355356/*357* Wait until the child has set its lock and then perform the358* test.359*/360if (read(pfd[0], &ch, 1) != 1)361err(1, "reading from pipe (child)");362363/*364* fcntl should return a lock structure reflecting the lock we365* made in the child process.366*/367if (fcntl(fd, F_GETLK, &fl) < 0)368err(1, "F_GETLK");369370printf("4 - F_GETLK on locked region: ");371FAIL(fl.l_start != 0);372FAIL(fl.l_len != 99);373FAIL(fl.l_type != F_WRLCK);374FAIL(fl.l_pid != pid);375#ifdef HAVE_SYSID376FAIL(fl.l_sysid != 0);377#endif378379kill(pid, SIGTERM);380safe_waitpid(pid);381close(pfd[0]);382close(pfd[1]);383384SUCCEED;385}386387/*388* Test 5 - F_SETLKW simple deadlock389*390* If a blocking shared lock request would cause a deadlock (i.e. the391* lock request is blocked by a process which is itself blocked on a392* lock currently owned by the process making the new request),393* EDEADLK is returned.394*/395static int396test5(int fd, __unused int argc, const __unused char **argv)397{398/*399* We create a child process to hold the lock which we will400* test. Because our test relies on the child process being401* blocked on the parent's lock, we can't easily use a pipe to402* synchronize so we just sleep in the parent to given the403* child a chance to setup.404*405* To create the deadlock condition, we arrange for the parent406* to lock the first byte of the file and the child to lock407* the second byte. After locking the second byte, the child408* will attempt to lock the first byte of the file, and409* block. The parent will then attempt to lock the second byte410* (owned by the child) which should cause deadlock.411*/412int pid;413struct flock fl;414int res;415416/*417* Lock the first byte in the parent.418*/419fl.l_start = 0;420fl.l_len = 1;421fl.l_type = F_WRLCK;422fl.l_whence = SEEK_SET;423if (fcntl(fd, F_SETLK, &fl) < 0)424err(1, "F_SETLK 1 (parent)");425426pid = fork();427if (pid < 0)428err(1, "fork");429430if (pid == 0) {431/*432* Lock the second byte in the child and then block on433* the parent's lock.434*/435fl.l_start = 1;436if (fcntl(fd, F_SETLK, &fl) < 0)437err(1, "F_SETLK (child)");438fl.l_start = 0;439if (fcntl(fd, F_SETLKW, &fl) < 0)440err(1, "F_SETLKW (child)");441exit(0);442}443444/*445* Wait until the child has set its lock and then perform the446* test.447*/448sleep(1);449450/*451* fcntl should immediately return -1 with errno set to452* EDEADLK. If the alarm fires, we failed to detect the453* deadlock.454*/455alarm(1);456printf("5 - F_SETLKW simple deadlock: ");457458fl.l_start = 1;459res = fcntl(fd, F_SETLKW, &fl);460kill(pid, SIGTERM);461safe_waitpid(pid);462463FAIL(res == 0);464FAIL(errno != EDEADLK);465466fl.l_start = 0;467fl.l_len = 0;468fl.l_type = F_UNLCK;469if (fcntl(fd, F_SETLK, &fl) < 0)470err(1, "F_UNLCK");471472/*473* Cancel the alarm to avoid confusing later tests.474*/475alarm(0);476477SUCCEED;478}479480/*481* Test 6 - F_SETLKW complex deadlock.482*483* This test involves three process, P, C1 and C2. We set things up so484* that P locks byte zero, C1 locks byte 1 and C2 locks byte 2. We485* also block C2 by attempting to lock byte zero. Lastly, P attempts486* to lock a range including byte 1 and 2. This represents a deadlock487* (due to C2's blocking attempt to lock byte zero).488*/489static int490test6(int fd, __unused int argc, const __unused char **argv)491{492/*493* Because our test relies on the child process being blocked494* on the parent's lock, we can't easily use a pipe to495* synchronize so we just sleep in the parent to given the496* children a chance to setup.497*/498int pid1, pid2;499struct flock fl;500int res;501502/*503* Lock the first byte in the parent.504*/505fl.l_start = 0;506fl.l_len = 1;507fl.l_type = F_WRLCK;508fl.l_whence = SEEK_SET;509if (fcntl(fd, F_SETLK, &fl) < 0)510err(1, "F_SETLK 1 (parent)");511512pid1 = fork();513if (pid1 < 0)514err(1, "fork");515516if (pid1 == 0) {517/*518* C1519* Lock the second byte in the child and then sleep520*/521fl.l_start = 1;522if (fcntl(fd, F_SETLK, &fl) < 0)523err(1, "F_SETLK (child1)");524pause();525exit(0);526}527528pid2 = fork();529if (pid2 < 0)530err(1, "fork");531532if (pid2 == 0) {533/*534* C2535* Lock the third byte in the child and then block on536* the parent's lock.537*/538fl.l_start = 2;539if (fcntl(fd, F_SETLK, &fl) < 0)540err(1, "F_SETLK (child2)");541fl.l_start = 0;542if (fcntl(fd, F_SETLKW, &fl) < 0)543err(1, "F_SETLKW (child2)");544exit(0);545}546547/*548* Wait until the children have set their locks and then549* perform the test.550*/551sleep(1);552553/*554* fcntl should immediately return -1 with errno set to555* EDEADLK. If the alarm fires, we failed to detect the556* deadlock.557*/558alarm(1);559printf("6 - F_SETLKW complex deadlock: ");560561fl.l_start = 1;562fl.l_len = 2;563res = fcntl(fd, F_SETLKW, &fl);564kill(pid1, SIGTERM);565safe_waitpid(pid1);566kill(pid2, SIGTERM);567safe_waitpid(pid2);568569fl.l_start = 0;570fl.l_len = 0;571fl.l_type = F_UNLCK;572if (fcntl(fd, F_SETLK, &fl) < 0)573err(1, "F_UNLCK");574575FAIL(res == 0);576FAIL(errno != EDEADLK);577578/*579* Cancel the alarm to avoid confusing later tests.580*/581alarm(0);582583SUCCEED;584}585586/*587* Test 7 - F_SETLK shared lock on exclusive locked region588*589* If a shared or exclusive lock cannot be set, fcntl returns590* immediately with EACCES or EAGAIN.591*/592static int593test7(int fd, __unused int argc, const __unused char **argv)594{595/*596* We create a child process to hold the lock which we will597* test. We use a pipe to communicate with the child.598*/599int pid;600int pfd[2];601struct flock fl;602char ch;603int res;604605if (pipe(pfd) < 0)606err(1, "pipe");607608fl.l_start = 0;609fl.l_len = 0;610fl.l_type = F_WRLCK;611fl.l_whence = SEEK_SET;612613pid = fork();614if (pid < 0)615err(1, "fork");616617if (pid == 0) {618/*619* We are the child. We set a write lock and then620* write one byte back to the parent to tell it. The621* parent will kill us when its done.622*/623if (fcntl(fd, F_SETLK, &fl) < 0)624err(1, "F_SETLK (child)");625if (write(pfd[1], "a", 1) < 0)626err(1, "writing to pipe (child)");627pause();628exit(0);629}630631/*632* Wait until the child has set its lock and then perform the633* test.634*/635if (read(pfd[0], &ch, 1) != 1)636err(1, "reading from pipe (child)");637638/*639* fcntl should wait until the alarm and then return -1 with640* errno set to EINTR.641*/642printf("7 - F_SETLK shared lock on exclusive locked region: ");643644fl.l_type = F_RDLCK;645res = fcntl(fd, F_SETLK, &fl);646kill(pid, SIGTERM);647safe_waitpid(pid);648close(pfd[0]);649close(pfd[1]);650651FAIL(res == 0);652FAIL(errno != EACCES && errno != EAGAIN);653654SUCCEED;655}656657/*658* Test 8 - F_SETLK shared lock on share locked region659*660* When a shared lock is set on a segment of a file, other processes661* shall be able to set shared locks on that segment or a portion of662* it.663*/664static int665test8(int fd, __unused int argc, const __unused char **argv)666{667/*668* We create a child process to hold the lock which we will669* test. We use a pipe to communicate with the child.670*/671int pid;672int pfd[2];673struct flock fl;674char ch;675int res;676677if (pipe(pfd) < 0)678err(1, "pipe");679680fl.l_start = 0;681fl.l_len = 0;682fl.l_type = F_RDLCK;683fl.l_whence = SEEK_SET;684685pid = fork();686if (pid < 0)687err(1, "fork");688689if (pid == 0) {690/*691* We are the child. We set a write lock and then692* write one byte back to the parent to tell it. The693* parent will kill us when its done.694*/695if (fcntl(fd, F_SETLK, &fl) < 0)696err(1, "F_SETLK (child)");697if (write(pfd[1], "a", 1) < 0)698err(1, "writing to pipe (child)");699pause();700exit(0);701}702703/*704* Wait until the child has set its lock and then perform the705* test.706*/707if (read(pfd[0], &ch, 1) != 1)708err(1, "reading from pipe (child)");709710/*711* fcntl should wait until the alarm and then return -1 with712* errno set to EINTR.713*/714printf("8 - F_SETLK shared lock on share locked region: ");715716fl.l_type = F_RDLCK;717res = fcntl(fd, F_SETLK, &fl);718719kill(pid, SIGTERM);720safe_waitpid(pid);721close(pfd[0]);722close(pfd[1]);723724fl.l_start = 0;725fl.l_len = 0;726fl.l_type = F_UNLCK;727if (fcntl(fd, F_SETLK, &fl) < 0)728err(1, "F_UNLCK");729730FAIL(res != 0);731732SUCCEED;733}734735/*736* Test 9 - F_SETLK exclusive lock on share locked region737*738* If a shared or exclusive lock cannot be set, fcntl returns739* immediately with EACCES or EAGAIN.740*/741static int742test9(int fd, __unused int argc, const __unused char **argv)743{744/*745* We create a child process to hold the lock which we will746* test. We use a pipe to communicate with the child.747*/748int pid;749int pfd[2];750struct flock fl;751char ch;752int res;753754if (pipe(pfd) < 0)755err(1, "pipe");756757fl.l_start = 0;758fl.l_len = 0;759fl.l_type = F_RDLCK;760fl.l_whence = SEEK_SET;761762pid = fork();763if (pid < 0)764err(1, "fork");765766if (pid == 0) {767/*768* We are the child. We set a write lock and then769* write one byte back to the parent to tell it. The770* parent will kill us when its done.771*/772if (fcntl(fd, F_SETLK, &fl) < 0)773err(1, "F_SETLK (child)");774if (write(pfd[1], "a", 1) < 0)775err(1, "writing to pipe (child)");776pause();777exit(0);778}779780/*781* Wait until the child has set its lock and then perform the782* test.783*/784if (read(pfd[0], &ch, 1) != 1)785err(1, "reading from pipe (child)");786787/*788* fcntl should wait until the alarm and then return -1 with789* errno set to EINTR.790*/791printf("9 - F_SETLK exclusive lock on share locked region: ");792793fl.l_type = F_WRLCK;794res = fcntl(fd, F_SETLK, &fl);795kill(pid, SIGTERM);796safe_waitpid(pid);797close(pfd[0]);798close(pfd[1]);799800FAIL(res == 0);801FAIL(errno != EACCES && errno != EAGAIN);802803SUCCEED;804}805806/*807* Test 10 - trying to set bogus pid or sysid values808*809* The l_pid and l_sysid fields are only used with F_GETLK to return810* the process ID of the process holding a blocking lock and the811* system ID of the system that owns that process812*/813static int814test10(int fd, __unused int argc, const __unused char **argv)815{816/*817* We create a child process to hold the lock which we will818* test. We use a pipe to communicate with the child.819*/820int pid;821int pfd[2];822struct flock fl;823char ch;824825if (pipe(pfd) < 0)826err(1, "pipe");827828fl.l_start = 0;829fl.l_len = 0;830fl.l_type = F_WRLCK;831fl.l_whence = SEEK_SET;832fl.l_pid = 9999;833#ifdef HAVE_SYSID834fl.l_sysid = 9999;835#endif836837pid = fork();838if (pid < 0)839err(1, "fork");840841if (pid == 0) {842/*843* We are the child. We set a write lock and then844* write one byte back to the parent to tell it. The845* parent will kill us when its done.846*/847if (fcntl(fd, F_SETLK, &fl) < 0)848err(1, "F_SETLK (child)");849if (write(pfd[1], "a", 1) < 0)850err(1, "writing to pipe (child)");851pause();852exit(0);853}854855/*856* Wait until the child has set its lock and then perform the857* test.858*/859if (read(pfd[0], &ch, 1) != 1)860err(1, "reading from pipe (child)");861862printf("10 - trying to set bogus pid or sysid values: ");863864if (fcntl(fd, F_GETLK, &fl) < 0)865err(1, "F_GETLK");866867kill(pid, SIGTERM);868safe_waitpid(pid);869close(pfd[0]);870close(pfd[1]);871872FAIL(fl.l_pid != pid);873#ifdef HAVE_SYSID874FAIL(fl.l_sysid != 0);875#endif876877SUCCEED;878}879880/*881* Test 11 - remote locks882*883* XXX temporary interface which will be removed when the kernel lockd884* is added.885*/886static int887test11(int fd, __unused int argc, const __unused char **argv)888{889#ifdef F_SETLK_REMOTE890struct flock fl;891int res;892893if (geteuid() != 0)894return 0;895896fl.l_start = 0;897fl.l_len = 0;898fl.l_type = F_WRLCK;899fl.l_whence = SEEK_SET;900fl.l_pid = 9999;901fl.l_sysid = 1001;902903printf("11 - remote locks: ");904905res = fcntl(fd, F_SETLK_REMOTE, &fl);906FAIL(res != 0);907908fl.l_sysid = 1002;909res = fcntl(fd, F_SETLK_REMOTE, &fl);910FAIL(res == 0);911FAIL(errno != EACCES && errno != EAGAIN);912913res = fcntl(fd, F_GETLK, &fl);914FAIL(res != 0);915FAIL(fl.l_pid != 9999);916FAIL(fl.l_sysid != 1001);917918fl.l_type = F_UNLCK;919fl.l_sysid = 1001;920fl.l_start = 0;921fl.l_len = 0;922res = fcntl(fd, F_SETLK_REMOTE, &fl);923FAIL(res != 0);924925fl.l_pid = 1234;926fl.l_sysid = 1001;927fl.l_start = 0;928fl.l_len = 1;929fl.l_whence = SEEK_SET;930fl.l_type = F_RDLCK;931res = fcntl(fd, F_SETLK_REMOTE, &fl);932FAIL(res != 0);933934fl.l_sysid = 1002;935res = fcntl(fd, F_SETLK_REMOTE, &fl);936FAIL(res != 0);937938fl.l_type = F_UNLCKSYS;939fl.l_sysid = 1001;940res = fcntl(fd, F_SETLK_REMOTE, &fl);941FAIL(res != 0);942943fl.l_type = F_WRLCK;944res = fcntl(fd, F_GETLK, &fl);945FAIL(res != 0);946FAIL(fl.l_pid != 1234);947FAIL(fl.l_sysid != 1002);948949fl.l_type = F_UNLCKSYS;950fl.l_sysid = 1002;951res = fcntl(fd, F_SETLK_REMOTE, &fl);952FAIL(res != 0);953954SUCCEED;955#else956return 0;957#endif958}959960/*961* Test 12 - F_SETLKW on locked region which is then unlocked962*963* If a shared or exclusive lock is blocked by other locks, the964* process waits until the request can be satisfied.965*/966static int967test12(int fd, __unused int argc, const __unused char **argv)968{969/*970* We create a child process to hold the lock which we will971* test. We use a pipe to communicate with the child.972*/973int pid;974int pfd[2];975struct flock fl;976char ch;977int res;978979if (pipe(pfd) < 0)980err(1, "pipe");981982fl.l_start = 0;983fl.l_len = 0;984fl.l_type = F_WRLCK;985fl.l_whence = SEEK_SET;986987pid = fork();988if (pid < 0)989err(1, "fork");990991if (pid == 0) {992/*993* We are the child. We set a write lock and then994* write one byte back to the parent to tell it. The995* parent will kill us when its done.996*/997if (fcntl(fd, F_SETLK, &fl) < 0)998err(1, "F_SETLK (child)");999if (write(pfd[1], "a", 1) < 0)1000err(1, "writing to pipe (child)");10011002sleep(1);1003exit(0);1004}10051006/*1007* Wait until the child has set its lock and then perform the1008* test.1009*/1010if (read(pfd[0], &ch, 1) != 1)1011err(1, "reading from pipe (child)");10121013/*1014* fcntl should wait until the alarm and then return -1 with1015* errno set to EINTR.1016*/1017printf("12 - F_SETLKW on locked region which is then unlocked: ");10181019//alarm(1);10201021res = fcntl(fd, F_SETLKW, &fl);1022kill(pid, SIGTERM);1023safe_waitpid(pid);1024close(pfd[0]);1025close(pfd[1]);1026FAIL(res != 0);10271028fl.l_start = 0;1029fl.l_len = 0;1030fl.l_type = F_UNLCK;1031if (fcntl(fd, F_SETLK, &fl) < 0)1032err(1, "F_UNLCK");10331034SUCCEED;1035}10361037/*1038* Test 13 - F_SETLKW on locked region, race with owner1039*1040* If a shared or exclusive lock is blocked by other locks, the1041* process waits until the request can be satisfied.1042*/1043static int1044test13(int fd, __unused int argc, const __unused char **argv)1045{1046/*1047* We create a child process to hold the lock which we will1048* test. We use a pipe to communicate with the child.1049*/1050int i;1051int pid;1052int pfd[2];1053struct flock fl;1054char ch;1055int res;1056struct itimerval itv;10571058printf("13 - F_SETLKW on locked region, race with owner: ");1059fflush(stdout);10601061for (i = 0; i < 100; i++) {1062if (pipe(pfd) < 0)1063err(1, "pipe");10641065fl.l_start = 0;1066fl.l_len = 0;1067fl.l_type = F_WRLCK;1068fl.l_whence = SEEK_SET;10691070pid = fork();1071if (pid < 0)1072err(1, "fork");10731074if (pid == 0) {1075/*1076* We are the child. We set a write lock and then1077* write one byte back to the parent to tell it. The1078* parent will kill us when its done.1079*/1080if (fcntl(fd, F_SETLK, &fl) < 0)1081err(1, "F_SETLK (child)");1082if (write(pfd[1], "a", 1) < 0)1083err(1, "writing to pipe (child)");10841085usleep(1);1086exit(0);1087}10881089/*1090* Wait until the child has set its lock and then perform the1091* test.1092*/1093while (read(pfd[0], &ch, 1) != 1) {1094if (errno == EINTR)1095continue;1096err(1, "reading from pipe (child)");1097}10981099/*1100* fcntl should wait until the alarm and then return -1 with1101* errno set to EINTR.1102*/1103itv.it_interval.tv_sec = 0;1104itv.it_interval.tv_usec = 0;1105itv.it_value.tv_sec = 0;1106itv.it_value.tv_usec = 2;1107setitimer(ITIMER_REAL, &itv, NULL);11081109res = fcntl(fd, F_SETLKW, &fl);1110kill(pid, SIGTERM);1111safe_waitpid(pid);1112close(pfd[0]);1113close(pfd[1]);1114FAIL(!(res == 0 || (res == -1 && errno == EINTR)));11151116fl.l_start = 0;1117fl.l_len = 0;1118fl.l_type = F_UNLCK;1119if (fcntl(fd, F_SETLK, &fl) < 0)1120err(1, "F_UNLCK");1121}1122SUCCEED;1123}11241125/*1126* Test 14 - soak test1127*/1128static int1129test14(int fd, int argc, const char **argv)1130{1131#define CHILD_COUNT 201132/*1133* We create a set of child processes and let each one run1134* through a random sequence of locks and unlocks.1135*/1136int i, j, id, id_base;1137int pids[CHILD_COUNT], pid;1138char buf[128];1139char tbuf[128];1140int map[128];1141char outbuf[512];1142struct flock fl;1143struct itimerval itv;1144int status;11451146id_base = 0;1147if (argc >= 2)1148id_base = strtol(argv[1], NULL, 0);11491150printf("14 - soak test: ");1151fflush(stdout);11521153for (i = 0; i < 128; i++)1154map[i] = F_UNLCK;11551156for (i = 0; i < CHILD_COUNT; i++) {11571158pid = fork();1159if (pid < 0)1160err(1, "fork");1161if (pid) {1162/*1163* Parent - record the pid and continue.1164*/1165pids[i] = pid;1166continue;1167}11681169/*1170* Child - do some work and exit.1171*/1172id = id_base + i;1173srandom(getpid());11741175for (j = 0; j < 50; j++) {1176int start, end, len;1177int set, wrlock;11781179do {1180start = random() & 127;1181end = random() & 127;1182} while (end <= start);11831184set = random() & 1;1185wrlock = random() & 1;11861187len = end - start;1188fl.l_start = start;1189fl.l_len = len;1190fl.l_whence = SEEK_SET;1191if (set)1192fl.l_type = wrlock ? F_WRLCK : F_RDLCK;1193else1194fl.l_type = F_UNLCK;11951196itv.it_interval.tv_sec = 0;1197itv.it_interval.tv_usec = 0;1198itv.it_value.tv_sec = 0;1199itv.it_value.tv_usec = 3000;1200setitimer(ITIMER_REAL, &itv, NULL);12011202if (fcntl(fd, F_SETLKW, &fl) < 0) {1203if (errno == EDEADLK || errno == EINTR) {1204if (verbose) {1205snprintf(outbuf, sizeof(outbuf),1206"%d[%d]: %s [%d .. %d] %s\n",1207id, j,1208set ? (wrlock ? "write lock"1209: "read lock")1210: "unlock", start, end,1211errno == EDEADLK1212? "deadlock"1213: "interrupted");1214write(1, outbuf,1215strlen(outbuf));1216}1217continue;1218} else {1219perror("fcntl");1220}1221}12221223itv.it_interval.tv_sec = 0;1224itv.it_interval.tv_usec = 0;1225itv.it_value.tv_sec = 0;1226itv.it_value.tv_usec = 0;1227setitimer(ITIMER_REAL, &itv, NULL);12281229if (verbose) {1230snprintf(outbuf, sizeof(outbuf),1231"%d[%d]: %s [%d .. %d] succeeded\n",1232id, j,1233set ? (wrlock ? "write lock" : "read lock")1234: "unlock", start, end);1235write(1, outbuf, strlen(outbuf));1236}12371238if (set) {1239if (wrlock) {1240/*1241* We got a write lock - write1242* our ID to each byte that we1243* managed to claim.1244*/1245for (i = start; i < end; i++)1246map[i] = F_WRLCK;1247memset(&buf[start], id, len);1248if (pwrite(fd, &buf[start], len,1249start) != len) {1250printf("%d: short write\n", id);1251exit(1);1252}1253} else {1254/*1255* We got a read lock - read1256* the bytes which we claimed1257* so that we can check that1258* they don't change1259* unexpectedly.1260*/1261for (i = start; i < end; i++)1262map[i] = F_RDLCK;1263if (pread(fd, &buf[start], len,1264start) != len) {1265printf("%d: short read\n", id);1266exit(1);1267}1268}1269} else {1270for (i = start; i < end; i++)1271map[i] = F_UNLCK;1272}12731274usleep(1000);12751276/*1277* Read back the whole region so that we can1278* check that all the bytes we have some kind1279* of claim to have the correct value.1280*/1281if (pread(fd, tbuf, sizeof(tbuf), 0) != sizeof(tbuf)) {1282printf("%d: short read\n", id);1283exit(1);1284}12851286for (i = 0; i < 128; i++) {1287if (map[i] != F_UNLCK && buf[i] != tbuf[i]) {1288snprintf(outbuf, sizeof(outbuf),1289"%d: byte %d expected %d, "1290"got %d\n", id, i, buf[i], tbuf[i]);1291write(1, outbuf, strlen(outbuf));1292exit(1);1293}1294}1295}1296if (verbose)1297printf("%d[%d]: done\n", id, j);12981299exit(0);1300}13011302status = 0;1303for (i = 0; i < CHILD_COUNT; i++) {1304status += safe_waitpid(pids[i]);1305}1306if (status)1307FAIL(status != 0);13081309SUCCEED;1310}13111312/*1313* Test 15 - flock(2) semantcs1314*1315* When a lock holder has a shared lock and attempts to upgrade that1316* shared lock to exclusive, it must drop the shared lock before1317* blocking on the exclusive lock.1318*1319* To test this, we first arrange for two shared locks on the file,1320* and then attempt to upgrade one of them to exclusive. This should1321* drop one of the shared locks and block. We interrupt the blocking1322* lock request and examine the lock state of the file after dropping1323* the other shared lock - there should be no active locks at this1324* point.1325*/1326static int1327test15(int fd, __unused int argc, const __unused char **argv)1328{1329#ifdef LOCK_EX1330/*1331* We create a child process to hold the lock which we will1332* test. We use a pipe to communicate with the child.1333*1334* Since we only have one file descriptors and lock ownership1335* for flock(2) goes with the file descriptor, we use fcntl to1336* set the child's shared lock.1337*/1338int pid;1339int pfd[2];1340struct flock fl;1341char ch;1342int res;13431344if (pipe(pfd) < 0)1345err(1, "pipe");13461347pid = fork();1348if (pid < 0)1349err(1, "fork");13501351if (pid == 0) {1352/*1353* We are the child. We set a shared lock and then1354* write one byte back to the parent to tell it. The1355* parent will kill us when its done.1356*/1357fl.l_start = 0;1358fl.l_len = 0;1359fl.l_type = F_RDLCK;1360fl.l_whence = SEEK_SET;1361if (fcntl(fd, F_SETLK, &fl) < 0)1362err(1, "fcntl(F_SETLK) (child)");1363if (write(pfd[1], "a", 1) < 0)1364err(1, "writing to pipe (child)");1365pause();1366exit(0);1367}13681369/*1370* Wait until the child has set its lock and then perform the1371* test.1372*/1373if (read(pfd[0], &ch, 1) != 1)1374err(1, "reading from pipe (child)");13751376(void)dup(fd);1377if (flock(fd, LOCK_SH) < 0)1378err(1, "flock shared");13791380/*1381* flock should wait until the alarm and then return -1 with1382* errno set to EINTR.1383*/1384printf("15 - flock(2) semantics: ");13851386alarm(1);1387flock(fd, LOCK_EX);13881389/*1390* Kill the child to force it to drop its locks.1391*/1392kill(pid, SIGTERM);1393safe_waitpid(pid);13941395fl.l_start = 0;1396fl.l_len = 0;1397fl.l_type = F_WRLCK;1398fl.l_whence = SEEK_SET;1399res = fcntl(fd, F_GETLK, &fl);14001401close(pfd[0]);1402close(pfd[1]);1403FAIL(res != 0);1404FAIL(fl.l_type != F_UNLCK);14051406SUCCEED;1407#else1408return 0;1409#endif1410}14111412struct test_ctx {1413struct flock tc_fl;1414int tc_fd;1415};14161417static void *1418test16_func(void *tc_in)1419{1420uintptr_t error;1421struct test_ctx *tc = tc_in;14221423error = fcntl(tc->tc_fd, F_SETLKW, &tc->tc_fl);14241425pthread_exit((void *)error);1426}14271428#define THREADS 1014291430/*1431* Test 16 - F_SETLKW from two threads1432*1433* If two threads within a process are blocked on a lock and the lock1434* is granted, make sure things are sane.1435*/1436static int1437test16(int fd, __unused int argc, const __unused char **argv)1438{1439/*1440* We create a child process to hold the lock which we will1441* test. We use a pipe to communicate with the child.1442*/1443int pid;1444int pfd[2];1445struct test_ctx tc = { .tc_fd = fd };1446char ch;1447int i;1448int error;1449pthread_t thr[THREADS];14501451if (pipe(pfd) < 0)1452err(1, "pipe");14531454tc.tc_fl.l_start = 0;1455tc.tc_fl.l_len = 0;1456tc.tc_fl.l_type = F_WRLCK;1457tc.tc_fl.l_whence = SEEK_SET;14581459pid = fork();1460if (pid < 0)1461err(1, "fork");14621463if (pid == 0) {1464/*1465* We are the child. We set a write lock and then1466* write one byte back to the parent to tell it. The1467* parent will kill us when its done.1468*/1469if (fcntl(fd, F_SETLK, &tc.tc_fl) < 0)1470err(1, "F_SETLK (child)");1471if (write(pfd[1], "a", 1) < 0)1472err(1, "writing to pipe (child)");1473pause();1474exit(0);1475}14761477/*1478* Wait until the child has set its lock and then perform the1479* test.1480*/1481if (read(pfd[0], &ch, 1) != 1)1482err(1, "reading from pipe (child)");14831484/*1485* fcntl should wait until the alarm and then return -1 with1486* errno set to EINTR.1487*/1488printf("16 - F_SETLKW on locked region by two threads: ");14891490for (i = 0; i < THREADS; i++) {1491error = pthread_create(&thr[i], NULL, test16_func, &tc);1492if (error)1493err(1, "pthread_create");1494}14951496/*1497* Sleep, then kill the child. This makes me a little sad, but it's1498* tricky to tell whether the threads are all really blocked by this1499* point.1500*/1501sleep(1);1502kill(pid, SIGTERM);1503safe_waitpid(pid);1504close(pfd[0]);1505close(pfd[1]);15061507for (i = 0; i < THREADS; i++) {1508void *res;1509error = pthread_join(thr[i], &res);1510if (error)1511err(1, "pthread_join");1512FAIL((uintptr_t)res != 0);1513}15141515SUCCEED;1516}15171518struct test {1519int (*testfn)(int, int, const char **); /* function to perform the test */1520int num; /* test number */1521int intr; /* non-zero if the test interrupts a lock */1522};15231524static struct test tests[] = {1525{ test1, 1, 0 },1526{ test2, 2, 0 },1527{ test3, 3, 1 },1528{ test4, 4, 0 },1529{ test5, 5, 1 },1530{ test6, 6, 1 },1531{ test7, 7, 0 },1532{ test8, 8, 0 },1533{ test9, 9, 0 },1534{ test10, 10, 0 },1535{ test11, 11, 1 },1536{ test12, 12, 0 },1537{ test13, 13, 1 },1538{ test14, 14, 0 },1539{ test15, 15, 1 },1540{ test16, 16, 1 },1541};15421543int1544main(int argc, const char *argv[])1545{1546int testnum;1547int fd;1548int nointr;1549unsigned i;1550struct sigaction sa;1551int test_argc;1552const char **test_argv;15531554if (argc < 2) {1555errx(1, "usage: flock <directory> [test number] ...");1556}15571558fd = make_file(argv[1], 1024);1559if (argc >= 3) {1560testnum = strtol(argv[2], NULL, 0);1561test_argc = argc - 2;1562test_argv = argv + 2;1563} else {1564testnum = 0;1565test_argc = 0;1566test_argv = NULL;1567}15681569sa.sa_handler = ignore_alarm;1570sigemptyset(&sa.sa_mask);1571sa.sa_flags = 0;1572sigaction(SIGALRM, &sa, 0);15731574nointr = 0;1575#if defined(__FreeBSD__) && __FreeBSD_version < 8000401576{1577/*1578* FreeBSD with userland NLM can't interrupt a blocked1579* lock request on an NFS mounted filesystem.1580*/1581struct statfs st;1582fstatfs(fd, &st);1583nointr = !strcmp(st.f_fstypename, "nfs");1584}1585#endif15861587for (i = 0; i < nitems(tests); i++) {1588if (tests[i].intr && nointr)1589continue;1590if (!testnum || tests[i].num == testnum)1591tests[i].testfn(fd, test_argc, test_argv);1592}15931594return 0;1595}159615971598