.. _UnixFile:

.. raw:: html

   <script>ODSA.SETTINGS.DISP_MOD_COMP = true;ODSA.SETTINGS.MODULE_NAME = "UnixFile";ODSA.SETTINGS.MODULE_LONG_NAME = "The UNIX File Abstraction";ODSA.SETTINGS.MODULE_CHAPTER = "Processes and OS Basics"; ODSA.SETTINGS.BUILD_DATE = "2021-06-14 17:15:25"; ODSA.SETTINGS.BUILD_CMAP = false;JSAV_OPTIONS['lang']='en';JSAV_EXERCISE_OPTIONS['code']='java';</script>


.. |--| unicode:: U+2013   .. en dash
.. |---| unicode:: U+2014  .. em dash, trimming surrounding whitespace
   :trim:


.. This file is part of the OpenCSF eTextbook project. It was
.. auto-generated by scripts from the OpenDSA eTextbook project.
.. See https://OpenCSF.org for more details. OpenCSF is distributed
.. under a Creative Commons Attribution-NonCommercial 4.0 International
.. License (see http://creativecommons.org/licenses/by-nc/4.0/),
.. Copyright (c) 2019-2021 by Michael S. Kirkpatrick. OpenDSA is
.. distributed under an MIT open source license, Copyright (c) 2012-2021
.. by the OpenDSA Project Contributors.

.. avmetadata::
   :author: Michael S. Kirkpatrick
   :requires:
   :satisfies: UNIX File Abstraction
   :topic: File, Metadata

The UNIX File Abstraction
=========================

When multiple processes exist on a single machine, they rely on virtual memory to create the
illusion that they have sole access to the CPU; the context switch mechanism prevents one process
from accessing another process's register values, stack, heap, etc. However, processes ultimately do
not have sole access to the entire machine. There are many resources, such as a network interface,
storage devices, user input devices, and so on, that must be shared with other processes on the same
machine. As such, processes act as a unit of ownership for instances of access to these resources.

The :term:`UNIX file abstraction`, which is widely used in modern OS design, provides a uniform
interface to these various shared resources. This abstraction relies on two features: **a file is a
sequence of bytes** and **everything is a file**. It is important to emphasize that this definition
is different from the common usage of the term "file," which is typically associated with persistent
data storage. The key differences between this common usage and the UNIX file abstraction are as follows:

  * Arbitrary or bidirectional access to a file is not necessarily possible. In some cases, once a
    byte has been read from the file, that byte no longer exists in the file; there is no way to seek
    to a previous position in such files. Similarly, sequential access of the bytes in order may be
    required, with no way to skip ahead.

  * Files may not have names or persistent storage. Some files (such as those described in Chapter 3
    for :term:`interprocess communication`, commonly referred to as IPC) exist solely as in-memory
    constructs at run-time, identified only by an integer :term:`file descriptor`. Other files (such as
    ``/dev/random`` on UNIX and Linux systems) exist solely as an abstract interface to a hardware
    component or generate data at run-time on demand.

  * Files do not necessarily have structure or typing. Readers are likely family with persistent files
    that can be distinguished by a file extension. For instance, a file with the ``.pdf`` extension has
    a different internal structure than one with a ``.png`` extension; programs that read or write
    these files must make sure that the bytes adhere to a pre-defined semantic structure. However, in
    the UNIX file abstraction, this pre-defined structure does not exist; a file is `just` a sequence of bytes.
    
By removing so much contextual information about files, this abstraction might seem to lose much of
its meaning or utility. On the contrary, this abstraction greatly simplifies the work of dealing
with a variety of resources; there are certain operations (creating, deleting, opening, closing,
reading, writing) that are common to the lifecycle of all files. The UNIX file abstraction provides
a single, consistent interface for these operations, thus eliminating much of the complexity of
supporting many such resources.

Basic File Access
-----------------

The most basic operations for working with files are creating and opening them. For files that can
be identified with named locations in the file system directory structure (such as ``/dev/random``,
``/usr/bin/cksum``, or ``/home/csf/movies.csv``), we can use the ``open()`` function. The first
parameter is the path to the file; this path can be an *absolute path* (such as
``/dev/random``) or a *relative path* (such as ``../src/main.c``) that describes the location
relative to the current working directory. If the file is successfully opened, the return value from
``open()`` is the file descriptor, a non-negative integer value that other functions use to identify
the file. This value should typically be greater than 2, as the default behavior is to open three
files when a process is created: 0 (``STDIN_FILENO``) for standard input (such as reading from the
command prompt), 1 (``STDOUT_FILENO``) for standard output (such as writing out to the screen), and
2 (``STDERR_FILENO``) for standard error (also writing out to the screen).

.. topic:: C library functions – <fcntl.h>

   .. figure:: Images/CSF-Images-Library.png
      :align: left
      :width: 100%
      :alt: Decorative C library image

   ``int open(const char *path, int oflag, ...);``
     Open or create a file for reading or writing.

The second parameter (``oflag``) specifies how the file will be accessed by the current process.
`Table 2.2 <#tbl2-2>`_ shows the flags that may be passed as a bit-mask to ``open()``. Note that these flags do
not necessarily align with the common notion of file permissions; a file that is accessible for both
reading and writing may be opened in read-only mode (``O_RDONLY``). However, if the file permissions
do not allow the requested access, ``open()`` will return -1.

.. _tbl2-2:

.. raw:: html

   <center>
     <div class="row-10">
       <table class="table table-bordered">
         <thead class="jmu-dark-purple-bg text-light">
           <tr>
             <th class="py-0 center">Permission</th>
             <th class="py-0 center">Purpose</th>
           </tr>
         </thead>
         <tbody>
           <tr>
             <td class="py-0"><code>O_RDONLY</code></td>
             <td class="py-0">Open for reading only</td>
           </tr>
           <tr>
             <td class="py-0"><code>O_WRONLY</code></td>
             <td class="py-0">Open for writing only</td>
           </tr>
           <tr>
             <td class="py-0"><code>O_RDWR</code></td>
             <td class="py-0">Open for reading and writing</td>
           </tr>
           <tr>
             <td class="py-0"><code>O_NONBLOCK</code></td>
             <td class="py-0">Do not block on opening while waiting for data</td>
           </tr>
           <tr>
             <td class="py-0"><code>O_CREAT</code></td>
             <td class="py-0">Create the file if it does not exist; requires passing <code>mode_t</code> argument</td>
           </tr>
           <tr>
             <td class="py-0"><code>O_TRUNC</code></td>
             <td class="py-0">Truncate to size 0</td>
           </tr>
           <tr>
             <td class="py-0"><code>O_EXCL</code></td>
             <td class="py-0">Error if <code>O_CREAT</code> and the file exists</td>
           </tr>
         </tbody>
       </table>
       <p>Table 2.2: Flags for opening files</p>
     </div>
   </center>

For the common usage of the term "file," the ``O_NONBLOCK`` flag is the least intuitive in `Table
2.2 <#tbl2-2>`_, as this flag is normally used for other purposes. Specifically, this flag plays an important
role in IPC and network programming. When using a file to communicate with other processes (either
on the same machine or across the network), the default behavior for reading is for processes to
:term:`block <blocking I/O>` (pause) until the data has been received from the sender. The ``O_NONBLOCK`` flag
changes this behavior so that reading will immediately fail and the process can move on to other
work instead of waiting.

`Code Listing 2.12 <#cl2-12>`_ illustrates how the flags can be combined as a bitmask using the bitwise-or
(``|``). In this case, the file is also being created (``O_CREAT``) with a size of 0 bytes initially
(``O_TRUNC``) and the current process will have write-only access (``O_WRONLY``). This file will be
persistent and stored in the file system with 644 permissions (6 = read and write for the owner of
the file, 4 = read-only for the associated group and others); as such, the file could later be
opened in read-write mode. Note that this third parameter (``mode``) is required when creating a new
file, but is ignored at other times.

.. _cl2-12:

.. codeinclude:: Processes/CodeListing-2.12.c
   :linenos: true

Once the file has been opened, it can be read from. The ``read()`` function takes three parameters:
the file descriptor, the address of a buffer in memory to read the bytes into, and the maximum
number of bytes to read. [#f15]_ The value returned from ``read()`` indicates the actual number of
bytes successfully read, which may be fewer than the ``nbyte`` parameter. (Calling ``read()`` with
``nbyte`` set to 100 on a file that only contains 10 bytes of data will return 10, not 100.)
Finally, when the process is finished working with a file, the ``close()`` function will release any
associated resources in the kernel or the C library data that have been allocated for this process. 

.. topic:: C library functions – <unistd.h>

   .. figure:: Images/CSF-Images-Library.png
      :align: left
      :width: 100%
      :alt: Decorative C library image

   ``ssize_t read(int fildes, void *buf, size_t nbyte);``
     Read up to nbyte bytes from a file into the buffer identified by buf.

   ``int close(int fildes);``
     Deletes a file descriptor.

.. topic:: Bug Warning

   .. figure:: Images/CSF-Images-BugWarning.png
      :align: left
      :width: 90%
      :alt: Decorative bug warning

   There are several key aspects of working with files that are easy to underestimate. First and
   foremost is the importance of using a correct value for the ``nbyte`` parameter of ``read()``. This
   parameter always indicates the maximum number of bytes to read and it should never indicate more
   than the size of the allocated buffer pointer. :term:`Buffer overflows <buffer overflow>` are some
   of the most dangerous and persistent sources of software vulnerabilities, and passing an incorrect
   parameter to ``read()`` is a common culprit. Consider the following example:

   .. codeinclude:: Processes/BugWarning-2.1.c
      :linenos: true

   The problem here is a misunderstanding of the ``sizeof()`` keyword, which returns the size of the
   specified parameter. The misunderstanding is that ``sizeof(buffer)`` returns the size of a pointer
   variable (8 bytes on a 64-bit system), not the size of the dynamically allocated buffer on the
   heap. (Contrast this with lines 5 and 6 in `Code Listing 2.13 <#cl2-13>`_ below.) As such, this code is trying
   to read up to 8 bytes of data into a buffer than can only hold 2 bytes. The result is that
   ``read()`` will simply copy the additional 6 bytes into the memory `after` the end of the buffer,
   potentially corrupting other data.

   There are other frequent, though less serious, problems with using files. One (which is also in the
   example above) is to call ``read()`` without checking its return value; programmers often assume
   that the number of bytes read is the same as the number of bytes requested, which is not
   necessarily true. To illustrate this, consider the possibility of calling ``read()`` on a file that
   has been opened in ``O_WRONLY`` mode; ``read()`` would return -1 to indicate the operation failed.
   Another problem is failing to call ``close()``; this causes memory leaks, as allocated data is not
   freed up appropriately. On the other hand, another problem can arise when a file descriptor is used
   after the file has been closed; this can cause future reads to fail or (potentially even worse) to
   read from the wrong file.

`Code Listing 2.13 <#cl2-13>`_ illustrates how to open, read from, and close a file. In this example, we are
reading from a special file known as ``/dev/random``. This file can be used to generate a sequence
of random numbers one byte at a time; every time this code runs, the result should be different.
Note that the file is closed on line 13, but the data is not used by the program until line 17. This
is not a problem, as the data was read into the process's memory; that is, the ``read()`` operation
has made a copy of the data on the stack, so access to the file is no longer necessary.

.. _cl2-13:

.. codeinclude:: Processes/CodeListing-2.13.c
   :linenos: true

Some files, particularly IPC and device interface files, require special handling when reading.
Recall that the default behavior for open files is to block until data is ready; this behavior is
undesirable when other productive work could be done. For instance, a web server that is blocking
while trying to read data from one client could be missing out on connection requests from other
clients. The ``poll()`` function provides a useful interface for avoiding this situation.

.. topic:: C library functions – <poll.h>

   .. figure:: Images/CSF-Images-Library.png
      :align: left
      :width: 100%
      :alt: Decorative C library image

   ``int poll(struct pollfd fds[], nfds_t nfds, int timeout);``
     Examine an array of file descriptors to determine if some are ready for I/O.

The first argument to ``poll()`` consists of an array of ``struct pollfd`` instances, the second
parameter is the length of the array, and the ``timeout`` designates a maximum amount of time
(measured in milliseconds) to wait for input to be ready. The fields of the ``struct pollfd`` are
shown below. For each ``struct`` in the array, the ``fd`` field designates a file descriptor to
monitor for input or output events, and the ``events`` field designates the events to wait for.
Typically, ``events`` is set to the constant ``POLLIN`` to indicate a check for the presence of
normal data that can be read without blocking. The ``revents`` field is set by the call to
``poll()``.

.. codeinclude:: Processes/Pollfd.h
   :linenos: true

`Code Listing 2.14 <#cl2-14>`_ shows how to use ``poll()`` to check for available data. If ``poll()`` returns 0,
then the requested event (available input data) has not occurred before the timeout expired. The
``revents`` field would be set to a value to indicate why the ``poll()`` failed. For instance,
``POLLHUP`` indicates the device has been disconnected, ``POLLNVAL`` indicates the file descriptor
is not open, and ``POLLERR`` indicates an error has occurred with the device.

.. _cl2-14:

.. codeinclude:: Processes/CodeListing-2.14.c
   :linenos: true

Working with Files
------------------

In addition to reading, programs typically need to write to a file. The arguments to ``write()`` are
identical to those for ``read()``. Unlike ``read()``, there is not really a concern with buffer
overflow with ``write()``, as data is being sent away from the current process; the kernel buffers
on the other end will prevent such errors. However, checking the return value from ``write()`` is as
important as it is with ``read()`` to make sure that all of the intended data was written
successfully; this is especially true when writing large pieces of data. `Code Listing 2.15 <#cl2-15>`_
illustrates how to write to a file. Note that writing to the end of a persistent file will cause it
to grow. In this example, the file is created to be empty (``O_TRUNC``), but writing six bytes
creates a file of size six (the last byte is the null terminator ``'\0'``).

.. topic:: C library functions – <unistd.h>

   .. figure:: Images/CSF-Images-Library.png
      :align: left
      :width: 100%
      :alt: Decorative C library image

   ``ssize_t write(int fildes, const void *buf, size_t nbyte);``
     Write up to nbyte bytes from a buffer into the specified file.

.. _cl2-15:

.. codeinclude:: Processes/CodeListing-2.15.c
   :linenos: true

If the file supports arbitrary accesses, the ``lseek()`` function will change the file's internal
location information to a specified target. The ``offset`` can be specified as either a positive or
negative value. The ``whence`` parameter, which takes a limited number of possible values, plays an
important role in determining this location. If ``whence`` if set to ``SEEK_SET``, then the
``offset`` argument is the exact number of bytes into the file to use as the location. Setting
``whence`` to ``SEEK_CUR`` will add the ``offset`` to the current location number; a negative
``offset`` will seek backwards, while a positive value seeks forward. Lastly, setting ``whence`` to
``SEEK_END`` will add the ``offset`` to the size of the file; using a negative offset moves the
location to the number of bytes before the end of the file. Whichever value is passed, the final
location must be positive. If the location is larger than the file size, performing a write at that
point will increase the file size accordingly. Any gap between the existing end of the file and the
new data will be filled with null bytes.

.. topic:: C library functions – <unistd.h>

   .. figure:: Images/CSF-Images-Library.png
      :align: left
      :width: 100%
      :alt: Decorative C library image

   ``off_t lseek(int fildes, off_t offset, int whence);``
     Reposition the offset of a file descriptor to a specified location.

.. _cl2-16:

.. codeinclude:: Processes/CodeListing-2.16.c
   :linenos: true

`Code Listing 2.16 <#cl2-16>`_ shows the effect of using ``lseek()`` and ``write()`` on the file created by Code
Listing 2.15. The file initially contained six bytes (``'h'``, ``'e'``, ``'l'``, ``'l'``, ``'o'``,
``'\0'``).  The seek on line 7 places the internal location to offset 10. The ``write()`` on line 11,
then, extends the file size to include the new data, as well as the padding of null bytes; the new
file size would then be 18 bytes. As such, the ``read()`` on line 20 requests 20 bytes but only gets
18. Printing the final file with ``hexdump`` shows the results:

.. codeinclude:: Processes/HexdumpTxt.txt

Accessing File Metadata
-----------------------

When working with files, it is often important to access :term:`metadata` – information about the
file – rather than the contents about the file itself. For instance, when reading a persistent file
into memory from storage, knowing the file's size is necessary for allocating memory for the buffer.
As another example, consider an *intrusion detection* program that is responsible for
monitoring a file system for security threats or attacks; this program might check for changes to
the associated permissions or the user ID that is considered the owner of the file.

.. topic:: C library functions – <sys/stat.h>

   .. figure:: Images/CSF-Images-Library.png
      :align: left
      :width: 100%
      :alt: Decorative C library image

   ``int fstat(int fildes, struct stat *buf);``
     Get status information about a file given an open file descriptor.

   ``int stat(const char *path, struct stat *buf);``
     Get status information about a file.

The ``fstat()`` and ``stat()`` functions provide an interface for accessing file metadata. Note that
``stat()`` uses the path name of the file within the directory structure, which is appropriate for
the common notion of a file as persistent storage; however, ``fstat()`` works on any file
descriptor, which allows you to examine the metadata of any file, including unnamed IPC or device
files. Both functions take a pointer to a ``struct stat``, writing the file metadata into this buffer.

.. codeinclude:: Processes/StructStat.h
   :linenos: true

This ``struct`` definition contains additional fields based on the particular operating system, but
the ones shown here are consistent across multiple platforms. A full discussion of all of these
fields is beyond the scope of this book, but a few of them are particularly important. To start,
consider the ``st_ino`` and ``st_nlink`` fields. Each file stored on typical storage device (USB
drive, hard drive, etc.) is uniquely identified by an :term:`inode`, an on-disk data structure that
contains the metadata; each inode is uniquely identified by an inode number (``st_ino``). However,
the file might have multiple human-readable names in the directory structure. These names –
*links* (also called *hard links*) – all point to the same file
contents; the ``st_nlink`` field indicates the number of links that exist to a single file. With
hard links, there is only one file; there are just multiple references to the same location. In
contrast a *symbolic link* is a distinct file that is not represented in the inode. See
Appendix A for a longer discussion of inodes and links.

Another key field of the ``struct stat`` is the ``st_mode`` field. The most common use of this field
is to set permissions for accessing the file. These permissions include combinations of read, write,
and execute for the owner of the file (the user), the associated group, or everyone else. The
``st_mode`` field also stores additional permissions and information about the file; for instance,
this field can be used to distinguish symbolic links, regular files, or directories. `Table 2.3 <#tbl2-3>`_ shows
the standard list of bitmask values that can be combined in the ``st_mode`` field.

.. _tbl2-3:

.. raw:: html

   <center>
   <div class="row">
     <div class="col-6 align-top">
       <table class="table table-bordered">
         <thead class="jmu-dark-purple-bg text-light">
           <tr>
             <th class="py-0 center">Name</th>
             <th class="py-0 center">Bitmask</th>
             <th class="py-0 center">Description</th>
           </tr>
         </thead>
         <tbody>
           <tr>
             <td class="py-0"><code>S_IRUSR</code></td>
             <td class="py-0"><code>000400</code></td>
             <td class="py-0">Read (user)</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IWUSR</code></td>
             <td class="py-0"><code>000200</code></td>
             <td class="py-0">Write (user)</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IXUSR</code></td>
             <td class="py-0"><code>000100</code></td>
             <td class="py-0">Execute (user)</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IRGRP</code></td>
             <td class="py-0"><code>000040</code></td>
             <td class="py-0">Read (group)</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IWGRP</code></td>
             <td class="py-0"><code>000020</code></td>
             <td class="py-0">Write (group)</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IXGRP</code></td>
             <td class="py-0"><code>000010</code></td>
             <td class="py-0">Execute (group)</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IROTH</code></td>
             <td class="py-0"><code>000004</code></td>
             <td class="py-0">Read (other)</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IWOTH</code></td>
             <td class="py-0"><code>000002</code></td>
             <td class="py-0">Write (other)</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IXOTH</code></td>
             <td class="py-0"><code>000001</code></td>
             <td class="py-0">Execute (other)</td>
           </tr>
         </tbody>
       </table>
     </div>
     <div class="col-6 align-top">
       <table class="table table-bordered">
         <thead class="jmu-dark-purple-bg text-light">
           <tr>
             <th class="py-0 center">Name</th>
             <th class="py-0 center">Bitmask</th>
             <th class="py-0 center">Description</th>
           </tr>
         </thead>
         <tbody>
           <tr>
             <td class="py-0"><code>S_IFIFO</code></td>
             <td class="py-0"><code>010000</code></td>
             <td class="py-0">Named pipe (IPC)</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IFCHR</code></td>
             <td class="py-0"><code>020000</code></td>
             <td class="py-0">Character device (terminal)</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IFDIR</code></td>
             <td class="py-0"><code>040000</code></td>
             <td class="py-0">Directory file type</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IFBLK</code></td>
             <td class="py-0"><code>006000</code></td>
             <td class="py-0">Block device (disk drive)</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IFREG</code></td>
             <td class="py-0"><code>100000</code></td>
             <td class="py-0">Regular file type</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IFLNK</code></td>
             <td class="py-0"><code>120000</code></td>
             <td class="py-0">Symbolic link</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_IFSOCK</code></td>
             <td class="py-0"><code>140000</code></td>
             <td class="py-0">Socket (IPC, networks)</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_ISUID</code></td>
             <td class="py-0"><code>004000</code></td>
             <td class="py-0">Setuid (<code>SUID</code>) bit</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_ISGID</code></td>
             <td class="py-0"><code>002000</code></td>
             <td class="py-0">Setgid (<code>SGID</code>) bit</td>
           </tr>
           <tr>
             <td class="py-0"><code>S_ISVTX</code></td>
             <td class="py-0"><code>001000</code></td>
             <td class="py-0">Sticky bit</td>
           </tr>
         </tbody>
       </table>
     </div>
   </div>
   <p>Table 2.3: Bitmasks used in the st_mode field</p>
   </center>

For example, the hello.c file above would have the bitmask ``100644`` (displayed as ``-rw-r--r--``
by the ``ls -l`` command), as it is a regular file (``100000``) with read/write permissions for the
user and read for group and others. The symlink.c would have ``st_mode`` ``120755`` (displayed as
``lrwxr-xr-x``). Note that the first character in the displayed version indicates the type of file
(``-`` for ``S_IFREG``, ``l`` for ``S_IFLNK``, ``d`` for ``S_IFDIR``, and so on).

.. topic:: Note

   .. figure:: Images/CSF-Images-Note.png
      :align: left
      :width: 100%
      :alt: Decorative note icon

   The ``SUID``, ``SGID``, and sticky bits have complex meanings and interpretations. One source of
   their complexity is that ``SUID`` only affects executable regular files, the sticky bit (which is
   mostly obsolete and has changed over time) only affects, and ``SGID`` affects both executables and
   directories! These meanings can be summarized as follows:

     * ``SUID``: Processes created with this executable will inherit the user ID of the file's owner,
       rather than the user ID of the user executing the program.
     * ``SGID`` (regular file): Processes created with this executable will inherit the group ID of the
       file's group, rather than the group ID of the user executing the program.
     * ``SGID`` (directory): Files and subdirectories created in this directory will inherit the group ID
       of this directory.
     * Sticky bit (modern usage): Files in this directory can only be deleted by the user who is considered the owner of the file.
     
   When these bits are set on a file, ``ls -l`` displays them by overlaying them on top of the execute
   bits in the permission field, using an ``'s'`` in the user field for ``SUID``, ``'s'`` in the group
   field for ``SGID``, and ``'t'`` in the other field for the sticky bit; if the corresponding ``'x'``
   bit is present, a lower-case letter is used, while an upper-case letter indicates the ``'x'`` bit
   is absent. For instance, ``rwsr-x---`` would indicate both ``S_IXUSR`` and ``S_ISUID`` are set;
   ``rw-r-Sr--`` would mean that ``S_IGUID`` is set but ``S_IXGRP`` is not.

`Code Listing 2.17 <#cl2-17>`_ illustrates how to use ``fstat()`` to investigate a file's metadata. The address
of the local variable ``info`` is passed to ``fstat()`` to collect the metadata on line 9. Lines 13 –
15 are using the bitwise-and operator (``&``) to determine if certain permission bits are set; the
result would be non-zero (true) if the bit is set but would be zero (false) if not. Lines 18 and 19
demonstrate a very common technique when reading in files. Line 19 uses the ``st_size`` field to
allocate the exact amount of space needed to read in the full file contents, then line 20 reads in
exactly that number of bytes. Once the file is read into memory, it can be accessed in a variety of
ways. Since this file is an ASCII-formatted CSV file, it can be manipulated just like a normal
string. Line 24 uses ``strtok()`` to split this string at the first instance of the newline
character (``'\n'``); line 25 can then print just that line as a string. This change only affects
the in-memory buffer, and it does not change the contents of the original file stored on disk. 

.. _cl2-17:

.. codeinclude:: Processes/CodeListing-2.17.c
   :linenos: true

.. topic:: Note

   .. figure:: Images/CSF-Images-Note.png
      :align: left
      :width: 100%
      :alt: Decorative note icon

   The traditional UNIX permission structure—assigning permissions based only on the user, group, or
   other—is inflexible and not well suited for many applications. For example, consider two user that
   are collaborating on a project. These two users both need full permissions to read and write to a
   file, but they do not want to make the file publicly accessible otherwise. Under the traditional
   approach, a system administrator could create a group containing these two users; the users could
   then set permissions based on the group ID. The problem is that each user can only be assigned to a
   single group. If these users also have similar collaborations with different users on the same
   system, they cannot use the same approach.

   To fix this problem, many modern systems support *access control lists*
   (ACLs). Using ACLs, users can grant or revoke permissions to other users on an individual basis. In
   addition, ACLs allow the same user to be a member of multiple groups. Rather than using the
   traditional ls and chmod commands to view and change permissions, ACLs use the ``getfacl`` and
   ``setfacl`` commands. Consider the following example of these two commands.

   .. codeinclude:: Processes/ACL.txt

   For each file and directory, there is an assigned owner and group, just like the traditional UNIX
   permissions, as indicated by the lines beginning with ``#``. The other lines are individual
   permissions that have been set for the particular file. The user and group permissions contain
   three fields separated by a colon (``:``). The middle field indicates which user or group and the
   third field indicates the permissions (read, write, execute); if the user or group field is empty,
   the permission applies to the owner or group of the file. Directories can also have ``default``
   permission lines; any time a file is created in this directory, the specified default permissions
   are automatically assigned to it. When using ``setfacl`` to add, change, or remove a permission
   entry, the terms ``user``, ``group``, and ``default`` can be abbreviated as simply ``u``, ``g``, or
   ``d``.

.. [#f15] To reiterate the notion of files as just a sequence of bytes, note that the file
   descriptor here was not necessarily the value returned from ``open()`` as described above. For
   files that do not correspond to named locations in the directory tree structure, the file
   descriptor may be created by a different function (such as ``pipe()`` or ``socket()`` as described
   in Chapters 3 and 4).

.. avembed:: Exercises/Processes/ProcFileSumm.html ka
   :module: UnixFile
   :points: 1.0
   :required: True
   :exer_opts: JXOP-debug=true&amp;JOP-lang=en&amp;JXOP-code=java
   :long_name: UNIX file questions
   :threshold: 5

