CS 355 - Systems Programming:
Unix/Linux with C

File Properties

Reference: Molay, Understanding Unix/Linux Programming, Chapter 3.6-3.9

The ls -l command

Our standard approach to studying a Unix system command:

  1. What does ls -l do?
  2. How does ls -l work?
  3. Can I write ls -l?

What does ls -l do?

ls -l prints a list of file names located in the current directory in the long format

$ ls -l
total 72
-rwxr-xr-x  1 stan  staff  8772 Oct  6 10:31 a.out
-rw-r--r--@ 1 stan  staff  1040 Sep 29 11:55 cp.c
drwxr-xr-x  2 stan  staff   512 Oct 12 10:37 dir
-rw-r--r--@ 1 stan  staff   637 Oct  6 10:33 ls1.c
-rw-r--r--@ 1 stan  staff  1067 Sep 29 10:36 stuff.txt
-rw-r--r--  1 stan  staff  1067 Sep 29 10:38 stuff1.txt
-rw-r--r--@ 1 stan  staff  1246 Sep 22 12:59 who1.c
-rw-r--r--@ 1 stan  staff  2392 Sep 22 12:46 who2.c

Read the manual:

LS(1)                     BSD General Commands Manual                    LS(1)

NAME
     ls -- list directory contents

     . . .

     If the -l option is given, the following information is displayed for
     each file: file mode, number of links, owner name, group name, number of
     bytes in the file, abbreviated month, day-of-month file was last modi-
     fied, hour file last modified, minute file last modified, and the path-
     name.  In addition, for each directory whose contents are displayed, the
     total number of 512-byte blocks used by the files in the directory is
     displayed on a line by itself, immediately before the information for the
     files in the directory.  If the file or directory has extended
     attributes, the permissions field printed by the -l option is followed by
     a '@' character.  Otherwise, if the file or directory has extended secu-
     rity information (such as an access control list), the permissions field
     printed by the -l option is followed by a '+' character.

     . . .

Each output line produced by ls -l consists of these seven fields:

mode
The first character represents the type of the file: either "-" (regular file) or "d" (directory). Remaining nine characters are the access permissions to read, write, and execute a file for three classes of users: user, group, and everyone else. On OSX, the "@" indicates that the file has extended permissions.
links
Number of links to the file.
owner
User id of the owner.
group
User group id, to which the file belongs.
size
Number of bytes in the file. For directories, it is a multiple of 512.
last-modified
Date/time when the file was last modified.
name
Then name of the file.

How does ls -l do it?

ls -l uses the stat system call:

int stat(char *fname, struct stat *bufp);
Copies the information about the file fname into the structure pointed by bufp. Returns 0 if success, or -1 in case of an error.
struct stat {
    dev_t     st_dev;     /* ID of device containing file */
    ino_t     st_ino;     /* inode number */
    mode_t    st_mode;    /* protection */
    nlink_t   st_nlink;   /* number of hard links */
    uid_t     st_uid;     /* user ID of owner */
    gid_t     st_gid;     /* group ID of owner */
    dev_t     st_rdev;    /* device ID (if special file) */
    off_t     st_size;    /* total size, in bytes */
    blksize_t st_blksize; /* blocksize for file system I/O */
    blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
    time_t    st_atime;   /* time of last access */
    time_t    st_mtime;   /* time of last modification */
    time_t    st_ctime;   /* time of last status change */
};

The following mask values are defined for the file type component of the st_mode field:

S_IFMT     0170000   bit mask for the file type bit fields

S_IFSOCK   0140000   socket
S_IFLNK    0120000   symbolic link
S_IFREG    0100000   regular file
S_IFBLK    0060000   block device
S_IFDIR    0040000   directory
S_IFCHR    0020000   character device
S_IFIFO    0010000   FIFO

The following mask values are defined for the file permissions component of the st_mode field:

S_ISUID   0004000   set-user-ID bit
S_ISGID   0002000   set-group-ID bit
S_ISVTX   0001000   sticky bit

S_IRWXU   0000700   mask for file owner permissions

S_IRUSR   0000400   owner has read permission
S_IWUSR   0000200   owner has write permission
S_IXUSR   0000100   owner has execute permission

S_IRWXG   0000070   mask for group permissions
S_IRGRP   0000040   group has read permission
S_IWGRP   0000020   group has write permission
S_IXGRP   0000010   group has execute permission

S_IRWXO   0000007   mask for permissions for others (not in group)
S_IROTH   0000004   others have read permission
S_IWOTH   0000002   others have write permission
S_IXOTH   0000001   others have execute permission

The set-group-ID bit S_ISGID has several special uses. For a directory: files created there inherit their group ID from the directory, not from the effective group ID of the creating process, and directories created there will also get the S_ISGID bit set. For a file that does not have the group execution bit S_IXGRP set, the set-group-ID bit indicates mandatory file/record locking.

The sticky bit S_ISVTX on a directory means that a file in that directory can be renamed or deleted only by the owner of the file, by the owner of the directory, and by a privileged process.

Can I write ls -l?

How to use masking to decode permission bits:

void mode_to_letters( int mode, char str[] )
{
    strcpy( str, "----------" );           /* default=no perms */

    if ( S_ISDIR(mode) )  str[0] = 'd';    /* directory?       */
    if ( S_ISCHR(mode) )  str[0] = 'c';    /* char devices     */
    if ( S_ISBLK(mode) )  str[0] = 'b';    /* block device     */

    if ( mode & S_IRUSR ) str[1] = 'r';    /* 3 bits for user  */
    if ( mode & S_IWUSR ) str[2] = 'w';
    if ( mode & S_IXUSR ) str[3] = 'x';

    if ( mode & S_IRGRP ) str[4] = 'r';    /* 3 bits for group */
    if ( mode & S_IWGRP ) str[5] = 'w';
    if ( mode & S_IXGRP ) str[6] = 'x';

    if ( mode & S_IROTH ) str[7] = 'r';    /* 3 bits for other */
    if ( mode & S_IWOTH ) str[8] = 'w';
    if ( mode & S_IXOTH ) str[9] = 'x';
}

How to produce a line containing file information:

void show_file_info( char *filename, struct stat *info_p )
{
   char   *uid_to_name(), *ctime(), *gid_to_name();
   void   mode_to_letters();
   char  modestr[11];

   mode_to_letters( info_p->st_mode, modestr );

   printf( "%s"    , modestr );
   printf( "%4d "  , (int) info_p->st_nlink);
   printf( "%-8s " , uid_to_name(info_p->st_uid) );
   printf( "%-8s " , gid_to_name(info_p->st_gid) );
   printf( "%8ld " , (long)info_p->st_size);
   printf( "%.12s ", 4+ctime(&info_p->st_mtime));
   printf( "%s\n"  , filename );
}

Functions uid_to_name() and gid_to_name() are used to obtain printable user and group names; see Molay pp. 88-91 for more detail.

Setting and modifying the properties of a file

Type of the file (whether it's a file or directory) is established when it is created and cannot be changed afterwards.

File type (e.g. "rwxr--r--") is established at the time of its creation:

fd = creat("newfile", 0744);

Useful system calls to modify the properties of a file include:

int chmod(char *fname, mode_t mode);
Changes the mode of the file fname. Returns 0 if success, or -1 in case of an error.
int chown(char *fname, uid_t owner, gid_t group);
Changes the ownership of the file fname: sets the user ID to owner and the group ID to group. Returns 0 if success, or -1 in case of an error.
int utime(char *fname, struct utmpbuf *newtimes);
Changes modification and access time specified in newtimes to file filename. Returns 0 if success, or -1 in case of an error.
int rename(char *old, char *new);
Renames the file named old to the name new. Returns 0 if success, or -1 in case of an error.