NTFS Documentation: Runlist

Definition

This is a table written in the content part of a non-resident file attribute, which allows to have access to its stream.

Layout

The runlist is a sequence of elements: each element stores an offset to the starting LCN of the previous element and the length in clusters of a run.

To save space, Offset and Length are variable size fields (probably up to 8 bytes), and an element is written in this crunched format:

Offset in nibble to the beginning of the element Length Description
0 1 F=Size of the Offset field
1 1 L=Size of the Length field
2 2*L Length of the run
2+2*L 2*F Offset to the starting LCN of the previous element

Offset to the starting LCN of the previous element
This is a signed value. For the first element, consider the offset as relative to the LCN 0, the beginning of the volume.

The layout of the runlist must take account of the data compression: the set of VCNs containing the stream of a compressed file attribute is divided in compression units (also called chunks) of 16 clusters: VCNs 0 to 15 constitutes the 1st compression unit, VCNs 16 to 31 the 2nd one, and so on... For each compression unit,

In pratice, this is a bit more complicated because some of the element can be gathered. But let's take an ...

...Example

We have to decode the following runlist:
21 14 00 01 11 10 18 11 05 15 01 27 11 20 05
First, we have to decrunch it. We obtain the readable format used to initialize the runlist[] table in the following example of C code. Second, we have to execute this program (a short code is sometimes better that long and confusing explanations):
/* printf */
#include <stdio.h>

struct s_element {
    int Offset, Length;
    };

struct s_element runlist[] = {
    {0x100, 0x14},
    { 0x18, 0x10},
    { 0x15,  0x5},
    {  0x0, 0x27},
    {  0x5, 0x20}
    };

int main(int argc, char **argv) 
{
    int index, V, L, LRef, R, cont;
    struct s_element begin, end;
    
    index = 0;
    end = runlist[index];
    L = LRef = end.Offset;
    V = 0;
    
    do {
	R = 16 - (V%16);
	if (R == 16) {
	    printf ("Compression unit beginning at VCN %x\n", V);
	    begin = end;
	}
	if (end.Length >= R) {
	    if (begin.Offset)
		if (end.Offset)
		    printf (" %x clusters at LCN %x\n"
                            " Unit not compressed\n", R, L);
		else
		    printf (" %x unused clusters: compressed unit\n",
		      R);
	    else
		printf (" 10 zeroed clusters: sparse unit\n");
	    L += R;
	    V += R;
	    end.Length -= R;
	}
	else {
	    printf (" %x clusters at LCN %x\n", end.Length, L);
	    V += end.Length;
	    end.Length = 0;
	}
	
	if (end.Length)
	    cont = 1;
	else
	    if (++index < (sizeof(runlist)/sizeof(struct s_element))) {
		end = runlist[index];
		L = LRef += end.Offset;
		cont = 1;
	    }
	    else
		cont = 0;
    } while (cont);

    exit(0);
}
The above program finally produces the following output:
Compression unit beginning at VCN 0
 10 clusters at LCN 100
 Unit not compressed
Compression unit beginning at VCN 10
 4 clusters at LCN 110
 c clusters at LCN 118
 Unit not compressed
Compression unit beginning at VCN 20
 4 clusters at LCN 124
 5 clusters at LCN 12d
 7 unused clusters: compressed unit
Compression unit beginning at VCN 30
 10 zeroed clusters: sparse unit
Compression unit beginning at VCN 40
 10 zeroed clusters: sparse unit
Compression unit beginning at VCN 50
 10 clusters at LCN 132
 Unit not compressed
Compression unit beginning at VCN 60
 10 clusters at LCN 142
 Unit not compressed
As you can see, the runlist format is really brain-damaged:)

Note: If you have a shorter code that can produce the same output, please let me know.


Regis Duchesne at VIA, ECP, France
Last modified: Sat Jan 23 20:15:14 PST 1999