16 #include <sys/ioctl.h>
19 #include <sys/socket.h>
20 #include <arpa/inet.h>
21 #include <sys/sysinfo.h>
22 #include <sys/types.h>
32 /* Serialize using fcntl() calls
35 int file_lock(char *tag
)
42 sprintf(fn
, "/var/lock/%s.lock", tag
);
43 if ((lockfd
= open(fn
, O_CREAT
| O_RDWR
, 0666)) < 0)
47 if (read(lockfd
, &lockpid
, sizeof(pid_t
))) {
48 // check if we already hold a lock
50 // don't close the file here as that will release all locks
55 memset(&lock
, 0, sizeof(lock
));
56 lock
.l_type
= F_WRLCK
;
59 if (fcntl(lockfd
, F_SETLKW
, &lock
) < 0) {
64 lseek(lockfd
, 0, SEEK_SET
);
65 write(lockfd
, &pid
, sizeof(pid_t
));
68 // No proper error processing
69 syslog(LOG_DEBUG
, "Error %d locking %s, proceeding anyway", errno
, fn
);
73 void file_unlock(int lockfd
)
81 char *detect_fs_type(char *device
)
84 unsigned char buf
[4096];
86 if ((fd
= open(device
, O_RDONLY
)) < 0)
89 if (read(fd
, buf
, sizeof(buf
)) != sizeof(buf
))
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 */
105 else if (memcmp(buf
+ 4086, "SWAPSPACE2", 10) == 0 ||
106 memcmp(buf
+ 4086, "SWAP-SPACE", 10) == 0)
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";
117 else if (buf
[510] == 0x55 && buf
[511] == 0xAA && /* signature */
118 memcmp(buf
+ 3, "NTFS ", 8) == 0)
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 */
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. */
190 #define BLKRRPART _IO(0x12,95) /* re-read partition table */
193 int exec_for_host(int host
, int when_to_update
, uint flags
, host_exec func
)
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. */
203 int host_no
; /* SCSI controller/host # */
204 int disc_num
; /* Disc # */
205 int part_num
; /* Parition # */
208 char *mp
; /* Ptr to after any leading ../ path */
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)
220 disc_num
= atoi(dp
->d_name
+ 4);
221 len
= readlink(bfr
, link
, sizeof(link
) - 1);
226 cp
= strstr(link
, "/scsi/host");
230 host_no
= atoi(cp
+ 10);
231 if (host
>= 0 && host_no
!= host
)
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.
239 if ((cp
= strstr(link
, "../")) != NULL
)
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
);
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
);
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
);
274 closedir(usb_dev_disc
);
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
)
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
))
301 if (file_dev
&& ((file_dev
== st_buf
.st_dev
) &&
302 (file_ino
== st_buf
.st_ino
)))
304 /* Check for [swap]file being on the device. */
305 if (file_dev
== 0 && file_ino
== 0 && file_rdev
== st_buf
.st_dev
)
313 struct mntent
*findmntents(char *file
, int swp
, int (*func
)(struct mntent
*mnt
, uint flags
), uint flags
)
317 dev_t file_dev
=0, file_rdev
=0;
321 if ((f
= setmntent(swp
? "/proc/swaps": "/proc/mounts", "r")) == NULL
)
324 if (stat(file
, &st_buf
) == 0) {
325 if (S_ISBLK(st_buf
.st_mode
)) {
326 file_rdev
= st_buf
.st_rdev
;
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
)) {
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";
362 int fd
= open("/proc/sys/kernel/hotplug", O_RDONLY
);
364 if (read(fd
, pgm
, sizeof(pgm
) - 5) >= 0) {
365 if ((p
= strchr(pgm
, '\n')) != NULL
)
373 // don't use value from /proc/sys/kernel/hotplug
374 // since it may be overriden by a user.
375 system("/sbin/hotplug usb");
377 unsetenv("INTERFACE");
379 unsetenv("SCSI_HOST");
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
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
)
420 memset(&id
, 0x00, sizeof(id
));
421 if (label
) *label
= 0;
423 if ((id
.fd
= open(dev_name
, O_RDONLY
)) < 0)
426 if (volume_id_probe_vfat(&id
) == 0 || id
.error
)
429 volume_id_get_buffer(&id
, 0, SB_BUFFER_SIZE
);
431 if (volume_id_probe_ext(&id
) == 0 || id
.error
)
433 if (volume_id_probe_linux_swap(&id
) == 0 || id
.error
)
435 if (volume_id_probe_ntfs(&id
) == 0 || id
.error
)
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
);
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
);