next up previous
Next: 2.7 User and Group Identities Up: 2. Environmentally Friendly I/O Previous: 2.5 Multiplexing with Select

  
2.6 Files, Links, and Directories

Given a file descriptor, it is possible to recover the filename that was originally passed to open if that is available in the current process, as the string-valued expression *

filename fd
As with the other I/O routines, fd can in fact be that original filename, in which case this call merely checks that the file is currently open and returns the filename. It is also possible to obtain the file descriptor corresponding to an open file as designated by its name or file descriptor, as the integer-valued expression *
fileno fd
Again, this is just a checking identity function for open file descriptors, sometimes used in the idiom *
fd := fileno open (name, ...);
as a way of ensuring that an om return from open immediately causes a run-time error.

To facilitate the use of SETL in a ``shell'' programming role without the need of resorting to running an external command, the boolean-valued expression *

fexists s
for any string s indicates whether a file named by the contents of s exists in the local environment. Similarly, the integer-valued expression *
fsize s
is the number of bytes in the file named in the string s, if the file exists.

Ordinary files are created automatically when they are first opened for writing, but the creation and manipulation of ``links'' requires the use of certain special functions. A ``hard'' link is created atomically by the following routine, if existing is a string naming a file that exists before the call, and new names a file that does not exist before the call: *

link (existingnew);           -- existing and new are filenames
After the call, existing and new are equivalent references to the same file. If existing does not exist before the call, or if new already exists, last_error is set to something other than no_error (see Section 2.13 [Normal and Abnormal Endings]).

In a local filesystem, link can be used to implement a ``test and set'' mutex: assuming existing exists, then if new also exists, the operation will fail, but if it doesn't exist, then it will be created and the calling process will ``own'' the mutex lock until it releases it by calling unlink (see below) on new.

Similarly, a ``symbolic'' link can be created by the call *

symlink (snew);               -- s is an arbitrary string
There is no requirement that a file named s exist beforehand in order for this call to succeed, although it will fail if new already exists. Thus symlink can be used to implement a mutex in much the same manner as link, but with the added benefit that new can be ``pointed at'' an arbitrary string. This may, for example, embed information about the process that currently holds the lock, a technique that is used by the vc-toplev.setl program listed in Section A.42 [vc-toplev.setl].

In order to find out whether a particular symbolic link currently exists in the filesystem under a name given in a string s, the boolean-valued expression *

lexists s
is used. Note that when fexists is applied to a symbolic link, it interrogates the existence of the file referred to by that link, whereas when lexists is applied to a symbolic link, it merely interrogates the existence of the link itself.

The use of lexists on a name intended to represent a mutex lock is unlikely to occur in code that is free of race conditions. For race-free operation, the following sequence has the requisite test-and-set atomicity: *

clear_error;
symlink (my_idlockfile);
if last_error = no_error then
  -- we have the lock
    ...
  unlink (lockfile);   -- release the lock (unlink is defined below)
else
  -- some other process has the lock
    ...
end if;

When s is known to name a symbolic link, its associated text is available as the string-valued *

readlink s
If s names something that exists but is not a symbolic link, or something that does not exist, then readlink returns om and sets last_error according to which case applies. By contrast, an attempt to read data from s will fail if s names something nonexistent or a symbolic link pointing to something nonexistent (ultimately, since symbolic links can point to filesystem entries that are themselves symbolic links, up to some system-imposed limit on the number of indirection levels).

Finally, *

unlink (s);
can be used to destroy a hard link or a symbolic link. When the last hard link to a file is destroyed in Unix filesystems, the file itself is destroyed as soon as the last process that has it open closes it. (Creating a file is the act which creates the first hard link to the file.) Thus unlink is the standard routine for destroying any file in Unix.

Sometimes, a program will desire the use of a ``scratch'' file, though this need is declining with the increase in virtual memory sizes. Because these will often have to reside in a shared public area, the primary consideration becomes simply that of choosing a unique filename. The Unix routine tmpnam is the scratch filesystem analogue of the SETL newat generator, and the nullary SETL primitive *

tmpnam()            -- trailing ``()'' optional, as usual
uses tmpnam to yield a string filename that is ``reserved'' for the calling program.

The current working directory, which is a notion supported by every modern operating system, is available in SETL as the string-valued *

getwd               -- trailing ``()'' optional
and can be changed using the call *
chdir (dirname);

The umask routine pertaining to file access rights is explained in Section 2.7 [User and Group Identities].


next up previous
Next: 2.7 User and Group Identities Up: 2. Environmentally Friendly I/O Previous: 2.5 Multiplexing with Select
David Bacon
1999-12-10