summaryrefslogtreecommitdiff
path: root/miscutils/ubi_tools.c
blob: 6f20f300c6ce4fda5306612658561ef604e48771 (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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
/* Ported to busybox from mtd-utils.
 *
 * Licensed under GPLv2, see file LICENSE in this source tree.
 */

//config:config UBIATTACH
//config:	bool "ubiattach"
//config:	default y
//config:	select PLATFORM_LINUX
//config:	help
//config:	  Attach MTD device to an UBI device.
//config:
//config:config UBIDETACH
//config:	bool "ubidetach"
//config:	default y
//config:	select PLATFORM_LINUX
//config:	help
//config:	  Detach MTD device from an UBI device.
//config:
//config:config UBIMKVOL
//config:	bool "ubimkvol"
//config:	default y
//config:	select PLATFORM_LINUX
//config:	help
//config:	  Create a UBI volume.
//config:
//config:config UBIRMVOL
//config:	bool "ubirmvol"
//config:	default y
//config:	select PLATFORM_LINUX
//config:	help
//config:	  Delete a UBI volume.
//config:
//config:config UBIRSVOL
//config:	bool "ubirsvol"
//config:	default y
//config:	select PLATFORM_LINUX
//config:	help
//config:	  Resize a UBI volume.
//config:
//config:config UBIUPDATEVOL
//config:	bool "ubiupdatevol"
//config:	default y
//config:	select PLATFORM_LINUX
//config:	help
//config:	  Update a UBI volume.

//applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiattach))
//applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubidetach))
//applet:IF_UBIMKVOL(APPLET_ODDNAME(ubimkvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubimkvol))
//applet:IF_UBIRMVOL(APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol))
//applet:IF_UBIRSVOL(APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol))
//applet:IF_UBIUPDATEVOL(APPLET_ODDNAME(ubiupdatevol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiupdatevol))

//kbuild:lib-$(CONFIG_UBIATTACH) += ubi_tools.o
//kbuild:lib-$(CONFIG_UBIDETACH) += ubi_tools.o
//kbuild:lib-$(CONFIG_UBIMKVOL)  += ubi_tools.o
//kbuild:lib-$(CONFIG_UBIRMVOL)  += ubi_tools.o
//kbuild:lib-$(CONFIG_UBIRSVOL)  += ubi_tools.o
//kbuild:lib-$(CONFIG_UBIUPDATEVOL) += ubi_tools.o

#include "libbb.h"
/* Some versions of kernel have broken headers, need this hack */
#ifndef __packed
# define __packed __attribute__((packed))
#endif
#include <mtd/ubi-user.h>

#define OPTION_M  (1 << 0)
#define OPTION_D  (1 << 1)
#define OPTION_n  (1 << 2)
#define OPTION_N  (1 << 3)
#define OPTION_s  (1 << 4)
#define OPTION_a  (1 << 5)
#define OPTION_t  (1 << 6)

#define do_attach (ENABLE_UBIATTACH && applet_name[3] == 'a')
#define do_detach (ENABLE_UBIDETACH && applet_name[3] == 'd')
#define do_mkvol  (ENABLE_UBIMKVOL  && applet_name[3] == 'm')
#define do_rmvol  (ENABLE_UBIRMVOL  && applet_name[4] == 'm')
#define do_rsvol  (ENABLE_UBIRSVOL  && applet_name[4] == 's')
#define do_update (ENABLE_UBIUPDATEVOL && applet_name[3] == 'u')

//usage:#define ubiattach_trivial_usage
//usage:       "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV"
//usage:#define ubiattach_full_usage "\n\n"
//usage:       "Attach MTD device to UBI\n"
//usage:     "\n	-m MTD_NUM	MTD device number to attach"
//usage:     "\n	-d UBI_NUM	UBI device number to assign"
//usage:
//usage:#define ubidetach_trivial_usage
//usage:       "-d UBI_NUM UBI_CTRL_DEV"
//usage:#define ubidetach_full_usage "\n\n"
//usage:       "Detach MTD device from UBI\n"
//usage:     "\n	-d UBI_NUM	UBI device number"
//usage:
//usage:#define ubimkvol_trivial_usage
//usage:       "UBI_DEVICE -N NAME [-s SIZE | -m]"
//usage:#define ubimkvol_full_usage "\n\n"
//usage:       "Create UBI volume\n"
//usage:     "\n	-a ALIGNMENT	Volume alignment (default 1)"
//usage:     "\n	-m		Set volume size to maximum available"
//usage:     "\n	-n VOLID	Volume ID, if not specified, it"
//usage:     "\n			will be assigned automatically"
//usage:     "\n	-N NAME		Volume name"
//usage:     "\n	-s SIZE		Size in bytes"
//usage:     "\n	-t TYPE		Volume type (static|dynamic)"
//usage:
//usage:#define ubirmvol_trivial_usage
//usage:       "UBI_DEVICE -n VOLID"
//usage:#define ubirmvol_full_usage "\n\n"
//usage:       "Remove UBI volume\n"
//usage:     "\n	-n VOLID	Volume ID"
//usage:
//usage:#define ubirsvol_trivial_usage
//usage:       "UBI_DEVICE -n VOLID -s SIZE"
//usage:#define ubirsvol_full_usage "\n\n"
//usage:       "Resize UBI volume\n"
//usage:     "\n	-n VOLID	Volume ID to resize"
//usage:     "\n	-s SIZE		Size in bytes"
//usage:
//usage:#define ubiupdatevol_trivial_usage
//usage:       "UBI_DEVICE [IMG_FILE]"
//usage:#define ubiupdatevol_full_usage "\n\n"
//usage:       "Update UBI volume\n"
//usage:     "\n	-t	Truncate UBI volume"
//usage:     "\n	-s SIZE	Bytes in input (if reading stdin)"


int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
{
	unsigned opts;
	char *ubi_ctrl;
	//struct stat st;
	int fd;
	int mtd_num;
	int dev_num = UBI_DEV_NUM_AUTO;
	int vol_id = UBI_VOL_NUM_AUTO;
	char *vol_name = NULL;
	int size_bytes;
	int alignment = 1;
	char *type = NULL;

	if (do_mkvol) {
		opt_complementary = "-1:d+:n+:s+:a+";
		opts = getopt32(argv, "md:n:N:s:a:t::",
				&dev_num, &vol_id,
				&vol_name, &size_bytes, &alignment, &type
			);
	} else {
		opt_complementary = "-1:m+:d+:n+:s+:a+";
		opts = getopt32(argv, "m:d:n:N:s:a:t::",
				&mtd_num, &dev_num, &vol_id,
				&vol_name, &size_bytes, &alignment, &type
		);
	}
	ubi_ctrl = argv[optind];

	fd = xopen(ubi_ctrl, O_RDWR);
	//xfstat(fd, &st, ubi_ctrl);
	//if (!S_ISCHR(st.st_mode))
	//	bb_error_msg_and_die("%s: not a char device", ubi_ctrl);

	if (do_attach) {
		struct ubi_attach_req req;
		if (!(opts & OPTION_M))
			bb_error_msg_and_die("%s device not specified", "MTD");

		memset(&req, 0, sizeof(req));
		req.mtd_num = mtd_num;
		req.ubi_num = dev_num;

		xioctl(fd, UBI_IOCATT, &req);
	} else
	if (do_detach) {
		if (!(opts & OPTION_D))
			bb_error_msg_and_die("%s device not specified", "UBI");

		xioctl(fd, UBI_IOCDET, &dev_num);
	} else
	if (do_mkvol) {
		struct ubi_mkvol_req req;
		int vol_name_len;
		if (opts & OPTION_M) {
			unsigned leb_avail;
			unsigned leb_size;
			unsigned num;
			char path[sizeof("/sys/class/ubi/ubi%d/avail_eraseblocks") + sizeof(int)*3];
			char *p;
			char buf[20];

			if (strncmp(ubi_ctrl, "/dev/ubi", 8) != 0)
				bb_error_msg_and_die("%s device node not in correct format", "UBI");
			num = xstrtou(ubi_ctrl+8, 10);
			p = path + sprintf(path, "/sys/class/ubi/ubi%d/", num);

			strcpy(p, "avail_eraseblocks");
			if ((num = open_read_close(path, buf, sizeof(buf))) <= 1)
				bb_error_msg_and_die("%s could not get LEB available", "UBI");
			buf[num-1] = '\0'; // trim trailing newline
			leb_avail = xstrtou(buf, 10);

			strcpy(p, "eraseblock_size");
			if ((num = open_read_close(path, buf, sizeof(buf))) <= 0)
				bb_error_msg_and_die("%s could not get LEB size", "UBI");
			buf[num-1] = '\0'; // trim trailing newline
			leb_size = xstrtou(buf, 10);

			size_bytes = leb_avail * leb_size;

			if (size_bytes <= 0)
				bb_error_msg_and_die("%s invalid maximum size calculated", "UBI");
		} else
		if (!(opts & OPTION_s))
			bb_error_msg_and_die("%s size not specified", "UBI");
		if (!(opts & OPTION_N))
			bb_error_msg_and_die("%s name not specified", "UBI");
		vol_name_len = strlen(vol_name);
		if (vol_name_len > UBI_MAX_VOLUME_NAME)
			bb_error_msg_and_die("%s volume name too long", "UBI");

		memset(&req, 0, sizeof(req));
		req.vol_id = vol_id;
		if ((opts & OPTION_t) && type) {
			if (type[0] == 's')
				req.vol_type = UBI_STATIC_VOLUME;
			else
				req.vol_type = UBI_DYNAMIC_VOLUME;
		} else {
			req.vol_type = UBI_DYNAMIC_VOLUME;
		}
		req.alignment = alignment;
		req.bytes = size_bytes;
		strncpy(req.name, vol_name, UBI_MAX_VOLUME_NAME);
		req.name_len = vol_name_len;

		xioctl(fd, UBI_IOCMKVOL, &req);
	} else
	if (do_rmvol) {
		if (!(opts & OPTION_n))
			bb_error_msg_and_die("%s volume id not specified", "UBI");

		xioctl(fd, UBI_IOCRMVOL, &vol_id);
	} else
	if (do_rsvol) {
		struct ubi_rsvol_req req;
		if (!(opts & OPTION_s))
			bb_error_msg_and_die("%s size not specified", "UBI");
		if (!(opts & OPTION_n))
			bb_error_msg_and_die("%s volume id not specified", "UBI");

		memset(&req, 0, sizeof(req));
		req.bytes = size_bytes;
		req.vol_id = vol_id;

		xioctl(fd, UBI_IOCRSVOL, &req);
	} else
	if (do_update) {
		long long bytes;

		if (opts & OPTION_t) {
			// truncate the volume by starting an update for size 0
			bytes = 0;
			xioctl(fd, UBI_IOCVOLUP, &bytes);
		}
		else {
			struct stat st;
			char buf[sizeof("/sys/class/ubi/ubi%d_%d/usable_eb_size") + 2 * sizeof(int)*3];
			int input_fd;
			unsigned ubinum, volnum;
			unsigned leb_size;
			ssize_t len;
			char *input_data;

			// Make assumption that device not is in normal format.
			// Removes need for scanning sysfs tree as full libubi does
			if (sscanf(ubi_ctrl, "/dev/ubi%u_%u", &ubinum, &volnum) != 2)
				bb_error_msg_and_die("%s volume node not in correct format", "UBI");

			sprintf(buf, "/sys/class/ubi/ubi%u_%u/usable_eb_size", ubinum, volnum);
			if (open_read_close(buf, buf, sizeof(buf)) <= 0)
				bb_error_msg_and_die("%s could not get LEB size", "UBI");
			if (sscanf(buf, "%u", &leb_size) != 1)
				bb_error_msg_and_die("%s could not get LEB size", "UBI");

			if (opts & OPTION_s) {
				input_fd = 0;
			} else {
				if (!argv[optind+1])
					bb_show_usage();
				xstat(argv[optind+1], &st);
				size_bytes = st.st_size;
				input_fd = xopen(argv[optind+1], O_RDONLY);
			}

			bytes = size_bytes;
			xioctl(fd, UBI_IOCVOLUP, &bytes);

			input_data = xmalloc(leb_size);
			while ((len = full_read(input_fd, input_data, leb_size)) > 0) {
				xwrite(fd, input_data, len);
			}
			if (len < 0)
				bb_error_msg_and_die("%s volume update failed", "UBI");
			if (ENABLE_FEATURE_CLEAN_UP)
				close(input_fd);
		}
	}

	if (ENABLE_FEATURE_CLEAN_UP)
		close(fd);

	return EXIT_SUCCESS;
}