1/*
2 * confmtd read/write utility functions
3 *
4 * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: $
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25#include <limits.h>
26#include <sys/stat.h>
27#include <fcntl.h>
28#include <errno.h>
29#include <error.h>
30#include <sys/ioctl.h>
31#include <dirent.h>
32
33#ifdef LINUX26
34#include <mtd/mtd-user.h>
35#else /* LINUX26 */
36#include <linux/mtd/mtd.h>
37#endif /* LINUX26 */
38
39#include <confmtd_utils.h>
40
41static unsigned short
42confmtd_checksum(const char *data, int datalen)
43{
44	unsigned short checksum = 0;
45	unsigned short *ptr = (unsigned short *)data;
46	int len = datalen;
47
48	while (len > 0) {
49		if (len == 1)
50			checksum += (*ptr & 0xff00);
51		else
52			checksum += *ptr;
53		ptr++;
54		len -= 2;
55	}
56	return checksum;
57}
58
59/*
60 * Open an MTD device
61 * @param	mtd	path to or partition name of MTD device
62 * @param	flags	open() flags
63 * @return	return value of open()
64 */
65int
66confmtd_open(const char *mtd, int flags)
67{
68	FILE *fp;
69	char dev[PATH_MAX];
70	int i;
71
72	if ((fp = fopen("/proc/mtd", "r"))) {
73		while (fgets(dev, sizeof(dev), fp)) {
74			if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
75#ifdef LINUX26
76				snprintf(dev, sizeof(dev), "/dev/mtd%d", i);
77#else
78				snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
79#endif
80				fclose(fp);
81				return open(dev, flags);
82			}
83		}
84		fclose(fp);
85	}
86
87	return open(mtd, flags);
88}
89
90/*
91 * Write a file to an MTD device
92 * @param	path	file to write or a URL
93 * @param	mtd	path to or partition name of MTD device
94 * @return	0 on success and errno on failure
95 */
96int
97confmtd_backup()
98{
99	char *cmd = "tar cf - confmtd -C /tmp | gzip -c > "CONFMTD_TGZ_TMP_FILE;
100	const char *cp_file = "cp "CONFMTD_TGZ_TMP_FILE" "NAND_DIR;
101	mtd_info_t mtd_info;
102	erase_info_t erase_info;
103	int mtd_fd = -1;
104	char *buf = NULL;
105	char *tmp_buf = NULL;
106	struct stat tmp_stat;
107	int ret = -1;
108	confmtd_hdr_t mtd_hdr;
109	DIR *dir;
110	int fd = -1;
111
112	/* backup confmtd directiries to raw partition */
113	unlink(CONFMTD_TGZ_TMP_FILE);
114
115	if ((dir = opendir(NAND_DIR))) {
116		closedir(dir);
117		system(cmd);
118		system(cp_file);
119
120		unlink(CONFMTD_TGZ_TMP_FILE);
121
122		return 0;
123	}
124
125	if (!(dir = opendir(RAMFS_CONFMTD_DIR))) {
126		fprintf(stderr, "Cannot find %s\n", RAMFS_CONFMTD_DIR);
127		return -1;
128	} else {
129		closedir(dir);
130	}
131
132	/* Open MTD device and get sector size */
133	if ((mtd_fd = confmtd_open("confmtd", O_RDWR)) < 0 ||
134	    ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0) {
135		perror("confmtd");
136		goto fail;
137	}
138
139	/* create as tar file */
140	errno = 0;
141
142	system(cmd);
143
144	if ((fd = open(CONFMTD_TGZ_TMP_FILE, O_RDONLY)) < 0) {
145		fprintf(stderr, "Open %s fail\n", CONFMTD_TGZ_TMP_FILE);
146		goto fail;
147	}
148
149	if (fstat(fd, &tmp_stat)) {
150		perror("tgz");
151		goto fail;
152	}
153
154	if ((tmp_stat.st_size + sizeof(confmtd_hdr_t)) > mtd_info.size || tmp_stat.st_size == 0) {
155		perror("size");
156		goto fail;
157	}
158
159	/* Allocate temporary buffer */
160	if ((tmp_buf = malloc(tmp_stat.st_size)) == NULL) {
161		perror("malloc");
162		goto fail;
163	}
164
165	if ((buf = malloc(tmp_stat.st_size + sizeof(confmtd_hdr_t))) == NULL) {
166		perror("malloc");
167		goto fail;
168	}
169
170	if (read(fd, tmp_buf, tmp_stat.st_size) != tmp_stat.st_size) {
171		fprintf(stderr, "read %s: size mismatch\n", CONFMTD_TGZ_TMP_FILE);
172		goto fail;
173	}
174
175	erase_info.start = 0;
176	erase_info.length = mtd_info.size;
177
178	/* create mtd header content */
179	memset(&mtd_hdr, 0, sizeof(mtd_hdr));
180	snprintf(&mtd_hdr.magic, sizeof(mtd_hdr.magic), "%s", CONFMTD_MAGIC);
181	mtd_hdr.len = tmp_stat.st_size;
182	mtd_hdr.checksum = confmtd_checksum(tmp_buf, tmp_stat.st_size);
183
184	memcpy(buf, &mtd_hdr, sizeof(confmtd_hdr_t));
185	memcpy((buf + sizeof(confmtd_hdr_t)), tmp_buf, tmp_stat.st_size);
186
187	/* Do it */
188	(void) ioctl(mtd_fd, MEMUNLOCK, &erase_info);
189	if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0 ||
190	    write(mtd_fd, buf, (sizeof(confmtd_hdr_t) + tmp_stat.st_size)) !=
191		(sizeof(confmtd_hdr_t) + tmp_stat.st_size)) {
192		perror("write");
193		goto fail;
194	}
195
196	printf("update confmtd partition OK\n");
197	ret = 0;
198
199fail:
200	if (tmp_buf)
201		free(tmp_buf);
202
203	if (buf)
204		free(buf);
205
206	if (mtd_fd >= 0)
207		close(mtd_fd);
208
209	if (fd >= 0)
210		close(fd);
211
212	unlink(CONFMTD_TGZ_TMP_FILE);
213
214	return ret;
215}
216
217/*
218 * Write a file to an MTD device
219 * @param	path	file to write or a URL
220 * @param	mtd	path to or partition name of MTD device
221 * @return	0 on success and errno on failure
222 */
223int
224confmtd_restore()
225{
226	char *cmd = "gunzip -c "CONFMTD_TGZ_TMP_FILE" | tar xf - -C /tmp";
227	const char *cp_file = "cp "NAND_FILE" "CONFMTD_TGZ_TMP_FILE;
228	mtd_info_t mtd_info;
229	int mtd_fd = -1;
230	FILE *fp = NULL;
231	char *buf = NULL;
232	int ret = -1;
233	confmtd_hdr_t mtd_hdr = {0};
234	struct stat tmp_stat;
235	DIR *dir;
236
237	/* create confmtd directory */
238	if (mkdir(RAMFS_CONFMTD_DIR, 0777) < 0 && errno != EEXIST) {
239		fprintf(stderr, "%s not created\n", RAMFS_CONFMTD_DIR);
240		return ret;
241	}
242
243	if ((dir = opendir(NAND_DIR))) {
244		closedir(dir);
245		if (stat(NAND_FILE, &tmp_stat)) {
246			perror(NAND_FILE);
247			return ret;
248		}
249		system(cp_file);
250		system(cmd);
251
252		unlink(CONFMTD_TGZ_TMP_FILE);
253
254		return 0;
255	}
256
257	/* Open MTD device and get sector size */
258	if ((mtd_fd = confmtd_open("confmtd", O_RDWR)) < 0 ||
259	    ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0) {
260		printf("mtd\n");
261		goto fail;
262	}
263
264	read(mtd_fd, &mtd_hdr, sizeof(confmtd_hdr_t));
265
266	if (strcmp(&mtd_hdr.magic, CONFMTD_MAGIC) != 0) {
267		printf("magic incorrect\n");
268		goto fail;
269	}
270
271	if (mtd_hdr.len > mtd_info.size) {
272		printf("size too long\n");
273		goto fail;
274	}
275
276	/* Allocate temporary buffer */
277	if ((buf = malloc(mtd_hdr.len)) == NULL) {
278		printf("buffer\n");
279		goto fail;
280	}
281	read(mtd_fd, buf, mtd_hdr.len);
282
283	if (confmtd_checksum(buf, mtd_hdr.len) != mtd_hdr.checksum) {
284		printf("checksum\n");
285		goto fail;
286	}
287
288	/* write mtd data to tar file */
289	if ((fp = fopen(CONFMTD_TGZ_TMP_FILE, "w")) == NULL) {
290		printf("%s: can't open file\n", CONFMTD_TGZ_TMP_FILE);
291		goto fail;
292	}
293	fwrite(buf, 1, mtd_hdr.len, fp);
294	fclose(fp);
295
296	/* untar confmtd directory */
297	system(cmd);
298
299	unlink(CONFMTD_TGZ_TMP_FILE);
300
301	ret = 0;
302fail:
303	if (buf)
304		free(buf);
305
306	if (mtd_fd >= 0)
307		close(mtd_fd);
308
309	return ret;
310}
311