summaryrefslogtreecommitdiff
path: root/partx/dos.c
blob: f962f3762ba88253ef3af1a116112126cbe5ec68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include <stdio.h>

#include "blkdev.h"

#include "partx.h"
#include "dos.h"

static int
is_extended(int type) {
	return (type == 5 || type == 0xf || type == 0x85);
}

/* assemble badly aligned little endian integer */
static inline unsigned int
assemble4le(unsigned char *p) {
	return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}

static inline unsigned int
partition_start(struct partition *p) {
	return assemble4le(&(p->start_sect[0]));
}

static inline unsigned int
partition_size(struct partition *p) {
	return assemble4le(&(p->nr_sects[0]));
}

static int
read_extended_partition(int fd, struct partition *ep,
			struct slice *sp, int ns, int ssf)
{
	struct partition *p;
	unsigned long start, here;
	unsigned char *bp;
	int loopct = 0;
	int moretodo = 1;
	int i, n=0;

	here = start = partition_start(ep);;

	while (moretodo) {
		moretodo = 0;
		if (++loopct > 100)
			return n;

		bp = getblock(fd, here * ssf);	/* in 512 blocks */
		if (bp == NULL)
			return n;

		if (bp[510] != 0x55 || bp[511] != 0xaa)
			return n;

		p = (struct partition *) (bp + 0x1be);

		for (i=0; i<2; i++, p++) {
			if (partition_size(p) == 0 || is_extended(p->sys_type))
				continue;
			if (n < ns) {
				sp[n].start = (here + partition_start(p)) * ssf;
				sp[n].size = partition_size(p) * ssf;
				n++;
			} else {
				fprintf(stderr,
				    "dos_extd_partition: too many slices\n");
				return n;
			}
			loopct = 0;
		}

		p -= 2;
		for (i=0; i<2; i++, p++) {
			if (partition_size(p) != 0 &&
			    is_extended(p->sys_type)) {
				here = start + partition_start(p);
				moretodo = 1;
				break;
			}
		}
	}
	return n;
}

static int
is_gpt(int type) {
	return (type == 0xEE);
}

int
read_dos_pt(int fd, struct slice all, struct slice *sp, int ns) {
	struct partition *p;
	unsigned long offset = all.start;
	int i, n=0;
	unsigned char *bp;
	int ssf;

	bp = getblock(fd, offset);
	if (bp == NULL)
		return -1;

	if (bp[510] != 0x55 || bp[511] != 0xaa)
		return -1;

	/* msdos PT depends sector size... */
	if (blkdev_get_sector_size(fd, &ssf) != 0)
		ssf = DEFAULT_SECTOR_SIZE;

	/* ... but partx counts everything in 512-byte sectors */
	ssf /= 512;

	p = (struct partition *) (bp + 0x1be);
	for (i=0; i<4; i++) {
		if (is_gpt(p->sys_type))
			return 0;
		p++;
	}
	p = (struct partition *) (bp + 0x1be);
	for (i=0; i<4; i++) {
		/* always add, even if zero length */
		if (n < ns) {
			sp[n].start = partition_start(p) * ssf;
			sp[n].size = partition_size(p) * ssf;
			n++;
		} else {
			fprintf(stderr,
				"dos_partition: too many slices\n");
			break;
		}
		p++;
	}
	p = (struct partition *) (bp + 0x1be);
	for (i=0; i<4; i++) {
		if (is_extended(p->sys_type))
			n += read_extended_partition(fd, p, sp+n, ns-n, ssf);
		p++;
	}
	return n;
}