After all the stuff that has been said on the last locking thread, I think it's better to restate the case than follow up. It's obvious from the messages in the last thread that a number of otherwise clever people have little understanding or knowledge of the concepts of file locking. I'm appending a hastily worked-over version of the section about locking from "Porting UNIX Software". Here's a summary of what I've been trying to say: All systems which do more than one thing at a time need file locking at some time or another. Since it involves cooperation between potentially unrelated processes, it's an obvious kernel function. Any "solution" requiring cooperation between processes isn't really a solution. As a result, I don't consider advisory locking to be real locking: it's just a kludge. FreeBSD is one of the few operating systems which doesn't have kernel-level locking. If we want to emulate other systems correctly, we *must* have advisory locking. This includes SCO UNIX, System V.4 and Linux. I suspect it also includes Microsoft. All this doesn't leave too much room for arguments about whether locking works or not: it works on all platforms except FreeBSD, and that's only because FreeBSD doesn't implement locking. As a result, I argue that we should implement locking. The questions are: how? I'd suggest three methods which can be individually enabled via sysctls: - System V style. We need this for compatibility with System V. The choice of mandatory or advisory locking depends on the file permissions. - Only mandatory locking. fcntl works as before, but locks are always mandatory, not advisory. I'm sure that this won't be popular, at least initially, but if you don't like it, you don't have to use it.y - Via separate calls to fcntl. fcntl currently has the following command values: #define F_DUPFD 0 /* duplicate file descriptor */ #define F_GETFD 1 /* get file descriptor flags */ #define F_SETFD 2 /* set file descriptor flags */ #define F_GETFL 3 /* get file status flags */ #define F_SETFL 4 /* set file status flags */ #define F_GETOWN 5 /* get SIGIO/SIGURG proc/pgrp */ #define F_SETOWN 6 /* set SIGIO/SIGURG proc/pgrp */ #define F_GETLK 7 /* get record locking information */ #define F_SETLK 8 /* set record locking information */ #define F_SETLKW 9 /* F_SETLK; wait if blocked */ We could add a F_SETMANDLOCK or some such. Any thoughts? Greg -- See complete headers for address, home page and phone numbers finger [EMAIL PROTECTED] for PGP public key
File locking FFile locking ____________ The Seventh Edition did not originally allow programs to coordinate concurrent access to a file. If two users both had a file open for modification at the same time, it was almost impossible to prevent disaster. This is an obvious disadvantage, and all modern versions of UNIX supply some form of file locking. Before we look at the functions that are available, it's a good idea to consider the various kinds of lock. There seem to be two of everything. First, the granularity is of interest: o file locking applies to the whole file. o range locking applies only to a range of byte offsets. This is sometimes misleadingly called record locking. With file locking, no other process can access the file when a lock is applied. With range locking, multiple locks can coexist as long as their ranges don't overlap. Secondly, there are two types of lock: o Advisory locks do not actually prevent access to the file. They work only if every participating process ensures that it locks the file before accessing it. If the file is already locked, the process blocks until it gains the lock. o mandatory locks prevent (block) read and write access to the file, but do not stop it from being removed or renamed. Many editors do just this, so even mandatory locking has its limitations. Finally, there are also two ways in which locks cooperate with each other: o exclusive locks allow no other locks that overlap the range. This is the only was to perform file locking, and it implies that only a single process can access the file at a time. These locks are also called also called write locks. Page 1 File locking o shared locks allow other shared locks to coexist with them. Their main purpose is to prevent an exclusive lock from being applied. In combination with mandatory range locking, a write is not permitted to a range covered by a shared lock. These locks are also called read locks. There are five different kinds of file or record locking in common use: o Lock files, also called dot locking, is a primitive workaround used by communication programs such as uucp and getty. It is independent of the system platform, but since it is frequently used we'll look at it briefly. It implements advisory file locking. o After the initial release of the Seventh Edition, a file locking package using the system call locking was introduced. It is still in use today on XENIX systems. It implements mandatory range locking. o BSD systems have the system call flock. It implements advisory file locking. o System V, POSIX.1, and more recent versions of BSD support range locking via the fcntl system call. BSD and POSIX.1 systems provide only advisory locking. System V supplies a choice of advisory or mandatory locking, depending on the file permissions. If you need to rewrite locking code, this is the method you should use. o System V also supplies range locking via the lockf library call. Again, it supplies a choice of advisory or mandatory locking, depending on the file permissions. The decision between advisory and mandatory locking in System V depends on the file permissions and not on the call to fcntl or lockf. The setgid bit is used for this purpose. Normally, in executables, the setgid bit specifies that the executable should assume the effective group ID of its owner group when execed. On files that do not have group execute permission, it specifies mandatory locking if it is set, and advisory locking if it is not set. For example, o A file with permissions 0764 (rwxrw-r--) will be locked with advisory locking, since its permissions include neither group execute nor setgid. o A file with permissions 0774 (rwxrwxr--) will be locked with advisory locking, since its permissions don't include setgid. o A file with permissions 02774 (rwxrwsr--) will be locked with advisory locking, since its permissions include both group execute and setgid. Page 2 File locking o A file with permissions 02764 will be locked with mandatory locking, since it has the setgid bit set, but group execute is not set. If you list the permissions of this file with ls -l, you get rwxrwlr-- on a System V system, but many versions of ls, including BSD and GNU versions, will list rwxrwSr--. Lock files __________ Lock files are the traditional method that uucp uses for locking serial lines. Serial lines are typically used either for dialing out, for example with uucp, or dialing in, which is handled by a program of the getty family. Some kind of synchronization is needed to ensure that both of these programs don't try to access the line at the same time. The other forms of locking we describe only apply to disk files, so we can't use them. Instead, uucp and getty create lock files. A typical lock file will have a name like /var/spool/uucp/LCK..ttyb, and for some reason these double periods in the name have led to the term dot locking. The locking algorithm is straightforward: if a process wants to access a serial line /dev/ttyb, it looks for a file /var/spool/uucp/LCK..ttyb. If it finds it, it checks the contents, which specify the process ID of the owner, and checks if the owner still exists. If it does, the file is locked, and the process can't access the serial line. If the file doesn't exist, or if the owner no longer exists, the process creates the file if necessary and puts its own process ID in the file. Although the algorithm is straightforward, the naming conventions are anything but standardized. When porting software from other platforms, it is absolutely essential that all programs using dot locking should be agreed on the lock file name and its format. Let's look at the lock file names for the device /dev/ttyb, which is major device number 29, minor device number 1. The ls -l listing looks like: $ ls -l /dev/ttyb crw-rw-rw- 1 root sys 29, 1 Feb 25 1995 /dev/ttyb Page 3 File locking This describes common conventions: | | System | Name | PID format -----------+--------------------------------+----------------- 4.3BSD | /usr/spool/uucp/LCK..ttyb | binary, 4 bytes 4.4BSD | /var/spool/uucp/LCK..ttyb | binary, 4 bytes System V.3 | /usr/spool/uucp/LCK..ttyb | ASCII, 10 bytes System V.4 | /var/spool/uucp/LK.032.029.001 | ASCII, 10 bytes A couple of points to note are: o The digits in the lock file name for System V.4 are the major device number of the disk on which /dev is located (32), the major device number of the serial device (29), and the minor device number of the serial device (1). o Some systems, such as SCO, have multiple names for terminal lines, depending on the characteristics which it should exhibit. For example, /dev/tty1a refers to a line when running without modem control signals, and /dev/tty1A refers to the same line when running with modem control signals. Clearly only one of these lines can be used at the same time: by convention, the lock file name for both devices is /usr/spool/uucp/LCK..tty1a. o The locations of the lock files vary considerably. Apart from those in the table, other possibilities are /etc/locks/LCK..ttyb, /usr/spool/locks/LCK..ttyb, and /usr/spool/uucp/LCK/LCK..ttyb. o Still other methods exist. See the file policy.h in the Taylor uucp distribution for further discussion. Lock files are unreliable. It is quite possible for two processes to go through this algorithm at the same time, both find that the lock file doesn't exist, both create it, and both put their process ID in it. The result is not what you want. Lock files should only be used when there is really no alternative. locking system call ___________________ locking comes from the original implementation introduced during the Seventh Edition. It is still available in XENIX. It implements mandatory range locking. Page 4 File locking int locking (int fd, int mode, long size); locking locks a block of data of length size bytes, starting at the current position in the file. mode can have one of the following values: | Parameter | Meaning ----------+-------------------------------------------------------------- LK_LOCK | Obtain an exclusive lock for the specified block. If any | part is not available, sleep until it becomes available. LK_NBLCK | Obtain an exclusive lock for the specified block. If any | part is not available, the request fails, and errno is set | to EACCES. LK_NBRLCK | Obtains a shared lock for the specified block. If any part | is not available, the request fails, and errno is set to | EACCES. LK_RLCK | Obtain a shared lock for the specified block. If any part | is not available, sleep until it becomes available. LK_UNLCK | Unlock a previously locked block of data. Figure 1: locking operation codes flock _____ flock is the weakest of all the lock functions. It provides only advisory file locking. #include <sys/file.h> (defined in sys/file.h) #define LOCK_SH 1 /* shared lock */ #define LOCK_EX 2 /* exclusive lock */ #define LOCK_NB 4 /* don't block when locking */ #define LOCK_UN 8 /* unlock */ int flock (int fd, int operation); flock applies or removes a lock on fd. By default, if a lock cannot be granted, the process blocks until the lock is available. If you set the flag LOCK_NB, flock returns immediately with errno set to EWOULDBLOCK if the lock cannot be granted. Page 5 File locking fcntl locking _____________ fcntl is a function that can perform various functions on open files. A number of these functions perform advisory record locking, and System V also offers the option of mandatory locking. All locking functions operate on a struct flock: struct flock { short l_type; /* lock type: read/write, etc. */ short l_whence; /* type of l_start */ off_t l_start; /* starting offset */ off_t l_len; /* len = 0 means until end of file */ long l_sysid; /* Only SVR4 */ pid_t l_pid; /* lock owner */ }; In this structure, o l_type specifies the type of the lock, listed below: | value | Function --------+------------------------------------- F_RDLCK | Acquire a read or shared lock. F_WRLCK | Acquire a write or exclusive lock. F_UNLCK | Clear the lock. Figure 2: flock.l_type values o The offset is specified in the same way as a file offset is specified to lseek: flock->l_whence may be set to SEEK_SET (offset is from the beginning of the file), SEEK_CUR (offset is relative to the current position) or SEEK_EOF (offset is relative to the current end of file position). All fcntl lock operations use this struct, which is passed to fcntl as the arg parameter. For example, to perform the operation F_FOOLK, you would write: struct flock flock; error = fcntl (myfile, F_FOOLK, &flock); The following fcntl operations relate to locking: Page 6 File locking o F_GETLK gets information on any current lock on the file. when calling, you set the fields flock->l_type, flock->l_whence, flock->l_start, and flock->l_len to the value of a lock that we want to set. If a lock that would cause a lock request to block already exists, flock is overwritten with information about the lock. The field flock->l_whence is set to SEEK_SET, and flock->l_start is set to the offset in the file. flock->l_pid is set to the pid of the process that owns the lock. If the lock can be granted, flock->l_type is set to F_UNLK and the rest of the structure is left unchanged, o F_SETLK tries to set a lock (flock->l_type set to F_RDLCK or F_WRLCK) or to reset a lock (flock->l_type set to F_UNLCK). If a lock cannot be obtained, fcntl returns with errno set to EACCES (System V) or EAGAIN (BSD and POSIX). o F_SETLKW works like F_SETLK, except that if the lock cannot be obtained, the process blocks until it can be obtained. o System V.4 has a further function, F_FREESP, which uses the struct flock, but in fact has nothing to do with file locking: it frees the space defined by flock->l_whence, flock->l_start, and flock->l_len. The data in this part of the file is physically removed, a read access returns EOF, and a write access writes new data. The only reason this operation uses the struct flock (and the reason we discuss it here) is because struct flock has suitable members to describe the area that needs to be freed. Many file systems allow data to be freed only if the end of the region corresponds with the end of file, in which case the call can be replaced with ftruncate. lockf _____ lockf is a library function supplied only with System V. Like fcntl, it implements advisory or mandatory range locking based on the file permissions. In some systems, it is implemented in terms of fcntl. It supports only exclusive locks: #include <unistd.h> int lockf (int fd, int function, long size); The functions are similar to those supplied by fcntl. l_type specifies the Page 7 File locking type of the lock, as shown below. | value | Function --------+-------------------------------------------- F_ULOCK | Unlock the range. F_LOCK | Acquire exclusive lock. F_TLOCK | Lock if possible, otherwise return status. F_TEST | Check range for other locks. Figure 3: lockf functions lockf does not specify a start offset for the range to be locked. This is always the current position in the file--you need to use lseek to get there if you are not there already. The following code fragments are roughly equiva- lent: flock->ltype = F_WRLK; /* lockf only supports write locks */ flock->whence = SEEK_SET; flock->l_start = filepos; /* this was set elsewhere */ flock->l_len = reclen; /* the length to set */ error = fcntl (myfile, F_GETLK, &flock); ...and lseek (myfile, SEEK_SET, filepos); /* Seek the correct place in the file */ error = lockf (myfile, F_LOCK, reclen); Which locking scheme? _____________________ As we've seen, file locking is a can of worms. Many portable software packages offer you a choice of locking mechanisms, and your system may supply a number of them. Which do you take? Here are some rules of thumb: o fcntl locking is the best choice, as long as your system and the package agree on what it means. On System V.3 and V.4, fcntl locking offers the choice of mandatory or advisory locking, whereas on other systems it only offers advisory locking. If your package expects to be able to set mandatory locking, and you're running, say, 4.4BSD, the package may not work correctly. If this happens, you may have to choose flock locking instead. o If your system doesn't have fcntl locking, you will almost certainly have either flock or lockf locking instead. If the package supports it, use it. Pure BSD systems don't support lockf, but some versions simulate it. Since Page 8 File locking lockf can also be used to require mandatory locking, it's better to use flock on BSD systems and lockf on System V systems. o You'll probably not come across any packages which support locking. If you do, and your system supports it, it's not a bad choice. o If all else fails, use lock files. This is a very poor option, though--it's probably a better idea to consider a more modern kernel. Page 9