xref: /minix/usr.sbin/installboot/minixfs3.c (revision 0a6a1f1d)
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <errno.h>
5 #include <sys/stat.h>
6 #include <limits.h>
7 #include <fcntl.h>
8 #include <unistd.h>
9 #include <string.h>
10 
11 #include "installboot.h"
12 
13 #ifndef DFL_SECSIZE
14 #define DFL_SECSIZE     512
15 #endif
16 
17 #define MFS_FIRST_SUBP_OFFSET	32
18 
19 enum {
20 	TYPE_BAD,
21 	TYPE_PART,
22 	TYPE_DISK
23 };
24 
25 static int
26 minixfs3_read_mbr(const char* device, char* buf)
27 {
28 	int fd;
29 	int bytes;
30 	int n;
31 
32 	fd = open(device, O_RDONLY);
33 	if (fd == -1) {
34 		fprintf(stderr, "Can't open %s: %s\n", device, strerror(errno));
35 		return 1;
36 	}
37 
38 	if (lseek(fd, MBR_PART_OFFSET, SEEK_SET) != MBR_PART_OFFSET) {
39 		fprintf(stderr, "Can't seek in %s to %d: %s\n",
40 			device, MBR_PART_OFFSET, strerror(errno));
41 		close(fd);
42 		return 1;
43 	}
44 
45 	bytes = DFL_SECSIZE - MBR_PART_OFFSET;
46 
47 	if ((n = read(fd, buf, bytes)) != bytes) {
48 		fprintf(stderr, "Can't read %d bytes from %s, %d read instead"
49 			": %s\n",
50 			bytes, device, n, strerror(errno));
51 		close(fd);
52 		return 1;
53 	}
54 
55 	if ((uint8_t)buf[bytes-2] != 0x55 || (uint8_t)buf[bytes-1] != 0xAA) {
56 		fprintf(stderr, "No MBR on %s, signature is %x\n",
57 			device, *(uint16_t*)(&buf[bytes-2]));
58 		close(fd);
59 		return 1;
60 	}
61 
62 	close(fd);
63 	return 0;
64 }
65 
66 static int
67 minixfs3_get_dev_type(const char *device, ib_params *params)
68 {
69 	int len, type;
70 
71 	/*
72 	 * Unless the -f flag is given, we expect to be provided with a primary
73 	 * partition.  That is, a device name that ends with "pN", N being 0-3.
74 	 * If the -f flag is given, we assume that anything else is a whole
75 	 * disk.  If we were given a subpartition, it will fail the subsequent
76 	 * MBR signature test, so we need not check this explicitly.
77 	 */
78 	len = strlen(device);
79 
80 	if (len > 2 && device[len-2] == 'p' &&
81 	    (unsigned) (device[len-1] - '0') <= 3) {
82 		type = TYPE_PART;
83 	} else {
84 		type = TYPE_DISK;
85 	}
86 
87 	if (type != TYPE_PART && !(params->flags & IB_FORCE)) {
88 		fprintf(stderr, "Wrong device %s, must be /.../cxdyp[0-3]\n",
89 			device);
90 		return TYPE_BAD;
91 	}
92 
93 	return type;
94 }
95 
96 int
97 minixfs3_is_minix_partition(ib_params *params)
98 {
99 	char buf[DFL_SECSIZE]; /* part table + signature */
100 
101 	if (minixfs3_get_dev_type(params->filesystem, params) == TYPE_BAD)
102 		return 0;
103 
104 	/* MINIX 3 partition with current scheme *must* have subpartitions,
105 	 * thus MBR has signature. minixfs3_read_mbr checks the signature.
106 	 */
107 	if (minixfs3_read_mbr(params->filesystem, buf))
108 		return 0;
109 	return 1;
110 }
111 
112 /* bootxx from NetBSD is ~8Kb, and old MINIX installations have just
113  * 1Kb of space for their bootblock. Check if there is enough space
114  * to install bootxx_minixfs3. New installation should have 16Kb before
115  * the first subpartition.
116  */
117 int
118 minixfs3_has_bootblock_space(ib_params *params)
119 {
120 	const char *device;
121 	char buf[DFL_SECSIZE]; /* part table + signature */
122 	char parent_name[NAME_MAX];
123 	struct mbr_partition *part;
124 	uint32_t first_subpartition = (uint32_t) ~0;
125 	uint32_t parent_partition;
126 	int i, len, type = 0;
127 
128 	device = params->filesystem;
129 
130 	if ((type = minixfs3_get_dev_type(device, params)) == TYPE_BAD)
131 		exit(1);
132 
133 	if (minixfs3_read_mbr(device, buf))
134 		exit(1);
135 
136 	part = (struct mbr_partition *) buf;
137 
138 	for (i = 0; i < 4; i++) {
139 		if (part[i].mbrp_size &&
140 		    part[i].mbrp_start < first_subpartition)
141 			first_subpartition = part[i].mbrp_start;
142 	}
143 
144 	if (type == TYPE_PART) {
145 		/* The target is a partition.  Look up its starting offset. */
146 		len = strlen(device);
147 		strncpy(parent_name, device, len - 2);
148 		parent_name[len - 2] = '\0';
149 
150 		if (minixfs3_read_mbr(parent_name, buf))
151 			exit(1);
152 
153 		parent_partition = 0;
154 		for (i = 0; i < 4; i++) {
155 			struct mbr_partition *p = &part[i];
156 			if (p->mbrp_size && p->mbrp_start <= first_subpartition
157 			    && (p->mbrp_start + p->mbrp_size) >
158 			    first_subpartition) {
159 				parent_partition = p->mbrp_start;
160 				break;
161 			}
162 		}
163 	} else {
164 		/* The target is a whole disk.  The starting offset is 0. */
165 		parent_partition = 0;
166 	}
167 
168 	if ((first_subpartition - parent_partition) < MFS_FIRST_SUBP_OFFSET)
169 		return 0;
170 	else
171 		return 1;
172 }
173