2
0

rootdisk.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU Lesser General Public License version 2.1
  6. * as published by the Free Software Foundation
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. */
  13. #include "common.h"
  14. #include <linux/loop.h>
  15. #define ROOTDEV_OVERLAY_ALIGN (64ULL * 1024ULL)
  16. struct squashfs_super_block {
  17. uint32_t s_magic;
  18. uint32_t pad0[9];
  19. uint64_t bytes_used;
  20. };
  21. struct rootdev_volume {
  22. struct volume v;
  23. uint64_t offset;
  24. char loop_name[32];
  25. };
  26. static const char *rootdev;
  27. static struct driver rootdisk_driver;
  28. static char *get_blockdev(dev_t dev)
  29. {
  30. const char *dirname = "/dev";
  31. DIR *dir = opendir(dirname);
  32. struct dirent *d;
  33. struct stat st;
  34. static char buf[256];
  35. char *ret = NULL;
  36. if (!dir)
  37. return ret;
  38. while ((d = readdir(dir)) != NULL) {
  39. snprintf(buf, sizeof(buf), "%s/%s", dirname, d->d_name);
  40. if (lstat(buf, &st) != 0)
  41. continue;
  42. if (!S_ISBLK(st.st_mode))
  43. continue;
  44. if (st.st_rdev != dev)
  45. continue;
  46. ret = buf;
  47. break;
  48. }
  49. closedir(dir);
  50. return ret;
  51. }
  52. static char *get_rootdev(const char *dir)
  53. {
  54. struct stat st;
  55. if (stat(dir, &st))
  56. return NULL;
  57. return get_blockdev(S_ISBLK(st.st_mode) ? st.st_rdev : st.st_dev);
  58. }
  59. static int get_squashfs(struct squashfs_super_block *sb)
  60. {
  61. FILE *f;
  62. int len;
  63. f = fopen(rootdev, "r");
  64. if (!f)
  65. return -1;
  66. len = fread(sb, sizeof(*sb), 1, f);
  67. fclose(f);
  68. if (len != 1)
  69. return -1;
  70. return 0;
  71. }
  72. static struct volume *rootdisk_volume_find(char *name)
  73. {
  74. struct squashfs_super_block sb;
  75. struct rootdev_volume *p;
  76. if (strcmp(name, "rootfs_data") != 0)
  77. return NULL;
  78. if (!rootdev)
  79. rootdev = get_rootdev("/");
  80. if (!rootdev)
  81. rootdev = get_rootdev("/rom");
  82. if (!rootdev)
  83. return NULL;
  84. if (get_squashfs(&sb))
  85. return NULL;
  86. if (memcmp(&sb.s_magic, "hsqs", sizeof(sb.s_magic)) != 0)
  87. return NULL;
  88. p = calloc(1, sizeof(*p));
  89. p->v.drv = &rootdisk_driver;
  90. p->v.name = "rootfs_data";
  91. p->offset = le64_to_cpu(sb.bytes_used);
  92. p->offset = ((p->offset + (ROOTDEV_OVERLAY_ALIGN - 1)) &
  93. ~(ROOTDEV_OVERLAY_ALIGN - 1));
  94. return &p->v;
  95. }
  96. static int rootdisk_volume_identify(struct volume *v)
  97. {
  98. struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
  99. FILE *f;
  100. int ret = FS_NONE;
  101. f = fopen(rootdev, "r");
  102. if (!f)
  103. return ret;
  104. ret = block_file_identify(f, p->offset);
  105. fclose(f);
  106. return ret;
  107. }
  108. static int rootdisk_create_loop(struct rootdev_volume *p)
  109. {
  110. struct loop_info64 info;
  111. int ret = -1;
  112. int fd = -1;
  113. int i, ffd;
  114. ffd = open(rootdev, O_RDWR);
  115. if (ffd < 0)
  116. return -1;
  117. for (i = 0; i < 8; i++) {
  118. snprintf(p->loop_name, sizeof(p->loop_name), "/dev/loop%d",
  119. i);
  120. if (fd >= 0)
  121. close(fd);
  122. fd = open(p->loop_name, O_RDWR);
  123. if (fd < 0)
  124. continue;
  125. if (ioctl(fd, LOOP_GET_STATUS64, &info) == 0) {
  126. if (strcmp((char *) info.lo_file_name, rootdev) != 0)
  127. continue;
  128. if (info.lo_offset != p->offset)
  129. continue;
  130. ret = 0;
  131. break;
  132. }
  133. if (errno != ENXIO)
  134. continue;
  135. if (ioctl(fd, LOOP_SET_FD, ffd) != 0)
  136. continue;
  137. memset(&info, 0, sizeof(info));
  138. snprintf((char *) info.lo_file_name, sizeof(info.lo_file_name), "%s",
  139. rootdev);
  140. info.lo_offset = p->offset;
  141. info.lo_flags |= LO_FLAGS_AUTOCLEAR;
  142. if (ioctl(fd, LOOP_SET_STATUS64, &info) != 0) {
  143. ioctl(fd, LOOP_CLR_FD, 0);
  144. continue;
  145. }
  146. /*
  147. * Don't close fd. Leave it open until this process exits, to avoid
  148. * the autoclear from happening too soon.
  149. */
  150. fd = -1;
  151. ret = 0;
  152. break;
  153. }
  154. if (fd >= 0)
  155. close(fd);
  156. close(ffd);
  157. if (ret)
  158. p->loop_name[0] = 0;
  159. return ret;
  160. }
  161. static int rootdisk_volume_init(struct volume *v)
  162. {
  163. struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
  164. if (!p->loop_name[0] && rootdisk_create_loop(p) != 0) {
  165. ULOG_ERR("unable to create loop device\n");
  166. return -1;
  167. }
  168. v->type = BLOCKDEV;
  169. v->blk = p->loop_name;
  170. return block_volume_format(v, p->offset, rootdev);
  171. }
  172. static struct driver rootdisk_driver = {
  173. .name = "rootdisk",
  174. .find = rootdisk_volume_find,
  175. .init = rootdisk_volume_init,
  176. .identify = rootdisk_volume_identify,
  177. };
  178. DRIVER(rootdisk_driver);