1/*
2 * WAPI mtd 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: wapi_utils.c 241182 2011-02-17 21:50:03Z $
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
32#ifdef LINUX26
33#include <mtd/mtd-user.h>
34#else /* LINUX26 */
35#include <linux/mtd/mtd.h>
36#endif /* LINUX26 */
37
38#include <bcmconfig.h>
39#include <wapi_utils.h>
40
41static unsigned short
42wapi_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
66wapi_mtd_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
97wapi_mtd_backup()
98{
99	char *cmd = "tar cf - "CONFIG_DIR" -C "RAMFS_WAPI_DIR" | gzip -c > "WAPI_TGZ_TMP_FILE;
100	mtd_info_t mtd_info;
101	erase_info_t erase_info;
102	int mtd_fd = -1;
103	FILE *fp = NULL;
104	char *buf = NULL;
105	struct stat tmp_stat;
106	int ret = -1;
107	wapi_mtd_hdr_t mtd_hdr;
108
109	/* backup wapi directiries to raw partition */
110	unlink(WAPI_TGZ_TMP_FILE);
111
112	/* Open MTD device and get sector size */
113	if ((mtd_fd = wapi_mtd_open("wapi", O_RDWR)) < 0 ||
114	    ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0) {
115		perror("wapi");
116		goto fail;
117	}
118
119	/* create as tar file */
120	errno = 0;
121	system(cmd);
122	if (stat(WAPI_TGZ_TMP_FILE, &tmp_stat) ||
123	    errno == ENOENT) {
124		perror("tgz");
125		goto fail;
126	}
127
128	if ((tmp_stat.st_size + sizeof(wapi_mtd_hdr_t)) > mtd_info.size || tmp_stat.st_size == 0) {
129		perror("size");
130		goto fail;
131	}
132
133	/* Allocate temporary buffer */
134	if ((buf = malloc(tmp_stat.st_size)) == NULL) {
135		perror("malloc");
136		goto fail;
137	}
138
139	fp = fopen(WAPI_TGZ_TMP_FILE, "r");
140	if (fp == NULL) {
141		fprintf(stderr, "%s: can't open file\n", WAPI_TGZ_TMP_FILE);
142		goto fail;
143	}
144
145	if (fread(buf, 1, tmp_stat.st_size, fp) != tmp_stat.st_size) {
146		perror("read");
147		goto fail;
148	}
149
150	erase_info.start = 0;
151	erase_info.length = mtd_info.size;
152
153	/* create mtd header content */
154	memcpy(&mtd_hdr.magic, WAPI_MTD_MAGIC, 4);
155	mtd_hdr.len = tmp_stat.st_size;
156	mtd_hdr.checksum = wapi_checksum(buf, tmp_stat.st_size);
157
158	/* Do it */
159	(void) ioctl(mtd_fd, MEMUNLOCK, &erase_info);
160	if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0 ||
161	    write(mtd_fd, &mtd_hdr, sizeof(wapi_mtd_hdr_t)) != sizeof(wapi_mtd_hdr_t) ||
162	    write(mtd_fd, buf, tmp_stat.st_size) != tmp_stat.st_size) {
163		perror("write");
164		goto fail;
165	}
166
167	printf("update wapi partition OK\n");
168	ret = 0;
169
170fail:
171	unlink(WAPI_TGZ_TMP_FILE);
172
173	if (buf)
174		free(buf);
175
176	if (mtd_fd >= 0)
177		close(mtd_fd);
178
179	if (fp)
180		fclose(fp);
181
182	return ret;
183}
184
185/*
186 * Write a file to an MTD device
187 * @param	path	file to write or a URL
188 * @param	mtd	path to or partition name of MTD device
189 * @return	0 on success and errno on failure
190 */
191int
192wapi_mtd_restore()
193{
194	char *cmd = "gunzip -c "WAPI_TGZ_TMP_FILE" | tar xf - -C "RAMFS_WAPI_DIR;
195	mtd_info_t mtd_info;
196	int mtd_fd = -1;
197	FILE *fp = NULL;
198	char *buf = NULL;
199	int ret = -1;
200	wapi_mtd_hdr_t mtd_hdr = {0};
201
202	/* create wapi directory */
203	mkdir(RAMFS_WAPI_DIR, 0777);
204
205	/* Open MTD device and get sector size */
206	if ((mtd_fd = wapi_mtd_open("wapi", O_RDWR)) < 0 ||
207	    ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0) {
208		printf("mtd");
209		goto fail;
210	}
211
212	read(mtd_fd, &mtd_hdr, sizeof(wapi_mtd_hdr_t));
213
214	if (memcmp(&mtd_hdr.magic, WAPI_MTD_MAGIC, 4)) {
215		printf("magic incorrect");
216		goto fail;
217	}
218
219	if (mtd_hdr.len > mtd_info.size) {
220		printf("size too long");
221		goto fail;
222	}
223
224	/* Allocate temporary buffer */
225	if ((buf = malloc(mtd_hdr.len)) == NULL) {
226		printf("buffer");
227		goto fail;
228	}
229	read(mtd_fd, buf, mtd_hdr.len);
230
231	if (wapi_checksum(buf, mtd_hdr.len) != mtd_hdr.checksum) {
232		printf("checksum");
233		goto fail;
234	}
235
236	/* write mtd data to tar file */
237	if ((fp = fopen(WAPI_TGZ_TMP_FILE, "w")) == NULL) {
238		printf("%s: can't open file\n", WAPI_TGZ_TMP_FILE);
239		goto fail;
240	}
241	fwrite(buf, 1, mtd_hdr.len, fp);
242	fclose(fp);
243
244	/* untar wapi directory */
245	system(cmd);
246
247	unlink(WAPI_TGZ_TMP_FILE);
248
249	ret = 0;
250fail:
251	if (buf)
252		free(buf);
253
254	if (mtd_fd >= 0)
255		close(mtd_fd);
256
257	if (ret) {
258		/* create directory */
259		mkdir(WAPI_WAI_DIR, 0777);
260		mkdir(WAPI_AS_DIR, 0777);
261	}
262
263	return ret;
264}
265