1/*
2
3	Tomato Firmware
4	USB Support Module
5
6*/
7#include <string.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <unistd.h>
11#include <ctype.h>
12#include <fcntl.h>
13#include <sys/stat.h>
14#include <stdarg.h>
15#include <syslog.h>
16#include <sys/ioctl.h>
17#include <net/if.h>
18#include <dirent.h>
19#include <sys/socket.h>
20#include <arpa/inet.h>
21#include <sys/sysinfo.h>
22#include <sys/types.h>
23#include <linux/version.h>
24
25#include <bcmnvram.h>
26#include <bcmdevs.h>
27#include <wlutils.h>
28
29#include "shutils.h"
30#include "shared.h"
31
32#include <linux/version.h>
33#ifndef LINUX_KERNEL_VERSION
34#define LINUX_KERNEL_VERSION LINUX_VERSION_CODE
35#endif
36
37/* Serialize using fcntl() calls
38 */
39
40int check_magic(char *buf, char *magic){
41	if(!strncmp(magic, "ext3_chk", 8)){
42		if(!((*buf)&4))
43			return 0;
44		if(*(buf+4) >= 0x40)
45			return 0;
46		if(*(buf+8) >= 8)
47			return 0;
48		return 1;
49	}
50
51	if(!strncmp(magic, "ext4_chk", 8)){
52		if(!((*buf)&4))
53			return 0;
54		if(*(buf+4) > 0x3F)
55			return 1;
56		if(*(buf+4) >= 0x40)
57			return 0;
58		if(*(buf+8) <= 7)
59			return 0;
60		return 1;
61	}
62
63	return 0;
64}
65
66char *detect_fs_type(char *device)
67{
68	int fd;
69	unsigned char buf[4096];
70
71	if ((fd = open(device, O_RDONLY)) < 0)
72		return NULL;
73
74	if (read(fd, buf, sizeof(buf)) != sizeof(buf))
75	{
76		close(fd);
77		return NULL;
78	}
79
80	close(fd);
81
82	/* first check for mbr */
83	if (*device && device[strlen(device) - 1] > '9' &&
84		buf[510] == 0x55 && buf[511] == 0xAA && /* signature */
85		((buf[0x1be] | buf[0x1ce] | buf[0x1de] | buf[0x1ee]) & 0x7f) == 0) /* boot flags */
86	{
87		return "mbr";
88	}
89	/* detect swap */
90	else if (memcmp(buf + 4086, "SWAPSPACE2", 10) == 0 ||
91		 memcmp(buf + 4086, "SWAP-SPACE", 10) == 0 ||
92		 memcmp(buf + 4086, "S1SUSPEND", 9) == 0 ||
93		 memcmp(buf + 4086, "S2SUSPEND", 9) == 0 ||
94		 memcmp(buf + 4086, "ULSUSPEND", 9) == 0)
95	{
96		return "swap";
97	}
98	/* detect ext2/3/4 */
99	else if (buf[0x438] == 0x53 && buf[0x439] == 0xEF)
100	{
101		if(check_magic((char *) &buf[0x45c], "ext3_chk"))
102			return "ext3";
103		else if(check_magic((char *) &buf[0x45c], "ext4_chk"))
104			return "ext4";
105		else
106			return "ext2";
107	}
108	/* detect hfs */
109	else if(buf[1024] == 0x48){
110		if(!memcmp(buf+1032, "HFSJ", 4)){
111			if(buf[1025] == 0x58) // with case-sensitive
112				return "hfs+jx";
113			else
114				return "hfs+j";
115		}
116		else
117			return "hfs";
118	}
119	/* detect ntfs */
120	else if (buf[510] == 0x55 && buf[511] == 0xAA && /* signature */
121		memcmp(buf + 3, "NTFS    ", 8) == 0)
122	{
123		return "ntfs";
124	}
125	/* detect vfat */
126	else if (buf[510] == 0x55 && buf[511] == 0xAA && /* signature */
127		buf[11] == 0 && buf[12] >= 1 && buf[12] <= 8 /* sector size 512 - 4096 */ &&
128		buf[13] != 0 && (buf[13] & (buf[13] - 1)) == 0) /* sectors per cluster */
129	{
130		if(buf[6] == 0x20 && buf[7] == 0x20 && !memcmp(buf+71, "EFI        ", 11))
131			return "apple_efi";
132		else
133			return "vfat";
134	}
135
136	return "unknown";
137}
138
139
140/* Execute a function for each disc partition on the specified controller.
141 *
142 * Directory /dev/discs/ looks like this:
143 * disc0 -> ../scsi/host0/bus0/target0/lun0/
144 * disc1 -> ../scsi/host1/bus0/target0/lun0/
145 * disc2 -> ../scsi/host2/bus0/target0/lun0/
146 * disc3 -> ../scsi/host2/bus0/target0/lun1/
147 *
148 * Scsi host 2 supports multiple drives.
149 * Scsi host 0 & 1 support one drive.
150 *
151 * For attached drives, like this.  If not attached, there is no "part#" item.
152 * Here, only one drive, with 2 partitions, is plugged in.
153 * /dev/discs/disc0/disc
154 * /dev/discs/disc0/part1
155 * /dev/discs/disc0/part2
156 * /dev/discs/disc1/disc
157 * /dev/discs/disc2/disc
158 *
159 * Which is the same as:
160 * /dev/scsi/host0/bus0/target0/lun0/disc
161 * /dev/scsi/host0/bus0/target0/lun0/part1
162 * /dev/scsi/host0/bus0/target0/lun0/part2
163 * /dev/scsi/host1/bus0/target0/lun0/disc
164 * /dev/scsi/host2/bus0/target0/lun0/disc
165 * /dev/scsi/host2/bus0/target0/lun1/disc
166 *
167 * Implementation notes:
168 * Various mucking about with a disc that just got plugged in or unplugged
169 * will make the scsi subsystem try a re-validate, and read the partition table of the disc.
170 * This will make sure the partitions show up.
171 *
172 * It appears to try to do the revalidate and re-read & update the partition
173 * information when this code does the "readdir of /dev/discs/disc0/?".  If the
174 * disc has any mounted partitions the revalidate will be rejected.  So the
175 * current partition info will remain.  On an unplug event, when it is doing the
176 * readdir's, it will try to do the revalidate as we are doing the readdir's.
177 * But luckily they'll be rejected, otherwise the later partitions will disappear as
178 * soon as we get the first one.
179 * But be very careful!  If something goes not exactly right, the partition entries
180 * will disappear before we've had a chance to unmount from them.
181 *
182 * To avoid this automatic revalidation, we go through /proc/partitions looking for the partitions
183 * that /dev/discs point to.  That will avoid the implicit revalidate attempt.
184 *
185 * If host < 0, do all hosts.   If >= 0, it is the host number to do.
186 *
187 */
188
189/* check if the block device has no partition */
190int is_no_partition(const char *discname)
191{
192	FILE *procpt;
193	char line[128], ptname[32];
194	int ma, mi, sz;
195	int count = 0;
196
197	if ((procpt = fopen("/proc/partitions", "r"))) {
198		while (fgets(line, sizeof(line), procpt)) {
199			if (sscanf(line, " %d %d %d %[^\n ]", &ma, &mi, &sz, ptname) != 4)
200				continue;
201			if (strstr(ptname, discname))
202				count++;
203		}
204	}
205
206	return (count == 1);
207}
208
209int exec_for_host(int host, int obsolete, uint flags, host_exec func)
210{
211	DIR *usb_dev_disc;
212	char ptname[32];/* Will be: discDN_PN	 					*/
213	char dsname[16];/* Will be: discDN	 					*/
214	int host_no;	/* SCSI controller/host # */
215	struct dirent *dp;
216	FILE *prt_fp;
217	int siz;
218	char line[256];
219	int result = 0;
220#ifdef LINUX26
221	int ret;
222	char hostbuf[16], device_path[PATH_MAX], linkbuf[PATH_MAX], *h;
223#else
224	char link[256];	/* Will be: ../scsi/host#/bus0/target0/lun#  that bfr links to. */
225			/* When calling the func, will be: /dev/discs/disc#/part#	*/
226	char bfr[256];	/* Will be: /dev/discs/disc#					*/
227	char bfr2[128];	/* Will be: /dev/discs/disc#/disc     for the BLKRRPART.	*/
228	char *cp;
229	int len;
230	int disc_num;	/* Disc # */
231	int part_num;	/* Parition # */
232	char *mp;	/* Ptr to after any leading ../ path */
233#endif
234
235	_dprintf("exec_for_host(%d, %d, %d, %d)\n", host, obsolete, flags, func);
236	if (!func)
237		return 0;
238
239	flags |= EFH_1ST_HOST;
240
241#ifdef LINUX26
242	/* /sys/bus/scsi/devices/X:X:X:X/block:sdX doesn't exist in kernel 3.0
243	 * 1. Enumerate sub-directory, DIR, of /sys/block.
244	 * 2. Skip ., .., loop*, mtdblock*, ram*, etc.
245	 * 3. read DIR/device link. Check whether X:X:X:X exist. e.g.
246	 *    56U: ../../devices/platform/rt3xxx-ehci/usb1/1-1/1-1:1.0/host1/target1:0:0/1:0:0:0
247	 *    65U: ../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-2/1-2:1.0/host1/target1:0:0/1:0:0:0
248	 * 4. If yes, DIR would be sda, sdb, etc.
249	 * 5. Search DIR in /proc/partitions.
250	 */
251	sprintf(hostbuf, "%d:", host);
252	if (!(usb_dev_disc = opendir("/sys/block")))
253		return 0;
254	while ((dp = readdir(usb_dev_disc))) {
255		if (!strncmp(dp->d_name, "loop", 4) ||
256		    !strncmp(dp->d_name, "mtdblock", 8) ||
257		    !strncmp(dp->d_name, "ram", 3) ||
258		    !strcmp(dp->d_name, ".") ||
259		    !strcmp(dp->d_name, "..")
260		   )
261			continue;
262#if LINUX_KERNEL_VERSION >= KERNEL_VERSION(3,3,0)
263		snprintf(device_path, sizeof(device_path), "/sys/block/%s", dp->d_name);
264#else
265		snprintf(device_path, sizeof(device_path), "/sys/block/%s/device", dp->d_name);
266#endif
267		if (readlink(device_path, linkbuf, sizeof(linkbuf)) == -1)
268			continue;
269		h = strstr(linkbuf, "/host");
270		if (!h)	continue;
271		if ((ret = sscanf(h, "/host%*d/target%*d:%*d:%*d/%d:%*d:%*d:%*d", &host_no)) != 1) {
272			_dprintf("%s(): sscanf can't distinguish host_no from [%s]. ret %d\n", __func__, linkbuf, ret);
273			continue;
274		}
275		if (host >= 0 && host != host_no)
276			continue;
277		snprintf(dsname, sizeof(dsname), dp->d_name);
278		siz = strlen(dsname);
279		flags |= EFH_1ST_DISC;
280		if (!(prt_fp = fopen("/proc/partitions", "r")))
281			continue;
282		while (fgets(line, sizeof(line) - 2, prt_fp)) {
283			if (sscanf(line, " %*s %*s %*s %s", ptname) != 1)
284				continue;
285
286			if (!strncmp(ptname, dsname, siz)) {
287				if (!strcmp(ptname, dsname) && !is_no_partition(dsname))
288					continue;
289				sprintf(line, "/dev/%s", ptname);
290				result = (*func)(line, host_no, dsname, ptname, flags) || result;
291				flags &= ~(EFH_1ST_HOST | EFH_1ST_DISC);
292			}
293		}
294		fclose(prt_fp);
295	}
296	closedir(usb_dev_disc);
297
298#else	/* !LINUX26 */
299
300	if ((usb_dev_disc = opendir(DEV_DISCS_ROOT))) {
301		while ((dp = readdir(usb_dev_disc))) {
302			sprintf(bfr, "%s/%s", DEV_DISCS_ROOT, dp->d_name);
303			if (strncmp(dp->d_name, "disc", 4) != 0)
304				continue;
305
306			disc_num = atoi(dp->d_name + 4);
307			len = readlink(bfr, link, sizeof(link) - 1);
308			if (len < 0)
309				continue;
310
311			link[len] = 0;
312			cp = strstr(link, "/scsi/host");
313			if (!cp)
314				continue;
315
316			host_no = atoi(cp + 10);
317			if (host >= 0 && host_no != host)
318				continue;
319
320			/* We have found a disc that is on this controller.
321			 * Loop thru all the partitions on this disc.
322			 * The new way, reading thru /proc/partitions.
323			 */
324			mp = link;
325			if ((cp = strstr(link, "../")) != NULL)
326				mp = cp + 3;
327			siz = strlen(mp);
328
329			flags |= EFH_1ST_DISC;
330			if (func && (prt_fp = fopen("/proc/partitions", "r"))) {
331				while (fgets(line, sizeof(line) - 2, prt_fp)) {
332					if (sscanf(line, " %*s %*s %*s %s", bfr2) == 1 &&
333					    strncmp(bfr2, mp, siz) == 0)
334					{
335						if ((cp = strstr(bfr2, "/part"))) {
336							part_num = atoi(cp + 5);
337							sprintf(line, "%s/part%d", bfr, part_num);
338							sprintf(dsname, "disc%d", disc_num);
339							sprintf(ptname, "disc%d_%d", disc_num, part_num);
340						}
341						else if ((cp = strstr(bfr2, "/disc"))) {
342							*(++cp) = 0;
343							if (!is_no_partition(bfr2))
344								continue;
345							sprintf(line, "%s/disc", bfr);
346							sprintf(dsname, "disc%d", disc_num);
347							strcpy(ptname, dsname);
348						}
349						else {
350							continue;
351						}
352						result = (*func)(line, host_no, dsname, ptname, flags) || result;
353						flags &= ~(EFH_1ST_HOST | EFH_1ST_DISC);
354					}
355				}
356				fclose(prt_fp);
357			}
358		}
359		closedir(usb_dev_disc);
360	}
361
362#endif	/* LINUX26 */
363
364	return result;
365}
366
367/* Concept taken from the e2fsprogs/ismounted.c.
368 * Find wherever 'file' (actually: device) is mounted.
369 * Either the exact same device-name, or another device-name.
370 * The latter is detected by comparing the rdev or dev&inode.
371 * So aliasing won't fool us---we'll still find if it's mounted.
372 * Return its mnt entry.
373 * In particular, the caller would look at the mnt->mountpoint.
374 *
375 * Find the matching devname(s) in mounts or swaps.
376 * If func is supplied, call it for each match.  If not, return mnt on the first match.
377 */
378
379static inline int is_same_device(char *fsname, dev_t file_rdev, dev_t file_dev, ino_t file_ino)
380{
381	struct stat st_buf;
382
383	if (stat(fsname, &st_buf) == 0) {
384		if (S_ISBLK(st_buf.st_mode)) {
385			if (file_rdev && (file_rdev == st_buf.st_rdev))
386				return 1;
387		}
388		else {
389			if (file_dev && ((file_dev == st_buf.st_dev) &&
390				(file_ino == st_buf.st_ino)))
391				return 1;
392			/* Check for [swap]file being on the device. */
393			if (file_dev == 0 && file_ino == 0 && file_rdev == st_buf.st_dev)
394				return 1;
395		}
396	}
397	return 0;
398}
399
400
401struct mntent *findmntents(char *file, int swp, int (*func)(struct mntent *mnt, uint flags), uint flags)
402{
403	struct mntent	*mnt;
404	struct stat	st_buf;
405	dev_t		file_dev=0, file_rdev=0;
406	ino_t		file_ino=0;
407	FILE		*f;
408
409	if ((f = setmntent(swp ? "/proc/swaps": "/proc/mounts", "r")) == NULL)
410		return NULL;
411
412	if (stat(file, &st_buf) == 0) {
413		if (S_ISBLK(st_buf.st_mode)) {
414			file_rdev = st_buf.st_rdev;
415		}
416		else {
417			file_dev = st_buf.st_dev;
418			file_ino = st_buf.st_ino;
419		}
420	}
421	while ((mnt = getmntent(f)) != NULL) {
422		/* Always ignore rootfs mount */
423		if (strcmp(mnt->mnt_fsname, "rootfs") == 0)
424			continue;
425
426		if (strcmp(file, mnt->mnt_fsname) == 0 ||
427		    strcmp(file, mnt->mnt_dir) == 0 ||
428		    is_same_device(mnt->mnt_fsname, file_rdev , file_dev, file_ino)) {
429			if (func == NULL)
430				break;
431			(*func)(mnt, flags);
432		}
433	}
434
435	endmntent(f);
436	return mnt;
437}
438
439
440//#define SAME_AS_KERNEL
441/* Simulate a hotplug event, as if a USB storage device
442 * got plugged or unplugged.
443 * Either use a hardcoded program name, or the same
444 * hotplug program that the kernel uses for a real event.
445 */
446void add_remove_usbhost(char *host, int add)
447{
448	setenv("ACTION", add ? "add" : "remove", 1);
449	setenv("SCSI_HOST", host, 1);
450	setenv("PRODUCT", host, 1);
451	setenv("INTERFACE", "TOMATO/0", 1);
452#ifdef SAME_AS_KERNEL
453	char pgm[256] = "/sbin/hotplug usb";
454	char *p;
455	int fd = open("/proc/sys/kernel/hotplug", O_RDONLY);
456	if (fd) {
457		if (read(fd, pgm, sizeof(pgm) - 5) >= 0) {
458			if ((p = strchr(pgm, '\n')) != NULL)
459				*p = 0;
460			strcat(pgm, " usb");
461		}
462		close(fd);
463	}
464	system(pgm);
465#else
466	// don't use value from /proc/sys/kernel/hotplug
467	// since it may be overriden by a user.
468	system("/sbin/hotplug usb");
469#endif
470	unsetenv("INTERFACE");
471	unsetenv("PRODUCT");
472	unsetenv("SCSI_HOST");
473	unsetenv("ACTION");
474}
475
476
477/****************************************************/
478/* Use busybox routines to get labels for fat & ext */
479/* Probe for label the same way that mount does.    */
480/****************************************************/
481
482#define VOLUME_ID_LABEL_SIZE		64
483#define VOLUME_ID_UUID_SIZE		36
484#define SB_BUFFER_SIZE			0x11000
485
486struct volume_id {
487	int		fd;
488	int		error;
489	size_t		sbbuf_len;
490	size_t		seekbuf_len;
491	uint8_t		*sbbuf;
492	uint8_t		*seekbuf;
493	uint64_t	seekbuf_off;
494	char		label[VOLUME_ID_LABEL_SIZE+1];
495	char		uuid[VOLUME_ID_UUID_SIZE+1];
496};
497
498extern void volume_id_set_uuid();
499extern void *volume_id_get_buffer();
500extern void volume_id_free_buffer();
501extern int volume_id_probe_ext();
502extern int volume_id_probe_vfat();
503extern int volume_id_probe_ntfs();
504extern int volume_id_probe_linux_swap();
505extern int volume_id_probe_hfs_hfsplus(struct volume_id *id);
506
507/* Put the label in *label and uuid in *uuid.
508 * Return 0 if no label/uuid found, NZ if there is a label or uuid.
509 */
510int find_label_or_uuid(char *dev_name, char *label, char *uuid)
511{
512	struct volume_id id;
513
514	memset(&id, 0x00, sizeof(id));
515	if (label) *label = 0;
516	if (uuid) *uuid = 0;
517	if ((id.fd = open(dev_name, O_RDONLY)) < 0)
518		return 0;
519
520	volume_id_get_buffer(&id, 0, SB_BUFFER_SIZE);
521
522	if (volume_id_probe_linux_swap(&id) == 0 || id.error)
523		goto ret;
524	if (volume_id_probe_vfat(&id) == 0 || id.error)
525		goto ret;
526	if (volume_id_probe_ext(&id) == 0 || id.error)
527		goto ret;
528	if (volume_id_probe_ntfs(&id) == 0 || id.error)
529		goto ret;
530#if defined(RTCONFIG_HFS)
531	if(volume_id_probe_hfs_hfsplus(&id) == 0 || id.error)
532		goto ret;
533#endif
534ret:
535	volume_id_free_buffer(&id);
536	if (label && (*id.label != 0))
537		strcpy(label, id.label);
538	if (uuid && (*id.uuid != 0))
539		strcpy(uuid, id.uuid);
540	close(id.fd);
541	return (label && *label != 0) || (uuid && *uuid != 0);
542}
543
544void *xmalloc(size_t siz)
545{
546	return (malloc(siz));
547}
548#if 0
549static void *xrealloc(void *old, size_t size)
550{
551	return realloc(old, size);
552}
553#endif
554ssize_t full_read(int fd, void *buf, size_t len)
555{
556	return read(fd, buf, len);
557}
558