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