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