1. The PROM Monitor

When you power on an SGI IRIS workstation, the processor begins running a program in the PROM (the chip that holds the computer firmware). This program checks all the IO devices for basic functionality, spins up and handshakes all of the connected disks, and starts the graphics running as what is called a textport.

The textport is a window drawn in the middle of the screen that holds the output of any program run before XWindows starts up. Interaction with the PROM, standalone programs, and IRIX in single-user mode all happen through the textport, which is coded in the PROM. As IRIX starts up XWindows, it re-connects its console device from the textport to the X console, which is an instance of XWsh, the standard IRIX terminal emulator.

The PROM on the SGI computers since about 1993 also contains some graphical menus, buttons, and mouse cursor routines, which look just like Motif. These graphics are not very functional, and only are used to make a more friendly interface for booting, installing the system, and choosing the keyboard map. You cannot use the mouse cursor in the PROM to select or edit input, it only works on the buttons such as "Exit".

2. Standalone Programs

'stand' is for Standalone, which refers to all of the programs compiled to run on the bare hardware, without the OS or libraries. There are a few of these you may see.

sash is the disk loader program. The IRIS PROM contains routines that can load standalone programs from only a few places, like the volume header (volhdr) of a local disk, a local tftp server, or from tape. To load a program from a filesystem the PROM relies on sash, which it loads automatically when you tell it to boot a file.

sash also has the ability to read files and directories on the disk. You can use ls or cat commands from the sash prompt. On a working IRIX disk, sash is always found in the volume header.

fx is the disk editor program. It usually lives on /stand/fx in an IRIX root filesystem, so loading it requires sash. fx can operate in one of two modes, a safe mode and an expert mode. The only reason you want to use the safe mode is to avoid damaging your disk when you are exploring. Any actual destructive changes to the disk require the expert mode. fx will ask you if you want to enable expert mode when it starts, and you can skip this step and go direct to expert mode by passing fx the -x option when you run it, as in boot stand/fx -x
fx can initialize disks to be used with IRIX, resize partitions, and change SCSI parameters. It can also be used to map out bad blocks, exercise (test) the disk for defects, and it has a debug mode that can be used to read and write raw disk blocks.

ide is the hardware test and diagnostic program. You would use it when testing a machine you just acquired, or when hunting down hardware issues. It can run torture tests of the different systems in the machine, and keep a list of errors it encountered to show you later on. Supposedly there are two versions of ide for each processor: one is shipped to customers and the other is for factory burn-in. ide usually lives in /usr/stand/ide and requires sash to be loaded, although in some cases it is in the volhdr and can be loaded without sash, by pressing 3 "Run Diagnostics" in the Maintenance Menu.

symmon is a resident kernel debugger that can be used to debug a running IRIX kernel. You won't need this unless you're hacking the kernel or device drivers. It usually lives in the volhdr when it's installed.

3. Hacking the IRIS

When you first turn on your IRIS, you see a dialog that asks you if you want to stop for maintenance. If you do nothing, the PROM will automatically start booting IRIX after about 5 seconds. If you click the dialog's button, or hit the Escape key, the boot sequence stops and you are shown the Maintenance Menu. On the post-1993 machines this is a graphical menu with 6 options, including Starting the System, Installing System Software, Running Diagnostics, Restoring from Backup, Command Prompt, and Keyboard Layout. The earlier machines only had numbered choices from a text menu, and hitting a number key from 1 to 6 works equally well on the new, graphical menu.

To open the textport and type commands at the PROM Monitor, hit the 5 key or click on Command Prompt. Now you can list the hardware makeup of the machine with hinv, or set the time in the battery-powered clock with date. Typing help or ? lists some of the commands available, but a few are missing from the list. The PROM understands names of devices to tell it where you are trying to load files from. As mentioned above, it cannot read filesystems. The acceptable devices are in either a traditional form or a new form. In almost all cases you can use the traditional form, which is much easier to type.

Traditional New
dksc(0,1,0) scsi(0)disk(1)partition(0)
tpsc(0,2,0) scsi(0)tape(2)tape(0)
There is another part you'll see when reading the "New" or ARCS-style device names, as in scsi(0)disk(1)rdisk(0)partition(8). This "rdisk" attribute is another name for the SCSI LUN number of a device. Unless you have a scsi vault with an internal RAID controller you will never have to use anything except 0 here, so it can be omitted.

The PROM doesn't support any kind of input editing, so keep your fingers off the arrow keys. It also doesn't support using the numeric pad to enter numbers, use the top number keys.

Often machines that haven't been run for a long time lose their date. If the machine was used in a different time zone, you will have to correct that too. Type printenv to see a list of all of the variables maintained in NVRAM (non-volatile memory) in the firmware. The TimeZone variable should be the correct 7-letter code for your time zone, with the first part being the abbreviation for winter time, then a number indicating hours west of Greenwich, then the abbreviation for summer time. In Boston this code is "EST5EDT". Set a variable by using the setenv command.
>> setenv TimeZone PST8PDT (for San Francisco)
>> date
January 14, 1995 06:17:09 GMT
>> ? date
date:            date [mmddhhmm[ccyy|yy][.ss]]
>> date 101507152005
October 15, 2005 07:15:00 GMT
Note that the date is always set in GMT, regardless of which timezone you are actually in.

Some other NVRAM variables of interest are eaddr, which shows the hardware Ethernet MAC address of the IRIS, and is generally used as a dongle for software licenses; console, which chooses whether the main interface to the machine is via the monitor and keyboard (g) or via a serial port (d, d1, d2); volume, which sets how loud the startup chimes are played; diagmode, which chooses whether to display a window when the power first comes on (if set to v), showing the results of the basic IO tests, and the model numbers of all attached drives; and various others.

4. Sash

ls will show the contents of the main disk's volhdr. You should see at least sash there, and maybe disklabel as well. If so you can enter sash by typing either boot or sash. You will see a version banner, then you can run standalone programs, like fx or ide, that live on a filesystem. Sash has input editing, but it doesn't use arrow keys. Instead you can use Control-p and Control-n to go through the command history, and Control-b and Control-f to move the cursor in the input line.

If all you wanted to do was load a program that only sash can load, you don't really need to enter the sash prompt. Instead you can pass the program as an argument to the boot command from the PROM.
>> boot usr/stand/ide

But if you wanted to use sash to look at files or directories on the disk, you do need to get to the sash prompt.
>> boot (boot is equivalent to typing sash)
130768+22320+3184+341792+48560d+4604+6816 entry: 0x97fa60d0
Standalone Shell SGI Version 6.5 ARCS   Apr 29, 1998 (32 Bit)
sash:
This is just the loader showing you all the executable file sections it loaded and where it jumped to to begin executing the program (in this case sash). After that is sash's own banner and prompt.

Let's try out sash's commands:
sash: ls
scsi(0)disk(1)rdisk(0)partition(8)/:
sgilabel  sash
scsi(0)disk(1)rdisk(0)partition(0)/:
.  ..  hw  ns  Mac  bin  dev  etc  lib  opt  tmp  usr  var  .ucp  .web  proc
sbin  dumpster  unix  .dtEnvPref  .netscape  .webcache  .sgihelprc
.desktop-IRIS  CDROM  Desktop  nsmail  .acrorc  .cshrc  stand  debug  .neditdb
.ebtpriv  .wshttymode  .varupdate  .syserr  .Sgiresources  lib32  .login
.sh_history  -c.lock  .showcaserc  .profile  .desktophost

Whew. So we can see that files and directory names are printed exactly the same (sbin is a directory, but unix is a file). There's no way to make it show which is which, you just have to know. Display a directory with ls, a file with cat. If you get it wrong you'll get a cryptic error. Pause output with Control-s, resume with Control-q.

sash: cat dksc(0,1,0)/.syserr
#2.0
NOTIFICATION=2
AUDIO=1
LOCALBLOCK=

I have no idea what that file does (like a lot of them) but it's short enough to include here as an example. To display a file inside a directory you would just change the command line: (press Control-p to go back to a previous command input)

sash: cat dksc(0,1,0)/etc/passwd
root:0z5nP1UbeZrec:0:0:Super-User:/:/bin/csh
sysadm:*:0:0:System V Administration:/usr/admin:/bin/sh
cmwlogin:*:0:994:CMW Login UserID:/usr/CMW:/sbin/csh
diag:*:0:996:Hardware Diagnostics:/usr/diags:/bin/csh
[... shortened for brevity ...]
EZsetup::992:998:System Setup:/var/sysadmdesktop/EZsetup:/bin/csh
demos:*LK*:993:997:Demonstration User:/usr/demos:/bin/csh
OutOfBox:*LK*:995:997:Out of Box Experience:/usr/people/OutOfBox:/bin/csh
guest:*:998:998:Guest Account:/usr/people/guest:/bin/csh
4Dgifts:*:999:998:4Dgifts Account:/usr/people/4Dgifts:/bin/csh
nobody:*:60001:60001:SVR4 nobody uid:/dev/null:/dev/null
noaccess:*:60002:60002:uid no access:/dev/null:/dev/null
haxor::4554:994:HaXoR Q. VaNdAl:/usr/people/haxor:/bin/tcsh
The passwd file shows the setup of each user account, including those used by the system. Each account is on its own line, with several fields divided by colons. The first field is the name you use to log in, the second is an encrypted form of the password, and the third is the user-id number, which if 0 gives super-user control of the system. The other fields need not concern us here.

If the second field is empty (the two enclosing colons are right next to each other) then no password is set, so no password prompt will be given when you try to log in. If the field is * or *LK* then logging in is not allowed. Otherwise the field holds 13 characters which are a one-way hash of an 8-character password.

One special case: If the second field of the entry for "root" is *, then that password is stored in a different file, /etc/shadow. Replace /etc/passwd with /etc/shadow in all of the steps.
So if your problem involved needing a root password to boot a machine, you can at least see the encrypted form here. That might be enough if you had a copy of Alex Muffett's Crack or some other password cracker.

5. Hacking the Disk

But if the root password was a secure one, with a lot of control-characters and no obvious dictionary words, then you're stuck. Except you aren't: you have physical access to the machine and the ability to write to its disk blocks (with fx). So all you need is to replace the 13-character password that's already there with a different 13 characters that encrypt an empty password. There are 4096 such, one for each possible value of the salt (read a unix textbook for an explanation). One of them is 0z5nP1UbeZrec (case-sensitive, the sixth char is a one, not lowercase L).

Now the problem is how to get that written into the passwd file without being able to boot into unix! If you had another computer that could read and write XFS filesystems in an SGI partitioned disk, you could just mount your system disk on that and do it. Short of that, you're going to have to be inventive. I have never seen this technique described before, and it's a little bit complicated, so bear with me on this.

[You will need a pencil and a pad of paper, plus a good engineering calculator that can do arithmetic on hexadecimal (base 16) numbers. I strongly recommend the HP48. A program for the HP48 to automate the process is in the Appendix.]

5.1. Disk Basics

The disk is composed, at its most basic level, of a number of sectors each assigned a number from 0 up to the end of the disk. The actual arrangement of cylinders and tracks is irrelevant, since the software addresses the disk in linear block addresses. Each sector is 512 bytes long. The disk is structured into partitions, in the SGI architecture there are up to 16 of them. There are some number of usable partitions which are nonoverlapping, plus a couple which overlap all of the others and are used for maintenance tasks such as cloning whole disks. The very first partition on the disk is called the volhdr and contains the partition map, which defines the layout of all the partitions on the disk. This layout is stored in the very first sector on disk, sector 0, but the volhdr continues for another few thousand sectors in order to hold files in a small area that can be used by the PROM. This area is not a filesystem per se, and can only store up to 15 small, contiguous files for system purposes. Normally there are only 1 or 2 files stored.

The remaining usable partitions are in four categories: swap partitions are used to hold virtual memory pages. Usr partitions are used to hold filesystems that cannot be booted. Root partitions hold filesystems that can be booted. xfslog partitions are used in some setups but you won't need to know anything about them. The files used to boot IRIX are all in the root partition on the disk, which is numbered partition 0 but is usually the third on the disk after the volhdr and swap. The way to see the partition table is to enter fx by typing
>> boot stand/fx
at the PROM and then following the prompts:
130768+22320+3184+341792+48560d+4604+6816 entry: 0x97fa60d0
Currently in safe read-only mode.
Do you require extended mode with all options available? (no)
SGI Version 6.5 ARCS BE  Oct  1, 1999
fx: "device-name" = (dksc)
fx: ctlr# = (0)
fx: drive# = (1)
...opening dksc(0,1,0)
...drive selftest...OK
Scsi drive type == SGI     QUANTUM XP32150 S89C

----- please choose one (? for help, .. to quit this menu)-----
[exi]t               [d]ebug/             [l]abel/
[b]adblock/          [exe]rcise/          [r]epartition/
fx>
and first typing
fx> label/show/part
----- partitions -----
part  type         blocks           Megabytes   (base+size)
  0: xfs       266240 + 4138249     130 + 2021
  1: raw         2048 + 264192        1 + 129
  8: volhdr         0 + 2048          0 + 1
 10: volume         0 + 4404489       0 + 2151

5.2. XFS Structure Basics

The first sector of the root partition contains a data structure central to the filesystem called the superblock. The superblock is the first information read from the filesystem and is used to reach everything else. This means that it contains a pointer to other structures, which eventually reach every file on the filesystem. These pointers are held in a structure which is too complicated to describe here, but I will summarize the information to the minimum needed for our hacking purposes.

The structure which holds the root directory of the filesystem (the root directory of the whole system, since we are talking about the root partition) is a directory inode. This structure in turn points to other structures that hold the list of the directory's contents. So by following these pointers we can move from the superblock, to the root directory inode, to inferior directories, to the data blocks that hold a file in one of those directories. A complication to all this is that the pointers are not represented as disk addresses. Instead they are represented using various different formats such as inode numbers and filesystem block numbers.

I mentioned that the XFS filesystem partition is not located at the beginning of the disk. It is located after the volhdr and the swap partition (type raw). So all of the pointers in the XFS structures are not in terms of disk addresses, but addresses within this partition. To convert a sector number within the partition to a sector number on disk, we add the base of the partition in blocks. In the example partition table above, this is 266240 sectors. (Think of "blocks" by itself as a synonym for sectors.) In hexadecimal this number is 0x41000.

When we want to convert some pointer from a XFS structure into a new sector address, we will have to add this number as the last step each time. So find the number from your own machine (using /label/show/part) and write it down in decimal and hexadecimal. I'll call it BASE for short.

5.3. Using the fx Debug Menu

In order to read the filesystem pointers from the disk blocks, we can use the debug features of the fx program. From the fx prompt, type fx> /debug

You should use the help feature of fx to get help on the syntax of all of these commands. Ask for help on each command separately.
To try out these commands, let's test reading in the very first block on disk, sector number 0. This holds the partition table.

fx/debug> seek 0
fx/debug> readbuf 20 1
fx/debug> dumpbuf s 0 266
We should see some output very close to this:
0x0000>  0000 0000 0000 0000 0000  0000 0000 0000 0000  .....A..../unix...
0x0012>  0000 0BE5 A941 0000 0001  2F75 6E69 7800 0000  ..................
0x0024>  0000 0000 0000 0000 0000  0000 0EF8 0000 0014  .....s............
0x0036>  0000 0000 0073 0200 0000  0000 0000 0000 0000  ..................
0x0048>  0000 0000 0000 0000 0000  0000 0000 0000 0086  }.sgilabel........
0x005a>  7D2E 7367 696C 6162 656C  0000 0002 0000 0200  sash..........N.sy
0x006c>  7361 7368 0000 0000 0000  0003 0004 4E00 7379  mmon.....*..h.ide.
0x007e>  6D6D 6F6E 0000 0000 022A  0005 6800 6964 6500  ..........N..ymmon
0x0090>  0000 0000 0000 06FE 0004  4E00 0079 6D6D 6F6E  ...........ymmon..
0x00a2>  0000 FFFF FFFF 0000 0000  0079 6D6D 6F6E 0000  ..................
0x00b4>  FFFF FFFF 0000 0000 0000  0000 0000 0000 0000  .......ash........
0x00c6>  0000 0000 0000 0061 7368  0000 0000 FFFF FFFF  .....ash..........
0x00d8>  0000 0000 0061 7368 0000  0000 FFFF FFFF 0000  ...de.............
0x00ea>  0000 0064 6500 0000 0000  FFFF FFFF 0000 0000  .de..............a
0x00fc>  0064 6500 0000 0000 FFFF  FFFF 0000 0000 0061  sh................
0x010e>  7368 0000 0000 FFFF FFFF  0000 0000 0000 0000  ..................
0x0120>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ...........de.....
0x0132>  0000 0000 0000 0000 0000  0064 6500 0000 0000  .........~m.......
0x0144>  FFFF FFFF 0000 0000 007E  6D2E 0008 1000 0000  ..................
0x0156>  000A 0008 0000 0000 1000  0000 0003 0000 0000  ..................
0x0168>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x017a>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x018c>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x019e>  0000 0000 0000 0000 0000  0000 0000 0000 1000  ..................
0x01b0>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ....}.............
0x01c2>  0000 0086 7D2E 0000 0000  0000 0006 0000 0000  ..................
0x01d4>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x01e6>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x01f8>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ...W.a....        
0x020a>  0000 C357 C861 0000 0000

Now don't get overwhelmed by this! All of this is really very easy and simple. The numbers on the left are showing the offset (in hexadecimal) of the beginning of each line of data from the start of the buffer. We always start displaying the buffer from 0, so they always start with 0. The middle section of each row are the hexadecimal bytes in the sector, grouped into words. On the far right, fx shows any ASCII characters encoded by the bytes of data.

Here lies a major bug in fx, but one we can work around! As you can see, the top line of hex data is all zero, but the ASCII on the right contains other stuff! The last line of hex data has no corresponding line of ASCII! What is happening is that fx prints the ASCII data on the right side for the line of hex data BELOW it.
The way to work around this is to always read data from the disk into the second line of the buffer, and always print the data from the start of the buffer (0). That's why we call readbuf with offset 20 bytes. If we ever want to write the buffer back to disk, we'll have to remember the offset is 20 bytes.

Each two digits in the middle hexadecimal section represent one byte. So to find the offset in the buffer, start at the first byte in the row, and count up from 0 each byte until you reach the one you want. Then add the hexadecimal offset at the beginning of that row.
With the S option to dumpbuf we get 16-bit words. There is also an L option to dumpbuf that shows 32-bit words, but that seems to be buggy on R10000 machines.

The first actual word in the sector is 0x0BE5A941, which we have put at position 20 in the buffer (hexadecimal 0x14). It is a magic number that identifies this as an SGI partition map. On the right you will see the letter 'A', because 0x41 is the ASCII encoding for 'A'.

5.4. The Superblock

We can begin by looking at the superblock. The superblock is at block 0 in the XFS partition, so take 0 and add the BASE number.
fx/debug> s 0x81000
fx/debug> r 20 1
fx/debug> d s 0
fx/debug/dumpbuf: nshorts = (266) 
0x0000>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..XFSB............
0x0012>  0000 5846 5342 0000 1000  0000 0000 000F CDA5  ................1W
0x0024>  0000 0000 0000 0000 0000  0000 0000 0000 3157  ...^......i..k....
0x0036>  CEEE 135E 101C 87E9 0800  690A 1C6B 0000 0000  ..................
0x0048>  0008 0004 0000 0000 0000  0080 0000 0000 0000  ..................
0x005a>  0081 0000 0000 0000 0082  0000 0010 0001 F9B5  ..................
0x006c>  0000 0008 0000 0000 0000  0490 1094 0200 0100  ..................
0x007e>  0010 0000 0000 0000 0000  0000 0000 0C09 0804  ..........(.......
0x0090>  1100 0019 0000 0000 0001  2880 0000 0000 0000  .Y.......[........
0x00a2>  0A59 0000 0000 0005 015B  0000 0000 0000 0000  ..................
0x00b4>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x00c6>  0000 0000 0002 0000 0000  0000 0000 0000 0000
[truncated for brevity]

Here are buried some more global parameters for the filesystem that we'll need to write down for later. All of these constants are 32-bit ints.

First, at offset 0x4 in the block (add 20 for where it will be located in the fx buffer, so here we get 0x18) is the size of a file system block, or FSB. We want to keep track of how many disk sectors are in each FSB, so we want to divide this number here by 512. In the case shown, 0x1000 / 512 = 8. Call this number FSBSIZE.

Next at offset 0x3C in the block (offset 0x50 in the fx buffer) is the number of the root inode. Write this down and save it for later.

At offset 0x54 in the block (offset 0x68 in the fx buffer) is a constant called the Allocation Group Size, measured in FSBs. Write this down as AGSIZE. Then, derive another constant from it by rounding it up to a power of 2. You can do this by taking the Log of AGSIZE, dividing by the Log of 2, rounding up to the next whole number, and raising 2 to that power. Write this down as AGMASK.

At offset 0x58 in the block (offset 0x6C in the fx buffer) is the number of allocation groups on the partition, AGCOUNT.

5.5. The Root Inode

Now we want to look at the next object in our filesystem chain, the root inode, which holds the root directory. From the previous step you should have the inode number (inum) of the root inode. In XFS, inodes are 256 bytes in size, which is half of a disk sector. Here are the steps for converting an inode number into a FSB number:

5.5.1. Inum to FSB

First notice if the inum is odd or even. If even, it will be the first inode in the corresponding sector; if odd, the second. Actually, we need to notice one more thing. If we long-divide FSBSIZE*2 into the inum, what is the remainder? This is probably easier to see if we look at the inum in hexadecimal. On the example disk, FSBSIZE*2 is 16, so the remainder is the least hexadecimal digit in inum. Meanwhile, the quotient, the FSB that corresponds to our inum, is all the hexadecimal digits except the last.
Pattern: 0x87654321 / 16 = 0x8765432 remainder 0x1
For other possibilities of FSBSIZE your calculator will help.

5.5.2. FSB to daddr

Now we need to find the disk block (within the XFS partition) or daddr corresponding to the FSB we just found. As before, long-divide the FSB num, but this time use the AGMASK as the divisor. The quotient is the agno or the number of the allocation group in which the FSB resides, while the remainder is the location of the FSB within that allocation group. Next, multiply the agno by the AGSIZE, and add the remainder. Multiply the result by FSBSIZE to get the daddr.

5.5.3. Reading the Inode

In actual fact, what we have is the first daddr making up our desired FSB. This is where we put back in the remainder we got by dividing the inum by FSBSIZE*2 (since there are two inodes per daddr, we will only use half of that remainder, rounding down). Finally, we can add BASE and load the desired sector into our fx buffer.
fx/debug> s 0x81040
fx/debug> r 20 1
fx/debug> d s 0
fx/debug/dumpbuf: nshorts = (266) 
0x0000>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..INA.............
0x0012>  0000 494E 41ED 0102 0020  0000 0000 0000 0000  ................=.
0x0024>  0000 0020 0000 0000 0000  0000 0000 0000 3DCF  .5..,(=.?d...|=.?d
0x0036>  0D35 0BA5 2C28 3DCA 3F64  09BF EC7C 3DCA 3F64  ...&..............
0x0048>  09C0 1526 0000 0000 0000  1000 0000 0000 0000  ..................
0x005a>  0001 0000 0000 0000 0001  0000 0002 0000 0000  ..................
0x006c>  0000 0000 0000 0000 FFFF  FFFF 0000 0000 0000  ......8`....xfsres
0x007e>  0000 0000 0000 3860 0001  8319 7866 7372 6573  torehousekeepingdi
0x0090>  746F 7265 686F 7573 656B  6565 7069 6E67 6469  r.........orphanag
0x00a2>  7200 0000 0000 2000 8009  6F72 7068 616E 6167  e.................
0x00b4>  6500 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x00c6>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x00d8>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x00ea>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x00fc>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ......IN..........
0x010e>  0000 0000 0000 494E 8000  0102 0001 0000 0000  ..................
0x0120>  0000 0000 0000 0001 0000  0000 0000 0000 0000  ..........,B.k..j.
0x0132>  0000 0000 0000 0000 0000  2C42 136B 1B18 6AA8  ,B.k..j...........
0x0144>  2C42 136B 1B18 6AA8 0000  0000 0000 0000 0000  ..................
0x0156>  0000 0000 0000 0000 0000  0000 0000 0000 0002  ..................
0x0168>  0000 0000 0000 0004 0000  0000 FFFF FFFF 0000  ..................
0x017a>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x018c>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x019e>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x01b0>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x01c2>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x01d4>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x01e6>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x01f8>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..........        
0x020a>  0000 0000 0000 0000 0000

Do you notice a pattern with the first word in every sector? The filesystem structures always start with a magic number. In this case it is 'IN' for an Inode (hex 0x494e). At offset 4 in the sector are two bytes, which should always be 0x01 and 0x02. These are the inode version and the inode format. If this word isn't 0x0102, something is wrong. Check your last step and make sure you calculated this sector correctly, because nothing in this document can help you if it isn't 0x0102.

XFS inodes hold pointers to extents, which are contiguous runs of FSBs. At offset 0x40 in the inode is an 8-byte doubleword containing the number of FSBs tracked by this inode. At offset 0x48 is the number of extents to which these same FSBs belong. The length of the file tracked by this inode is an 8-byte doubleword at offset 0x38. The extent records begin at offset 0x64 in the inode, and number up to eight.

Each extent record has three parts: 8 bytes give the file position at the start of the extent, 6 bytes give the extent's address as an FSB number multiplied by 32, and the last 2 bytes give the length of the extent in FSBs.

Since the file position of each extent isn't that interesting, I have highlighted in red the last 8 bytes of the first extent record on the sample filesystem. The extent's address is given as 0x3860, an FSB number times 32. This extent is 0x01 FSB long.

5.6. The Directory Blocks

XFS inodes are handles which point to files, and keep track of their extents. While this is true of "normal" files, like the passwd file we are trying to find, it is also true of directories, which XFS treats as a type of file. Directories have a special internal structure, organized as a B-tree so that they can efficiently contain millions of entries. When the B-tree is more than one FSB long, it begins with at least one header block, which contains information about the hashing algorithm that sorts file entries between the other blocks. The file entries themselves are in "leaf" directory nodes, which begin after the first block (if the directory is bigger than will fit in a single block). The root directory we are looking for is not, in all likelihood, very large, but it will span multiple blocks on a disk with a small FSBSIZE.

5.6.1. The Address of a Directory Block

If we look at the extent records of the inode, we see the file blocks broken up into one or more extents, each with an address and a length. If we are looking for a file that has been recently modified, such as passwd in the /etc directory, it is more expedient to search beginning with the last extent, since that is where new files are often put. Likewise, if the directory is made up of multiple blocks, we can skip searching the first block, because it is only a header node, not a leaf node with file entries.

Take the 6-byte address of the extent and divide it by 32. Then use the same procedure outlined in § 5.5.2 to find the disk sector address of the extent.

Since the length of directory nodes is a full FSB, it isn't useful to read a single sector from the disk. So when using the readbuf command, enter the length of the extent where we had 1 before. The extent is as long as the number in the last 2 bytes of the record, times the FSBSIZE. When the very long output appears on the screen, use the Control-S key to pause output while you read it. Control-Q will resume output, but any other key should do so as well.

5.6.2. Reading the Directory Block

fx/debug> s 0x81e18
fx/debug> r 20 8
fx/debug> d s 0
fx/debug/dumpbuf: nshorts = (2058) 
0x0000>  0000 0000 0000 0000 0000  0000 0000 0000 0000  .............../.!
0x0012>  0000 0000 0000 0000 0000  FEEB 0000 002F 0121  .A................
0x0024>  0D41 0100 0198 0BA9 0D94  000B 0DB8 000F 0000  ................4w
0x0036>  002E 0FF7 0100 0000 172E  0FED 0200 0000 3477  ......7s..........
0x0048>  0EC8 0200 0000 3773 0ED2  0200 0018 B4EE 0EBD  ....2.......zc....
0x005a>  0300 0019 32F6 0EDC 0300  0019 7A63 0EE7 0300  ..4........t......
0x006c>  001B 34E2 0EF2 0300 001B  F874 0EFD 0300 001C  ........6.......y.
0x007e>  F0F4 0F08 0300 001D 36F0  0F13 0300 001D 79F2  .........)........
0x0090>  0F1E 0300 001D B0F2 0F29  0300 05DD F2E2 0FE1  .......4...x...@..
0x00a2>  0400 0E1C B7E3 0F34 0400  0E78 B4EE 0F40 0400  .}...........L....
0x00b4>  0E7D F0F0 0D88 0400 0EB4  E59D 0F4C 0800 0EBB  ...s....cs.......:
0x00c6>  B4F8 0E73 0400 12D7 6373  0FD0 0900 14C0 F63A  ......q..m..01.(.\
0x00d8>  0FBE 0A00 1D9A 7185 0D6D  0600 3031 8228 0F5C  ..8.......=|$..q..
0x00ea>  0D00 3894 A7C9 0DC7 0500  3D7C 24DF 0F71 0700  >z8.....>.wc....>.
0x00fc>  3E7A 3815 0EAF 0600 3E98  7763 0F80 0500 3EC6  Z6....L....f..M<..
0x010e>  5A36 0D9F 0800 4CB8 BAE1  0E66 0500 4D3C F5B4  ....NA#.....`z.z..
0x0120>  0DD4 0500 4E41 23E1 0DF7  0800 607A 9A7A 0E07  ..nj*y.>..z[......
0x0132>  0B00 6E6A 2A79 0E3E 0B00  7A5B B1DD 0E7F 0A00  ...u.........'...{
0x0144>  8DFC FA75 0E1A 0500 93AE  ADD9 0E27 0F00 9C7B  ..._...yDx.Q.....0
0x0156>  F605 0D5F 0600 AE79 4478  0E51 0D00 B98F CF30  .....8.......8.2..
0x0168>  0DE1 0E00 CD38 99B4 0F8D  0500 CD38 9B32 0F9A  .......{..........
0x017a>  0500 CDF8 F0EA 0D7B 0500  CDF9 F598 0EA1 0600  .y2`.A.......N....
0x018c>  EC79 3260 0D41 0500 F292  F68A 0D4E 0900 FC87  ..................
0x019e>  B5F3 0E91 0800 FEC9 F3B9  0FA7 0C00 0000 0000  ..................
[... very long output elided for brevity's sake ...]
0x0e6a>  0000 8D2E 5367 6972 6573  6F75 7263 6573 0000  .....jdebug......o
0x0e7c>  0000 0002 A86A 6465 6275  6700 0000 0000 056F  >unix......!..varu
0x0e8e>  3E75 6E69 7800 0000 0000  0021 192E 7661 7275  pdate......!..prof
0x0ea0>  7064 6174 6500 0000 0000  0021 182E 7072 6F66  ile......!..login.
0x0eb2>  696C 6500 0000 0000 0021  172E 6C6F 6769 6E00  .....!..cshrc.....
0x0ec4>  0000 0000 0021 162E 6373  6872 6300 0000 0000  ..qbin.......'hw..
0x0ed6>  001F 7162 696E 0000 0000  00C0 0327 6877 0000  ......ns.......&de
0x0ee8>  0000 00A0 0307 6E73 0000  0000 0020 0326 6465  v.......#etc......
0x0efa>  7600 0000 0000 C003 2365  7463 0000 0000 00A0  ..lib........opt..
0x0f0c>  0303 6C69 6200 0000 0000  2003 206F 7074 0000  .....hsat.....@4xt
0x0f1e>  0000 0000 1F68 7361 7400  0000 0000 4034 7874  mp.....`..usr.....
0x0f30>  6D70 0000 0000 0060 00BF  7573 7200 0000 0000  ...var........proc
0x0f42>  C000 8276 6172 0000 0000  00A0 0082 7072 6F63  .....`..sbin.....@
[truncated for brevity]

There's a magic number again, but this time it's at offset 0x8 in the block instead of 0. '0xFEEB' means this is a leaf directory node, which we want. By visually searching the screen for the letters "etc", we found the file entry for the /etc directory. Look on the line below for the ASCII code for the letters "etc", which is 0x657463. The 8 bytes preceding the name holds the number of this file's inode, which on the sample is 0xc00323.

5.7. Rinse and Repeat

By repeating the steps in § 5.5.1 to 5.5.3, we can find the inode for /etc. In this sample, it is an odd inum, so we should remember to use the last inode in the respective sector.

fx/debug> s 0x66e381
fx/debug> r 20 1
fx/debug> d s 0
fx/debug/dumpbuf: nshorts = (266) 
0x0000>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..INA.............
0x0012>  0000 494E 41ED 0101 0002  0000 0000 0000 0000  ................=.
0x0024>  0000 0002 0000 0000 0000  0000 0000 0000 3DCA  G.3.F.:9...t!.:9..
0x0036>  47B5 3305 4691 3A39 B0E0  0074 21AD 3A39 B0E0  .t!...............
0x0048>  0074 21AD 0000 0000 0000  001D 0000 0000 0000  ..................
0x005a>  0000 0000 0000 0000 0000  0000 0002 0000 0000  .................`
0x006c>  0000 0000 0000 0000 FFFF  FFFF 0000 0000 0060  .B........^.libi4s
0x007e>  0342 0100 0000 0000 C5C4  5E0B 6C69 6269 3473  hl.sop...p...r...o
0x0090>  686C 2E73 6F70 9E88 0F70  A1A0 0F72 FCC4 0F6F  ...o.|.o.<.o...s6.
0x00a2>  DDE0 0F6F DE7C 0F6F DF3C  0F6F E0CC 0F73 3694  .s;h.s<..sA<.sF..s
0x00b4>  0F73 3B68 0F73 3CC0 0F73  413C 0F73 46E4 0F73  O..v...v...p.`.p..
0x00c6>  4F00 0F76 C3DC 0F76 C410  0F70 9B60 0F70 9CFC  .v...p.4.s...s.d.t
0x00d8>  0F76 C28C 0F70 A334 0F73  F3F0 0F73 FB64 0F74  .$.t...t...t...t.4
0x00ea>  0124 0F74 07C0 0F74 04B0  0F74 0380 0F74 0134  .s.l.s...t.P.s...t
0x00fc>  0F73 ED6C 0F73 EED4 0F74  0050 0F73 F8B4 0F74  ...p.dINA.........
0x010e>  0510 0F70 9064 494E 41ED  0102 0010 0000 0000  ..................
0x0120>  0000 0000 0000 0010 0000  0000 0000 0000 0000  ..=...:..r=..?.M.Z
0x0132>  0000 3DCF 0CFB 3AB6 1A72  3DCF 0C3F 184D 9F5A  =..?.M.Z......0...
0x0144>  3DCF 0C3F 184D 9F5A 0000  0000 0000 3000 0000  ..................
0x0156>  0000 0000 0003 0000 0000  0000 0002 0000 0002  ..................
0x0168>  0000 0000 0000 0000 0000  0000 FFFF FFFF 0000  ..................
0x017a>  0000 0000 0000 0000 0180  0620 0001 0000 0000  .........`...o.P.o
0x018c>  0000 0200 0000 018B 1060  0002 0F6F 1C50 0F6F  ...o...z...rn..r.T
0x019e>  1CB4 0F6F 11E8 0F7A 1CF0  0F72 6EDC 0F72 8E54  .r...z.0.r...r...r
0x01b0>  0F72 86A0 0F7A 1C30 0F72  8EF0 0F72 86D4 0F72  x..r...r.P.o...o..
0x01c2>  7884 0F72 8C10 0F72 8750  0F6F 1BD8 0F6F 16F8  .z...r...z.8.rl..r
0x01d4>  0F7A 1BDC 0F72 900C 0F7A  1C38 0F72 6C84 0F72  n..z...rl..r.H.r..
0x01e6>  6E04 0F7A 1CBC 0F72 6CEC  0F72 8448 0F72 84C0  .r...r...o...o...z
0x01f8>  0F72 84F8 0F72 8580 0F6F  1C18 0F6F 16C0 0F7A  ...r.`.z..        
0x020a>  1A80 0F72 9060 0F7A 1AB8                                         

This inode tracks a file 0x3000 bytes long, totalling 0x03 FSBs in 0x02 extents. The first extent record is 1 FSB long, which since this is a directory, means that it only holds a header node. So we proceed straight to the other extent, which must, by inference, contain the leaf nodes.

By repeating step § 5.6.1, we can read the /etc directory.
fx/debug> s 0x69a608
fx/debug> r 20 16
fx/debug> d s 0
fx/debug/dumpbuf: nshorts = (2058) 
0x0000>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ...............`..
0x0012>  0000 0000 0002 0000 0000  FEEB 0000 0060 028D  .-................
0x0024>  0A2D 0100 0320 070D 0A8F  0014 0ABF 0013 0000  ................:t
0x0036>  002E 0FF7 0100 0000 172E  0FED 0200 0000 3A74  .......c..........
[... very, very long output elided ...]
0x0a32>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..._.pcp.env......
0x0a44>  0000 C35F 1C70 6370 2E65  6E76 0000 0000 00C0  ..opasswd.........
0x0a56>  0083 6F70 6173 7377 6400  0000 0000 0000 0000  ...........orbix_p
0x0a68>  0000 0000 0000 0000 C000  9A6F 7262 6978 5F70  rofile.........gfs
0x0a7a>  726F 6669 6C65 0000 0000  00C0 009E 2E67 6673  lock........resolv
0x0a8c>  6C6F 636B 0000 0000 00C0  0093 7265 736F 6C76  .conf.............
0x0a9e>  2E63 6F6E 6600 0000 0000  0000 0000 0000 0000  ..............uiop
0x0ab0>  0000 0000 0000 0000 0000  0000 C4E2 7569 6F70  erms.......mrmtab.
0x0ac2>  6572 6D73 0000 0000 00C5  886D 726D 7461 6200  ..................
0x0ad4>  0000 0000 0000 0000 0000  0000 0000 0000 0000  .......ksnmpd.star
0x0ae6>  0000 0000 00C5 886B 736E  6D70 642E 7374 6172  t.......jpeer_nov.
0x0af8>  7400 0000 0000 C588 6A70  6565 725F 6E6F 7600  ......imtab......A
0x0b0a>  0000 0000 C588 696D 7461  6200 0000 0000 C441  Psavecore......R.r
0x0b1c>  5073 6176 6563 6F72 6500  0000 0000 C452 8972  eboot.............
0x0b2e>  6562 6F6F 7400 0000 0000  0000 0000 0000 0000  ..............TIME
0x0b40>  0000 0000 0000 0000 0000  00C0 0115 5449 4D45  ZONE......Raxfsres
0x0b52>  5A4F 4E45 0000 0000 00C4  5261 7866 7372 6573  tore......A1fstyp.
0x0b64>  746F 7265 0000 0000 00C4  4131 6673 7479 702E  d......AIprtvtoc..
0x0b76>  6400 0000 0000 C441 4970  7274 7674 6F63 0000  .....vbootptab....
0x0b88>  0000 00C4 F676 626F 6F74  7074 6162 0000 0000  ..Rwmntproc......A
0x0b9a>  00C4 5277 6D6E 7470 726F  6300 0000 0000 C441  2ftimer........eth
0x0bac>  3266 7469 6D65 7200 0000  0000 C50F 0065 7468  ers......A+devnm..
0x0bbe>  6572 7300 0000 0000 C441  2B64 6576 6E6D 0000  ....Rkdatemsk.....
0x0bd0>  0000 00C4 526B 6461 7465  6D73 6B00 0000 0000  .R.shutdown......R
0x0be2>  C452 8B73 6875 7464 6F77  6E00 0000 0000 C452  .stdcshrc......AMr
0x0bf4>  9173 7464 6373 6872 6300  0000 0000 C441 4D72  estore......A8inst
0x0c06>  6573 746F 7265 0000 0000  00C4 4138 696E 7374  all......AOrrestor
0x0c18>  616C 6C00 0000 0000 C441  4F72 7265 7374 6F72  e......A/fsstat...
0x0c2a>  6500 0000 0000 C441 2F66  7373 7461 7400 0000  ...Rjcshrc......AT
0x0c3c>  0000 C452 6A63 7368 7263  0000 0000 00C4 4154  sysinfo......ABnch
0x0c4e>  7379 7369 6E66 6F00 0000  0000 C441 426E 6368  eck......R`xfsdump
0x0c60>  6563 6B00 0000 0000 C452  6078 6673 6475 6D70  .......Amiser_repa
0x0c72>  0000 0000 00C4 FC41 6D69  7365 725F 7265 7061  ck.conf.......Bmis
0x0c84>  636B 2E63 6F6E 6600 0000  0000 C4FC 426D 6973  er_system.conf....
0x0c96>  6572 5F73 7973 7465 6D2E  636F 6E66 0000 0000  ..Rldevice.tab....
0x0ca8>  00C4 526C 6465 7669 6365  2E74 6162 0000 0000  ..Rfbrutab.......@
0x0cba>  00C4 5266 6272 7574 6162  0000 0000 00C4 FC40  miser_default.conf
0x0ccc>  6D69 7365 725F 6465 6661  756C 742E 636F 6E66  ......A5growfs....
0x0cde>  0000 0000 00C4 4135 6772  6F77 6673 0000 0000  .`.Dcron.d......R.
0x0cf0>  0060 0344 6372 6F6E 2E64  0000 0000 00C4 5285  projid......A:labe
0x0d02>  7072 6F6A 6964 0000 0000  00C4 413A 6C61 6265  lit......AKrdump..
0x0d14>  6C69 7400 0000 0000 C441  4B72 6475 6D70 0000  ....K.rc3.d.......
0x0d26>  0000 0080 4BE4 7263 332E  6400 0000 0000 A003  .rc2.d.......$rc0.
0x0d38>  0472 6332 2E64 0000 0000  00C0 0324 7263 302E  d......R.passwd.sg
0x0d4a>  6400 0000 0000 C452 7F70  6173 7377 642E 7367  i.......Lsyslog.co
0x0d5c>  6900 0000 0000 C4FC 4C73  7973 6C6F 672E 636F  nf........passwd..
0x0d6e>  6E66 0000 0000 00C0 011B  7061 7373 7764 0000  ....AVuadmin......
0x0d80>  0000 00C4 4156 7561 646D  696E 0000 0000 00C4  Rzmrouted.conf....
0x0d92>  527A 6D72 6F75 7465 642E  636F 6E66 0000 0000  ..t.xtab......A^wt
0x0da4>  00C5 740F 7874 6162 0000  0000 00C4 415E 7774  mp......A\wall....
0x0db6>  6D70 0000 0000 00C4 415C  7761 6C6C 0000 0000  ....uucp......AZut
0x0dc8>  00E0 0304 7575 6370 0000  0000 00C4 415A 7574  mp......ASswap....
[output truncated]

A word of caution is due here. There are sometimes three different files under /etc which contain the word "passwd" on IRIX systems. "opasswd" is a backup of the passwd file, not the version in use. "passwd.sgi" is a file containing some extra information about users on a system, that controls how accounts are displayed by the graphical login program. Neither of these is what we want. Additionally, if in § 4 you determined that you needed to use the "/etc/shadow" file, you should search for that instead of "passwd".

5.8. Nearing the End

Repeat the appropriate steps again, and read in the inode for the passwd file. Remember that hexadecimal digits B, D, and F are odd, when you determine which inode in the sector to use.

fx/debug> s 0x66e27d
fx/debug> r 20 1
fx/debug> d s 0
fx/debug/dumpbuf: nshorts = (266) 
0x0000>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..IN..............
0x0012>  0000 494E 8100 0102 0001  0000 0000 0000 0000  ................9l
0x0024>  0000 0001 0000 0000 0000  0000 0000 0000 396C  ....nK:M.E2..-:M.E
0x0036>  F60C 07DE 6E4B 3A4D FF45  32C1 C02D 3A4D FF45  2..-..............
0x0048>  32C1 C02D 0000 0000 0000  0000 0000 0000 0000  ..................
0x005a>  0000 0000 0000 0000 0000  0000 0002 0000 0000  ..................
0x006c>  0000 0000 0000 0001 FFFF  FFFF 0000 0000 00A0  ..........A.visfly
0x007e>  011A 0200 0000 0000 CEF1  410E 7669 7366 6C79  info.rgb.......B.v
0x0090>  696E 666F 2E72 6762 0000  0000 00CE F142 0E76  isflylogo.xpm.....
0x00a2>  6973 666C 796C 6F67 6F2E  7870 6D00 0000 0000  ..................
0x00b4>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x00c6>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x00d8>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x00ea>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x00fc>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ......IN..........
0x010e>  0000 0000 0000 494E 81A4  0102 0001 0000 0000  ..................
0x0120>  0000 0000 0000 0001 0000  0000 0000 0000 0000  ..=..j.\..:M.q#...
0x0132>  0000 3DCF 0D6A 1C5C A51E  3A4D FF71 23DB F2D8  :M.q#..........^..
0x0144>  3A4D FF71 23DB F2D8 0000  0000 0000 045E 0000  ..................
0x0156>  0000 0000 0001 0000 0000  0000 0001 0000 0002  ..................
0x0168>  0000 0000 0000 0000 0000  0010 FFFF FFFF 0000  ..................
0x017a>  0000 0000 0000 0000 0188  C600 0001 0000 0000  ..................
0x018c>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x019e>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x01b0>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x01c2>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x01d4>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x01e6>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..................
0x01f8>  0000 0000 0000 0000 0000  0000 0000 0000 0000  ..........
0x020a>  0000 0000 0000 0000 0000

In this example, there is only one extent and one FSB for the file. By repeating the appropriate steps you can find the sector address of the passwd file. If there was more than one block, you would take the first one, since the root passwd is almost always the first line of the file.

6. Editing the Password

By now you should know how to type the commands.

View the first few lines of the file, and confirm that root does, in fact, have a password. (If it was blank, you just wasted a few hours. Don't ask me about it.) You are now going to change that password into something else, by writing over those characters. First make sure you have the sector number of the passwd file written down on paper.

Now quit and re-enter fx. Type Control-D or click the textport's Exit button, and start fx again. (Reread § 2 if you've forgotten how.) This time enable "Expert mode". (Either use flag -x or say "yes" when you're asked if you require all options.) Take the default on all the other questions.

fx> d
fx/debug> seek (the sector number of the passwd file you wrote down)
fx/debug> r 20 1
fx/debug> e
fx/debug/editbuf: itype = (bytes) 
fx/debug/editbuf: buf offset = (0) 20
fx/debug/editbuf: value = (114) 
fx/debug/editbuf: value = (111) 
fx/debug/editbuf: value = (111) 
fx/debug/editbuf: value = (116) 
fx/debug/editbuf: value = (58) 
["root:" is decimal 114,111,111,116,58]

From here on the values are the ASCII codes of the 13-letter encrypted password. Just enter the new value at each prompt. The program understands hex notation with a leading "0x", and might also understand characters between single-quotes. But decimal codes work just as well. After you have entered all 13 letters of the new password, type Control-C on the next line. Or, type two dots on this line:

fx/debug/editbuf: value = (58) ..

Verify what you just did by displaying it using dumpbuf. Then, if you're satisfied, commit the changes to disk.

fx/debug> w 20 1

Now restart by hitting Control-D. Congratulations, you just did what most people still think is impossible.

7. Appendix: With the HP48

It took me a while to write the program, but once it is typed in, it can be used to hack the disk with virtually no effort. Just find an HP48 calculator and define these variables:

The HP48 has three shift keys on the lower left edge. [Alpha], [Left], and [Right]. There are six soft-keys along the screen; when the Alpha annunciator is off, they refer to the commands in the screen menu. The HP48 has a very useful feature that allows the entry of binary numbers, including automatic binary arithmetic on such numbers. The syntax for binary numbers looks like # FEEDBABEh where the last letter shows the radix, either h, d, o, or b. The radix mode can be changed globally, which redisplays all the binary numbers in the new base, without changing their value.

Now finding the disk sector that holds a particular File-System Block (FSB) is as simple as entering the hex number using [Right]÷ [Alpha][Alpha] typing the number using the A-F softkeys for the A through F hex digits, and then [Alpha] again, then press the A softkey for the FSB command. The program does the step of dividing by 32 itself.

Similarly, the disk sector that holds a particular Inode can be found the same way, except hitting the B softkey for the INUM command. Remember if the Inum was even or odd! Even means it is first in its sector, odd means it is last. Hex digits B, D, and F are odd.

For readers who already know how to operate the calculator, here is the program.
BASE Start sector of the XFS partition.
FSBSIZE Size of a File-System Block in sectors.
AGSIZE Size of an Allocation Group in FSBs.
AGMASK AGSIZE B→R LOG 2 LOG / CEIL 2 SWAP ^ R→B
FLOOR2 « DUP2 / DUP TYPE 10 ≠ « FLOOR » IFT DUP 4 ROLLD ∗ − »
FSB « AGMASK FLOOR2 SWAP AGSIZE ∗ + FSBSIZE ∗ BASE + »
INUM « FSBSIZE 2 ∗ FLOOR2 2 FLOOR2 DROP SWAP FSB + »
CST { { "FSB" « 32 / FSB » } INUM }

Greetz: All former employees of Silicon Graphics, especially the authors of xfs_db. rs, h3x, Impact, telemnstr, berkeley, SkyWriter, demize, tarachian, phuzzy, Farm_Guy, KMR. Keep the dream alive: keep on hacking the IRIS!