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