[TUHS] Early UNIX file permission oddity

Warren Toomey wkt at tuhs.org
Wed May 21 14:32:24 AEST 2008


I was just browsing through the 1974 UNIX CACM paper, the one that first
publicly described the design and functionality of UNIX. I came across
some sentences which describe the file permissions, and they sounded quite odd:

  When a file is created, it is marked with the user ID of its owner.
  Also given for new files is a set of seven protection bits.
  Six of these specify independently read, write, and execute permission
  for the owner of the file and for all other users. [The seventh bit
  is the set-user-id bit. ]

This seems to indicate that there are "rwx" bits for owner, "rwx" bits for other,
and no "rwx" bits for group. I've never seen a UNIX system with 6 file
permission bits, so I thought I would poke around to see what I could find. It
turns out that none of the source code or document artifacts that we have
describe a UNIX system with just 6 "rwxrwx" bits: there are either "rw" user,
"rw" other and a separate execute bit, or the modern 9 "rwxrwxrwx" permission
bits.

1st Edition UNIX (Nov 1971) has these permission bits for an i-node:

#define I_SETUID        0000040		set-user-id
#define I_EXEC          0000020		a single execute bit
#define I_UREAD         0000010
#define I_UWRITE        0000004		read/write for user
#define I_OREAD         0000002
#define I_OWRITE        0000001		read/write for other

3rd Edition UNIX (Feb 1973) has these permission bits for an i-node:

000040  set user ID on execution
000020  executable
000010  read, owner
000004  write, owner
000002  read, non-owner
000001  write, non-owner		i.e same as for 1st Edition

By the time we get to the Nsys kernel (Aug 1973, just before 4th Edition UNIX),
the system has the concept of groups and the setgid() & getgid() system calls.
The inode.h header file defines these permission bits:

#define ISUID   04000
#define ISGID   02000
#define IREAD   0400
#define IWRITE  0200
#define IEXEC   0100

This is a bit unclear, but the code for the access() kernel function implies
that there are read/write/execute bits for user, group and other. Here is the
code for access() with my comments:

/* Determine if the current user can access a file with the given mode */
access(ip, mode)
int *ip;
{
        register *rip;

        if(u.u_uid == 0)		/* root can access all files */
                return(0);
        rip = ip;
        if(u.u_uid != rip->i_uid) {	/* not owner, shift mode 3 bits, lose */
                mode =>> 3;		/* user bits, replace with group bits */
                if(u.u_gid != rip->i_gid) /* not group, shift 3 again, lose */
                        mode =>> 3;	/* group bits, replace with other bits */
        }
        if((rip->i_mode&mode) != 0)	/* If mode mask and file's mode leave */
                return(0);		/* some bits enabled, allow access */
        u.u_error = EACCES;
        return(1);
}

And when we get to the 4th Edition (Nov 1973), the filesystem manual gives
these permissions:

000400  read (owner)
000200  write (owner)
000100  execute (owner)
000070  read, write, execute (group)
000007  read, write, execute (others)

So, editions up to the 3rd Edition had "rwrw" + "x"; the Nsys kernel and
onwards had "rwxrwxrwx" permission bits.

The only possibility that I can see is, as 3rd Edition was being rewritten
from assembly into C, the filesystem went through a stage where there
"rwx" execute bits for user, and "rxw" execute bits for other as the CACM
paper described, but groups had not been introduced yet. Then, the idea of
groups was added: the i-node structure had the i_gid field added, and the
access() function was extended with the lines:

		if(u.u_gid != rip->i_gid) /* not group, shift 3 again, lose */
                        mode =>> 3;     /* group bits, replace with other bits */

I'll have to ask Dennis is this sounds plausible.

Cheers,
	Warren



More information about the TUHS mailing list