make mountd also work for mmc partitions
[project/mountd.git] / mount.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <fcntl.h>
8 #include <sys/ioctl.h>
9 #include <linux/hdreg.h>
10 #include <scsi/sg.h>
11 #include <dirent.h>
12 #include <sys/wait.h>
13 #include <sys/inotify.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <glob.h>
17 #include <libgen.h>
18 #include <poll.h>
19 #include <dirent.h>
20 #include <syslog.h>
21
22 #include "include/log.h"
23 #include "include/list.h"
24 #include "include/sys.h"
25 #include "include/signal.h"
26 #include "include/timer.h"
27 #include "include/autofs.h"
28 #include "include/ucix.h"
29 #include "include/fs.h"
30 #include "include/mount.h"
31
32 int mount_new(char *path, char *dev);
33
34 struct list_head mounts;
35
36 struct mount {
37 struct list_head list;
38 char name[64];
39 char dev[64];
40 char serial[64];
41 char vendor[64];
42 char model[64];
43 char rev[64];
44 int mounted;
45 int ignore;
46 char size[64];
47 char sector_size[64];
48 int fs;
49 };
50
51 char *fs_names[] = {
52 "",
53 "",
54 "MBR",
55 "EXT2",
56 "EXT3",
57 "FAT",
58 "HFSPLUS",
59 "",
60 "NTFS",
61 "",
62 "EXT4"
63 };
64
65 #define MAX_MOUNTED 32
66 #define MAX_MOUNT_NAME 32
67
68 char mounted[MAX_MOUNTED][3][MAX_MOUNT_NAME];
69 int mounted_count = 0;
70 extern char uci_path[32];
71
72 static void mount_dump_uci_state(void)
73 {
74 struct uci_context *ctx;
75 struct list_head *p;
76 char mountd[] = {"mountd"};
77 char type[] = {"mountd_disc"};
78 int mounted = 0;
79 unsigned long long int size = 0;
80 unlink("/var/state/mountd");
81 ctx = ucix_init("mountd");
82 uci_set_savedir(ctx, "/var/state/");
83 ucix_add_option_int(ctx, mountd, mountd, "count", list_count(&mounts));
84 list_for_each(p, &mounts)
85 {
86 struct mount *q = container_of(p, struct mount, list);
87 char t[64];
88 if(q->fs == EXTENDED)
89 continue;
90 ucix_add_section(ctx, mountd, q->serial, type);
91 strcpy(t, q->dev);
92 t[3] = '\0';
93 ucix_add_option(ctx, mountd, q->serial, "disc", t);
94 ucix_add_option(ctx, mountd, q->serial, "sector_size", q->sector_size);
95 snprintf(t, 64, "part%dmounted", atoi(&q->dev[3]));
96 ucix_add_option(ctx, mountd, q->serial, t, (q->mounted)?("1"):("0"));
97 ucix_add_option(ctx, mountd, q->serial, "vendor", q->vendor);
98 ucix_add_option(ctx, mountd, q->serial, "model", q->model);
99 ucix_add_option(ctx, mountd, q->serial, "rev", q->rev);
100 snprintf(t, 64, "size%d", atoi(&q->dev[3]));
101 ucix_add_option(ctx, mountd, q->serial, t, q->size);
102 if(q->fs > MBR && q->fs <= EXT4)
103 {
104 snprintf(t, 64, "fs%d", atoi(&q->dev[3]));
105 ucix_add_option(ctx, mountd, q->serial, t, fs_names[q->fs]);
106 }
107 if(q->mounted)
108 mounted++;
109 if((!q->ignore) && q->size && q->sector_size)
110 size = size + (((unsigned long long int)atoi(q->size)) * ((unsigned long long int)atoi(q->sector_size)));
111 }
112 ucix_add_option_int(ctx, mountd, mountd, "mounted", mounted);
113 ucix_add_option_int(ctx, mountd, mountd, "total", size);
114 system_printf("echo -n %llu > /tmp/run/mountd_size", size);
115 ucix_save_state(ctx, "mountd");
116 ucix_cleanup(ctx);
117 }
118
119 static struct mount* mount_find(char *name, char *dev)
120 {
121 struct list_head *p;
122 list_for_each(p, &mounts)
123 {
124 struct mount *q = container_of(p, struct mount, list);
125 if(name)
126 if(!strcmp(q->name, name))
127 return q;
128 if(dev)
129 if(!strcmp(q->dev, dev))
130 return q;
131 }
132 return 0;
133 }
134
135 static void mount_add_list(char *name, char *dev, char *serial,
136 char *vendor, char *model, char *rev, int ignore, char *size, char *sector_size, int fs)
137 {
138 struct mount *mount;
139 char tmp[64], tmp2[64];
140 if(fs <= MBR || fs > EXT4)
141 return;
142 mount = malloc(sizeof(struct mount));
143 INIT_LIST_HEAD(&mount->list);
144 strncpy(mount->vendor, vendor, 64);
145 strncpy(mount->model, model, 64);
146 strncpy(mount->rev, rev, 64);
147 strncpy(mount->name, name, 64);
148 strncpy(mount->dev, dev, 64);
149 strncpy(mount->serial, serial, 64);
150 strncpy(mount->size, size, 64);
151 strncpy(mount->sector_size, sector_size, 64);
152 mount->ignore = ignore;
153 mount->mounted = 0;
154 mount->fs = fs;
155 list_add(&mount->list, &mounts);
156 if((!mount->ignore) && (mount->fs > MBR) && (mount->fs <= EXT4))
157 {
158 log_printf("new mount : %s -> %s (%s)\n", name, dev, fs_names[mount->fs]);
159 snprintf(tmp, 64, "%s%s", uci_path, name);
160 snprintf(tmp2, 64, "/tmp/run/mountd/%s", dev);
161 symlink(tmp2, tmp);
162 mount_new("/tmp/run/mountd/", dev);
163 }
164 }
165
166 static int mount_check_disc(char *disc)
167 {
168 FILE *fp = fopen("/proc/mounts", "r");
169 char tmp[256];
170 int avail = -1;
171 if(!fp)
172 {
173 log_printf("error reading /proc/mounts");
174 fclose(fp);
175 return avail;
176 }
177 while((fgets(tmp, 256, fp) > 0) && (avail == -1))
178 {
179 char *t;
180 char tmp2[32];
181 t = strstr(tmp, " ");
182 if(t)
183 {
184 int l;
185 *t = '\0';
186 l = snprintf(tmp2, 31, "/dev/%s", disc);
187
188 if(!strncmp(tmp, tmp2, l))
189 avail = 0;
190 }
191 }
192 fclose(fp);
193 return avail;
194 }
195
196 static int mount_wait_for_disc(char *disc)
197 {
198 int i = 10;
199 while(i--)
200 {
201 int ret = mount_check_disc(disc);
202 if(!ret)
203 return ret;
204 poll(0, 0, 100);
205 }
206 return -1;
207 }
208
209 int mount_new(char *path, char *dev)
210 {
211 struct mount *mount;
212 char tmp[256];
213 int ret = 1;
214 pid_t pid;
215 mount = mount_find(0, dev);
216 if(!mount)
217 {
218 log_printf("request for invalid path %s%s\n", path, dev);
219 return -1;
220 }
221 if(mount->ignore || mount->mounted || mount->fs == EXTENDED)
222 return -1;
223 snprintf(tmp, 256, "%s%s", path, mount->dev);
224 log_printf("mounting %s\n", tmp);
225 mkdir(tmp, 777);
226
227 pid = autofs_safe_fork();
228 if(!pid)
229 {
230 if(mount->fs == FAT)
231 {
232 log_printf("mount -t vfat -o rw,uid=1000,gid=1000 /dev/%s %s", mount->dev, tmp);
233 ret = system_printf("mount -t vfat -o rw,uid=1000,gid=1000 /dev/%s %s", mount->dev, tmp);
234 }
235 if(mount->fs == EXT4)
236 {
237 log_printf("mount -t ext4 -o rw,defaults /dev/%s %s", mount->dev, tmp);
238 ret = system_printf("mount -t ext4 -o rw,defaults /dev/%s %s", mount->dev, tmp);
239 }
240 if(mount->fs == EXT3)
241 {
242 log_printf("mount -t ext3 -o rw,defaults /dev/%s %s", mount->dev, tmp);
243 ret = system_printf("mount -t ext3 -o rw,defaults /dev/%s %s", mount->dev, tmp);
244 }
245 if(mount->fs == EXT2)
246 {
247 log_printf("mount -t ext2 -o rw,defaults /dev/%s %s", mount->dev, tmp);
248 ret = system_printf("mount -t ext2 -o rw,defaults /dev/%s %s", mount->dev, tmp);
249 }
250 if(mount->fs == HFSPLUS)
251 {
252 log_printf("mount -t hfsplus -o rw,defaults,uid=1000,gid=1000 /dev/%s %s", mount->dev, tmp);
253 ret = system_printf("mount -t hfsplus -o rw,defaults,uid=1000,gid=1000 /dev/%s %s", mount->dev, tmp);
254 }
255 if(mount->fs == NTFS)
256 {
257 log_printf("ntfs-3g /dev/%s %s -o force", mount->dev, tmp);
258 ret = system_printf("ntfs-3g /dev/%s %s -o force", mount->dev, tmp);
259 }
260 exit(WEXITSTATUS(ret));
261 }
262 pid = waitpid(pid, &ret, 0);
263 ret = WEXITSTATUS(ret);
264 log_printf("----------> mount ret = %d\n", ret);
265 if(ret && (ret != 0xff))
266 return -1;
267 if(mount_wait_for_disc(mount->dev) == 0)
268 {
269 mount->mounted = 1;
270 mount_dump_uci_state();
271 } else return -1;
272 return 0;
273 }
274
275 int mount_remove(char *path, char *dev)
276 {
277 struct mount *mount;
278 char tmp[256];
279 int ret;
280 snprintf(tmp, 256, "%s%s", path, dev);
281 log_printf("%s has expired... unmounting\n", tmp);
282 ret = system_printf("/bin/umount %s", tmp);
283 if(ret != 0)
284 return 0;
285 rmdir(tmp);
286 mount = mount_find(0, dev);
287 if(mount)
288 mount->mounted = 0;
289 log_printf("finished unmounting\n");
290 mount_dump_uci_state();
291 return 0;
292 }
293
294 static int dir_sort(const struct dirent **a, const struct dirent **b)
295 {
296 return 0;
297 }
298
299 static int dir_filter(const struct dirent *a)
300 {
301 if(strstr(a->d_name, ":"))
302 return 1;
303 return 0;
304 }
305
306 static char* mount_get_serial(char *dev)
307 {
308 static char tmp[64];
309 static char tmp2[64];
310 int disc;
311 static struct hd_driveid hd;
312 int i;
313 static char *serial;
314 static char disc_id[13];
315 snprintf(tmp, 64, "/dev/%s", dev);
316 disc = open(tmp, O_RDONLY);
317 if(!disc)
318 {
319 log_printf("Trying to open unknown disc\n");
320 return 0;
321 }
322 i = ioctl(disc, HDIO_GET_IDENTITY, &hd);
323 close(disc);
324 if(!i)
325 serial = (char*)hd.serial_no;
326 /* if we failed, it probably a usb storage device */
327 /* there must be a better way for this */
328 if(i)
329 {
330 struct dirent **namelist;
331 int n = scandir("/sys/bus/scsi/devices/", &namelist, dir_filter, dir_sort);
332 if(n > 0)
333 {
334 while(n--)
335 {
336 char *t = strstr(namelist[n]->d_name, ":");
337 if(t)
338 {
339 int id;
340 struct stat buf;
341 char tmp3[64];
342 int ret;
343 *t = 0;
344 id = atoi(namelist[n]->d_name);
345 *t = ':';
346 sprintf(tmp3, "/sys/bus/scsi/devices/%s/block:%s/", namelist[n]->d_name, dev);
347 ret = stat(tmp3, &buf);
348 if(ret)
349 {
350 sprintf(tmp3, "/sys/bus/scsi/devices/%s/block/%s/", namelist[n]->d_name, dev);
351 ret = stat(tmp3, &buf);
352 }
353 if(!ret)
354 {
355 FILE *fp;
356 snprintf(tmp2, 64, "/proc/scsi/usb-storage/%d", id);
357 fp = fopen(tmp2, "r");
358 if(fp)
359 {
360 while(fgets(tmp2, 64, fp) > 0)
361 {
362 serial = strstr(tmp2, "Serial Number:");
363 if(serial)
364 {
365 serial += strlen("Serial Number: ");
366 serial[strlen(serial) - 1] = '\0';
367 i = 0;
368 break;
369 }
370 }
371 fclose(fp);
372 }
373 }
374 }
375 free(namelist[n]);
376 }
377 free(namelist);
378 }
379 }
380 if(i)
381 {
382 log_printf("could not find a serial number for the device %s\n", dev);
383 } else {
384 /* serial string id is cheap, but makes the discs anonymous */
385 unsigned char uniq[6];
386 unsigned int *u = (unsigned int*) uniq;
387 int l = strlen(serial);
388 int i;
389 memset(disc_id, 0, 13);
390 memset(uniq, 0, 6);
391 for(i = 0; i < l; i++)
392 {
393 uniq[i%6] += serial[i];
394 }
395 sprintf(disc_id, "%08X%02X%02X", *u, uniq[4], uniq[5]);
396 //log_printf("Serial number - %s %s\n", serial, disc_id);
397 return disc_id;
398 }
399 sprintf(disc_id, "000000000000");
400 return disc_id;
401 }
402
403 static void mount_dev_add(char *dev)
404 {
405 struct mount *mount = mount_find(0, dev);
406 if(!mount)
407 {
408 char node[64];
409 char name[64];
410 int ignore = 0;
411 char *s;
412 char tmp[64];
413 char tmp2[64];
414 char *p;
415 struct uci_context *ctx;
416 char vendor[64];
417 char model[64];
418 char rev[64];
419 char size[64];
420 char sector_size[64];
421 FILE *fp;
422 int offset = 3;
423
424 strcpy(name, dev);
425 if (!strncmp(name, "mmcblk", 6))
426 offset = 7;
427 name[offset] = '\0';
428 s = mount_get_serial(name);
429 if(!s) {
430 return;
431 }
432 if (!strncmp(name, "mmcblk", 6)) {
433 snprintf(tmp, 64, "part%s", &dev[8]);
434 snprintf(node, 64, "SD-P%s", &dev[8]);
435
436 } else {
437 snprintf(tmp, 64, "part%s", &dev[3]);
438 snprintf(node, 64, "USB-%s", &dev[2]);
439 }
440 if(node[4] >= 'a' && node[4] <= 'z')
441 {
442 node[4] -= 'a';
443 node[4] += 'A';
444 }
445 ctx = ucix_init("mountd");
446 p = ucix_get_option(ctx, "mountd", s, tmp);
447 ucix_cleanup(ctx);
448 if(p)
449 {
450 if(strlen(p) == 1)
451 {
452 if(*p == '0')
453 ignore = 1;
454 } else {
455 snprintf(node, 64, "%s", p);
456 }
457 }
458 strcpy(name, dev);
459 name[3] = '\0';
460 snprintf(tmp, 64, "/sys/class/block/%s/device/model", name);
461 fp = fopen(tmp, "r");
462 if(!fp)
463 {
464 snprintf(tmp, 64, "/sys/block/%s/device/model", name);
465 fp = fopen(tmp, "r");
466 }
467 if(!fp)
468 snprintf(model, 64, "unknown");
469 else {
470 fgets(model, 64, fp);
471 model[strlen(model) - 1] = '\0';;
472 fclose(fp);
473 }
474 snprintf(tmp, 64, "/sys/class/block/%s/device/vendor", name);
475 fp = fopen(tmp, "r");
476 if(!fp)
477 {
478 snprintf(tmp, 64, "/sys/block/%s/device/vendor", name);
479 fp = fopen(tmp, "r");
480 }
481 if(!fp)
482 snprintf(vendor, 64, "unknown");
483 else {
484 fgets(vendor, 64, fp);
485 vendor[strlen(vendor) - 1] = '\0';
486 fclose(fp);
487 }
488 snprintf(tmp, 64, "/sys/class/block/%s/device/rev", name);
489 fp = fopen(tmp, "r");
490 if(!fp)
491 {
492 snprintf(tmp, 64, "/sys/block/%s/device/rev", name);
493 fp = fopen(tmp, "r");
494 }
495 if(!fp)
496 snprintf(rev, 64, "unknown");
497 else {
498 fgets(rev, 64, fp);
499 rev[strlen(rev) - 1] = '\0';
500 fclose(fp);
501 }
502 snprintf(tmp, 64, "/sys/class/block/%s/size", dev);
503 fp = fopen(tmp, "r");
504 if(!fp)
505 {
506 snprintf(tmp, 64, "/sys/block/%s/%s/size", name, dev);
507 fp = fopen(tmp, "r");
508 }
509 if(!fp)
510 snprintf(size, 64, "unknown");
511 else {
512 fgets(size, 64, fp);
513 size[strlen(size) - 1] = '\0';
514 fclose(fp);
515 }
516 strcpy(tmp2, dev);
517 tmp2[3] = '\0';
518 snprintf(tmp, 64, "/sys/block/%s/queue/hw_sector_size", tmp2);
519 fp = fopen(tmp, "r");
520 if(!fp)
521 snprintf(sector_size, 64, "unknown");
522 else {
523 fgets(sector_size, 64, fp);
524 sector_size[strlen(sector_size) - 1] = '\0';
525 fclose(fp);
526 }
527 snprintf(tmp, 64, "/dev/%s", dev);
528 mount_add_list(node, dev, s, vendor, model, rev, ignore, size, sector_size, detect_fs(tmp));
529 mount_dump_uci_state();
530 }
531 }
532
533 static void mount_dev_del(char *dev)
534 {
535 struct mount *mount = mount_find(0, dev);
536 char tmp[256];
537 if(mount)
538 {
539 if(mount->mounted)
540 {
541 snprintf(tmp, 256, "%s%s", "/tmp/run/mountd/", mount->name);
542 log_printf("%s has dissappeared ... unmounting\n", tmp);
543 snprintf(tmp, 256, "%s%s", "/tmp/run/mountd/", mount->dev);
544 system_printf("/bin/umount %s", tmp);
545 rmdir(tmp);
546 snprintf(tmp, 64, "%s%s", uci_path, mount->name);
547 unlink(tmp);
548 mount_dump_uci_state();
549 }
550 }
551 }
552
553 void mount_dump_list(void)
554 {
555 struct list_head *p;
556 list_for_each(p, &mounts)
557 {
558 struct mount *q = container_of(p, struct mount, list);
559 log_printf("* %s %s %d\n", q->name, q->dev, q->mounted);
560 }
561 }
562
563 char* is_mounted(char *block, char *path)
564 {
565 int i;
566 for(i = 0; i < mounted_count; i++)
567 {
568 if(block)
569 if(!strncmp(&mounted[i][0][0], block, strlen(&mounted[i][0][0])))
570 return &mounted[i][0][0];
571 if(path)
572 if(!strncmp(&mounted[i][1][1], &path[1], strlen(&mounted[i][1][0])))
573 return &mounted[i][0][0];
574 }
575 return 0;
576 }
577
578 static void mount_check_mount_list(void)
579 {
580 FILE *fp = fopen("/proc/mounts", "r");
581 char tmp[256];
582
583 if(!fp)
584 {
585 log_printf("error reading /proc/mounts");
586 fclose(fp);
587 return;
588 }
589 mounted_count = 0;
590 while(fgets(tmp, 256, fp) > 0)
591 {
592 char *t, *t2;
593 t = strstr(tmp, " ");
594 if(t)
595 {
596 *t = '\0';
597 t++;
598 } else t = tmp;
599 strncpy(&mounted[mounted_count][0][0], tmp, MAX_MOUNT_NAME);
600 t2 = strstr(t, " ");
601 if(t2)
602 {
603 *t2 = '\0';
604 t2++;
605 } else t2 = t;
606 strncpy(&mounted[mounted_count][1][0], t, MAX_MOUNT_NAME);
607 t = strstr(t2, " ");
608 if(t)
609 {
610 *t = '\0';
611 t++;
612 } else t = tmp;
613 strncpy(&mounted[mounted_count][2][0], t2, MAX_MOUNT_NAME);
614 /* printf("%s %s %s\n",
615 mounted[mounted_count][0],
616 mounted[mounted_count][1],
617 mounted[mounted_count][2]);*/
618 if(mounted_count < MAX_MOUNTED - 1)
619 mounted_count++;
620 else
621 log_printf("found more than %d mounts \n", MAX_MOUNTED);
622 }
623 fclose(fp);
624 }
625
626 /* FIXME: we need more intelligence here */
627 static int dir_filter2(const struct dirent *a)
628 {
629 if(!strncmp(a->d_name, "mmcblk", 6) || !strncmp(a->d_name, "sd", 2))
630 return 1;
631 return 0;
632 }
633 #define MAX_BLOCK 64
634 char block[MAX_BLOCK][MAX_BLOCK];
635 int blk_cnt = 0;
636
637 static int check_block(char *b)
638 {
639 int i;
640 for(i = 0; i < blk_cnt; i++)
641 {
642 if(!strcmp(block[i], b))
643 return 1;
644 }
645 return 0;
646 }
647
648 static void mount_enum_drives(void)
649 {
650 struct dirent **namelist, **namelist2;
651 int i, n = scandir("/sys/block/", &namelist, dir_filter2, dir_sort);
652 struct list_head *p;
653 blk_cnt = 0;
654 if(n > 0)
655 {
656 while(n--)
657 {
658 if(blk_cnt < MAX_BLOCK)
659 {
660 int m;
661 char tmp[64];
662 snprintf(tmp, 64, "/sys/block/%s/", namelist[n]->d_name);
663 m = scandir(tmp, &namelist2, dir_filter2, dir_sort);
664 while(m--)
665 {
666 strncpy(&block[blk_cnt][0], namelist2[m]->d_name, MAX_BLOCK);
667 blk_cnt++;
668 free(namelist2[m]);
669 }
670 free(namelist2);
671 }
672 free(namelist[n]);
673 }
674 free(namelist);
675 }
676 p = mounts.next;
677 while(p != &mounts)
678 {
679 struct mount *q = container_of(p, struct mount, list);
680 char tmp[64];
681 struct uci_context *ctx;
682 int del = 0;
683 char *t;
684 snprintf(tmp, 64, "part%s", &q->dev[3]);
685 ctx = ucix_init("mountd");
686 t = ucix_get_option(ctx, "mountd", q->serial, tmp);
687 ucix_cleanup(ctx);
688 if(t && !q->mounted)
689 {
690 if(!strcmp(t, "0"))
691 {
692 if(!q->ignore)
693 del = 1;
694 } else if(!strcmp(t, "1"))
695 {
696 if(strncmp(q->name, "Disc-", 5))
697 del = 1;
698 } else if(strcmp(q->name, t))
699 {
700 del = 1;
701 }
702 }
703 if(!check_block(q->dev)||del)
704 {
705 mount_dev_del(q->dev);
706 p->prev->next = p->next;
707 p->next->prev = p->prev;
708 p = p->next;
709 log_printf("removing %s\n", q->dev);
710 snprintf(tmp, 64, "%s%s", uci_path, q->name);
711 unlink(tmp);
712 system_printf("/etc/mountd/event remove %s %s", q->dev, q->name);
713 free(q);
714 mount_dump_uci_state();
715 system_printf("/etc/fonstated/ReloadSamba");
716 } else p = p->next;
717 }
718
719 for(i = 0; i < blk_cnt; i++)
720 mount_dev_add(block[i]);
721 }
722
723 static void mount_check_enum(void)
724 {
725 waitpid(-1, 0, WNOHANG);
726 mount_enum_drives();
727 }
728
729 void mount_init(void)
730 {
731 INIT_LIST_HEAD(&mounts);
732 timer_add(mount_check_mount_list, 2);
733 timer_add(mount_check_enum, 1);
734 mount_check_mount_list();
735 }