Fixed execution of .autorun and aftermount scripts
[tomato.git] / release / src / router / shared / usb.c
blob58bf116b2a64d92d635a26b0fef4d262f6bee5e4
1 /*
3 Tomato Firmware
4 USB Support Module
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>
24 #include <bcmnvram.h>
25 #include <bcmdevs.h>
26 #include <wlutils.h>
28 #include "shutils.h"
29 #include "shared.h"
32 /* Serialize using fcntl() calls
35 int file_lock(char *tag)
37 char fn[64];
38 struct flock lock;
39 int lockfd = -1;
40 pid_t lockpid;
42 sprintf(fn, "/var/lock/%s.lock", tag);
43 if ((lockfd = open(fn, O_CREAT | O_RDWR, 0666)) < 0)
44 goto lock_error;
46 pid_t pid = getpid();
47 if (read(lockfd, &lockpid, sizeof(pid_t))) {
48 // check if we already hold a lock
49 if (pid == lockpid) {
50 // don't close the file here as that will release all locks
51 return -1;
55 memset(&lock, 0, sizeof(lock));
56 lock.l_type = F_WRLCK;
57 lock.l_pid = pid;
59 if (fcntl(lockfd, F_SETLKW, &lock) < 0) {
60 close(lockfd);
61 goto lock_error;
64 lseek(lockfd, 0, SEEK_SET);
65 write(lockfd, &pid, sizeof(pid_t));
66 return lockfd;
67 lock_error:
68 // No proper error processing
69 syslog(LOG_DEBUG, "Error %d locking %s, proceeding anyway", errno, fn);
70 return -1;
73 void file_unlock(int lockfd)
75 if (lockfd >= 0) {
76 ftruncate(lockfd, 0);
77 close(lockfd);
81 char *detect_fs_type(char *device)
83 int fd;
84 unsigned char buf[4096];
86 if ((fd = open(device, O_RDONLY)) < 0)
87 return NULL;
89 if (read(fd, buf, sizeof(buf)) != sizeof(buf))
91 close(fd);
92 return NULL;
95 close(fd);
97 /* first check for mbr */
98 if (*device && device[strlen(device) - 1] > '9' &&
99 buf[510] == 0x55 && buf[511] == 0xAA && /* signature */
100 ((buf[0x1be] | buf[0x1ce] | buf[0x1de] | buf[0x1ee]) & 0x7f) == 0) /* boot flags */
102 return "mbr";
104 /* detect swap */
105 else if (memcmp(buf + 4086, "SWAPSPACE2", 10) == 0 ||
106 memcmp(buf + 4086, "SWAP-SPACE", 10) == 0)
108 return "swap";
110 /* detect ext2/3 */
111 else if (buf[0x438] == 0x53 && buf[0x439] == 0xEF)
113 return ((buf[0x460] & 0x0008 /* JOURNAL_DEV */) != 0 ||
114 (buf[0x45c] & 0x0004 /* HAS_JOURNAL */) != 0) ? "ext3" : "ext2";
116 /* detect ntfs */
117 else if (buf[510] == 0x55 && buf[511] == 0xAA && /* signature */
118 memcmp(buf + 3, "NTFS ", 8) == 0)
120 return "ntfs";
122 /* detect vfat */
123 else if (buf[510] == 0x55 && buf[511] == 0xAA && /* signature */
124 buf[11] == 0 && buf[12] >= 1 && buf[12] <= 8 /* sector size 512 - 4096 */ &&
125 buf[13] != 0 && (buf[13] & (buf[13] - 1)) == 0) /* sectors per cluster */
127 return "vfat";
130 return NULL;
134 /* Execute a function for each disc partition on the specified controller.
136 * Directory /dev/discs/ looks like this:
137 * disc0 -> ../scsi/host0/bus0/target0/lun0/
138 * disc1 -> ../scsi/host1/bus0/target0/lun0/
139 * disc2 -> ../scsi/host2/bus0/target0/lun0/
140 * disc3 -> ../scsi/host2/bus0/target0/lun1/
142 * Scsi host 2 supports multiple drives.
143 * Scsi host 0 & 1 support one drive.
145 * For attached drives, like this. If not attached, there is no "part#" item.
146 * Here, only one drive, with 2 partitions, is plugged in.
147 * /dev/discs/disc0/disc
148 * /dev/discs/disc0/part1
149 * /dev/discs/disc0/part2
150 * /dev/discs/disc1/disc
151 * /dev/discs/disc2/disc
153 * Which is the same as:
154 * /dev/scsi/host0/bus0/target0/lun0/disc
155 * /dev/scsi/host0/bus0/target0/lun0/part1
156 * /dev/scsi/host0/bus0/target0/lun0/part2
157 * /dev/scsi/host1/bus0/target0/lun0/disc
158 * /dev/scsi/host2/bus0/target0/lun0/disc
159 * /dev/scsi/host2/bus0/target0/lun1/disc
161 * Implementation notes:
162 * Various mucking about with a disc that just got plugged in or unplugged
163 * will make the scsi subsystem try a re-validate, and read the partition table of the disc.
164 * This will make sure the partitions show up.
166 * It appears to try to do the revalidate and re-read & update the partition
167 * information when this code does the "readdir of /dev/discs/disc0/?". If the
168 * disc has any mounted partitions the revalidate will be rejected. So the
169 * current partition info will remain. On an unplug event, when it is doing the
170 * readdir's, it will try to do the revalidate as we are doing the readdir's.
171 * But luckily they'll be rejected, otherwise the later partitions will disappear as
172 * soon as we get the first one.
173 * But be very careful! If something goes not exactly right, the partition entries
174 * will disappear before we've had a chance to unmount from them.
176 * To avoid this automatic revalidation, we go through /proc/partitions looking for the partitions
177 * that /dev/discs point to. That will avoid the implicit revalidate attempt.
178 * Which means that we had better do it ourselves. An ioctl BLKRRPART does just that.
181 * If host < 0, do all hosts. If >= 0, it is the host number to do.
182 * When_to_update, flags:
183 * 0x01 = before reading partition info
184 * 0x02 = after reading partition info
188 /* So as not to include linux/fs.h, let's explicitly do this here. */
189 #ifndef BLKRRPART
190 #define BLKRRPART _IO(0x12,95) /* re-read partition table */
191 #endif
193 int exec_for_host(int host, int when_to_update, uint flags, host_exec func)
195 DIR *usb_dev_disc;
196 char bfr[128]; /* Will be: /dev/discs/disc# */
197 char link[256]; /* Will be: ../scsi/host#/bus0/target0/lun# that bfr links to. */
198 /* When calling the func, will be: /dev/discs/disc#/part# */
199 char bfr2[128]; /* Will be: /dev/discs/disc#/disc for the BLKRRPART. */
200 int fd;
201 char *cp;
202 int len;
203 int host_no; /* SCSI controller/host # */
204 int disc_num; /* Disc # */
205 int part_num; /* Parition # */
206 struct dirent *dp;
207 FILE *prt_fp;
208 char *mp; /* Ptr to after any leading ../ path */
209 int siz;
210 char line[256];
211 int result = 0;
213 flags |= EFH_1ST_HOST;
214 if ((usb_dev_disc = opendir(DEV_DISCS_ROOT))) {
215 while ((dp = readdir(usb_dev_disc))) {
216 sprintf(bfr, "%s/%s", DEV_DISCS_ROOT, dp->d_name);
217 if (strncmp(dp->d_name, "disc", 4) != 0)
218 continue;
220 disc_num = atoi(dp->d_name + 4);
221 len = readlink(bfr, link, sizeof(link) - 1);
222 if (len < 0)
223 continue;
225 link[len] = 0;
226 cp = strstr(link, "/scsi/host");
227 if (!cp)
228 continue;
230 host_no = atoi(cp + 10);
231 if (host >= 0 && host_no != host)
232 continue;
234 /* We have found a disc that is on this controller.
235 * Loop thru all the partitions on this disc.
236 * The new way, reading thru /proc/partitions.
238 mp = link;
239 if ((cp = strstr(link, "../")) != NULL)
240 mp = cp + 3;
241 siz = strlen(mp);
243 if (when_to_update & 0x01) {
244 sprintf(bfr2, "%s/disc", bfr); /* Prepare for BLKRRPART */
245 if ((fd = open(bfr2, O_RDONLY | O_NONBLOCK)) >= 0) {
246 ioctl(fd, BLKRRPART);
247 close(fd);
251 flags |= EFH_1ST_DISC;
252 if (func && (prt_fp = fopen("/proc/partitions", "r"))) {
253 while (fgets(line, sizeof(line) - 2, prt_fp)) {
254 if (sscanf(line, " %*s %*s %*s %s", bfr2) == 1) {
255 if ((cp = strstr(bfr2, "/part")) && strncmp(bfr2, mp, siz) == 0) {
256 part_num = atoi(cp + 5);
257 sprintf(line, "%s/part%d", bfr, part_num);
258 result = (*func)(line, host_no, disc_num, part_num, flags) || result;
259 flags &= ~(EFH_1ST_HOST | EFH_1ST_DISC);
263 fclose(prt_fp);
266 if (when_to_update & 0x02) {
267 sprintf(bfr2, "%s/disc", bfr); /* Prepare for BLKRRPART */
268 if ((fd = open(bfr2, O_RDONLY | O_NONBLOCK)) >= 0) {
269 ioctl(fd, BLKRRPART);
270 close(fd);
274 closedir(usb_dev_disc);
276 return result;
279 /* Concept taken from the e2fsprogs/ismounted.c.
280 * Find wherever 'file' (actually: device) is mounted.
281 * Either the exact same device-name, or another device-name.
282 * The latter is detected by comparing the rdev or dev&inode.
283 * So aliasing won't fool us---we'll still find if it's mounted.
284 * Return its mnt entry.
285 * In particular, the caller would look at the mnt->mountpoint.
287 * Find the matching devname(s) in mounts or swaps.
288 * If func is supplied, call it for each match. If not, return mnt on the first match.
291 static inline int is_same_device(char *fsname, dev_t file_rdev, dev_t file_dev, ino_t file_ino)
293 struct stat st_buf;
295 if (stat(fsname, &st_buf) == 0) {
296 if (S_ISBLK(st_buf.st_mode)) {
297 if (file_rdev && (file_rdev == st_buf.st_rdev))
298 return 1;
300 else {
301 if (file_dev && ((file_dev == st_buf.st_dev) &&
302 (file_ino == st_buf.st_ino)))
303 return 1;
304 /* Check for [swap]file being on the device. */
305 if (file_dev == 0 && file_ino == 0 && file_rdev == st_buf.st_dev)
306 return 1;
309 return 0;
313 struct mntent *findmntents(char *file, int swp, int (*func)(struct mntent *mnt, uint flags), uint flags)
315 struct mntent *mnt;
316 struct stat st_buf;
317 dev_t file_dev=0, file_rdev=0;
318 ino_t file_ino=0;
319 FILE *f;
321 if ((f = setmntent(swp ? "/proc/swaps": "/proc/mounts", "r")) == NULL)
322 return NULL;
324 if (stat(file, &st_buf) == 0) {
325 if (S_ISBLK(st_buf.st_mode)) {
326 file_rdev = st_buf.st_rdev;
328 else {
329 file_dev = st_buf.st_dev;
330 file_ino = st_buf.st_ino;
333 while ((mnt = getmntent(f)) != NULL) {
334 if (strcmp(file, mnt->mnt_fsname) == 0 ||
335 is_same_device(mnt->mnt_fsname, file_rdev , file_dev, file_ino)) {
336 if (func == NULL)
337 break;
338 (*func)(mnt, flags);
342 endmntent(f);
343 return mnt;
347 //#define SAME_AS_KERNEL
348 /* Simulate a hotplug event, as if a USB storage device
349 * got plugged or unplugged.
350 * Either use a hardcoded program name, or the same
351 * hotplug program that the kernel uses for a real event.
353 void add_remove_usbhost(char *host, int add)
355 setenv("ACTION", add ? "add" : "remove", 1);
356 setenv("SCSI_HOST", host, 1);
357 setenv("PRODUCT", host, 1);
358 setenv("INTERFACE", "TOMATO/0", 1);
359 #ifdef SAME_AS_KERNEL
360 char pgm[256] = "/sbin/hotplug usb";
361 char *p;
362 int fd = open("/proc/sys/kernel/hotplug", O_RDONLY);
363 if (fd) {
364 if (read(fd, pgm, sizeof(pgm) - 5) >= 0) {
365 if ((p = strchr(pgm, '\n')) != NULL)
366 *p = 0;
367 strcat(pgm, " usb");
369 close(fd);
371 system(pgm);
372 #else
373 // don't use value from /proc/sys/kernel/hotplug
374 // since it may be overriden by a user.
375 system("/sbin/hotplug usb");
376 #endif
377 unsetenv("INTERFACE");
378 unsetenv("PRODUCT");
379 unsetenv("SCSI_HOST");
380 unsetenv("ACTION");
384 /****************************************************/
385 /* Use busybox routines to get labels for fat & ext */
386 /* Probe for label the same way that mount does. */
387 /****************************************************/
389 #define VOLUME_ID_LABEL_SIZE 64
390 #define VOLUME_ID_UUID_SIZE 36
391 #define SB_BUFFER_SIZE 0x11000
393 struct volume_id {
394 int fd;
395 int error;
396 size_t sbbuf_len;
397 size_t seekbuf_len;
398 uint8_t *sbbuf;
399 uint8_t *seekbuf;
400 uint64_t seekbuf_off;
401 char label[VOLUME_ID_LABEL_SIZE+1];
402 char uuid[VOLUME_ID_UUID_SIZE+1];
405 extern void volume_id_set_uuid();
406 extern void *volume_id_get_buffer();
407 extern void volume_id_free_buffer();
408 extern int volume_id_probe_ext();
409 extern int volume_id_probe_vfat();
410 extern int volume_id_probe_ntfs();
411 extern int volume_id_probe_linux_swap();
413 /* Put the label in *label and uuid in *uuid.
414 * Return 0 if no label/uuid found, NZ if there is a label or uuid.
416 int find_label_or_uuid(char *dev_name, char *label, char *uuid)
418 struct volume_id id;
420 memset(&id, 0x00, sizeof(id));
421 if (label) *label = 0;
422 if (uuid) *uuid = 0;
423 if ((id.fd = open(dev_name, O_RDONLY)) < 0)
424 return 0;
426 if (volume_id_probe_vfat(&id) == 0 || id.error)
427 goto ret;
429 volume_id_get_buffer(&id, 0, SB_BUFFER_SIZE);
431 if (volume_id_probe_ext(&id) == 0 || id.error)
432 goto ret;
433 if (volume_id_probe_linux_swap(&id) == 0 || id.error)
434 goto ret;
435 if (volume_id_probe_ntfs(&id) == 0 || id.error)
436 goto ret;
437 ret:
438 volume_id_free_buffer(&id);
439 if (label && (*id.label != 0))
440 strcpy(label, id.label);
441 if (uuid && (*id.uuid != 0))
442 strcpy(uuid, id.uuid);
443 close(id.fd);
444 return (label && *label != 0) || (uuid && *uuid != 0);
447 void *xmalloc(size_t siz)
449 return (malloc(siz));
452 void *xrealloc(void *old, size_t size)
454 return realloc(old, size);
457 ssize_t full_read(int fd, void *buf, size_t len)
459 return read(fd, buf, len);