You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

376 lines
9.3 KiB

  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. // UBI backend for uvol
  3. // (c) 2022 Daniel Golle <daniel@makrotopia.org>
  4. //
  5. // This plugin uses UBI on NAND flash as a storage backend for uvol.
  6. function read_file(file) {
  7. let fp = fs.open(file);
  8. if (!fp)
  9. return null;
  10. let var = rtrim(fp.read("all"));
  11. fp.close();
  12. return var;
  13. }
  14. function mkdtemp() {
  15. math = require("math");
  16. let r1 = math.rand();
  17. let r2 = math.rand();
  18. let randbytes = chr((r1 >> 24) & 0xff, (r1 >> 16) & 0xff, (r1 >> 8) & 0xff, r1 & 0xff,
  19. (r2 >> 24) & 0xff, (r2 >> 16) & 0xff, (r2 >> 8) & 0xff, r2 & 0xff);
  20. let randstr = replace(b64enc(randbytes), /[\/-_.=]/g, "");
  21. let dirname = sprintf("/tmp/uvol-%s", randstr);
  22. fs.mkdir(dirname, 0700);
  23. return dirname;
  24. }
  25. function ubi_get_dev(vol_name) {
  26. let wcstring = sprintf("uvol-[rw][owpd]-%s", vol_name);
  27. for (vol_dir in fs.glob(sprintf("/sys/devices/virtual/ubi/%s/%s_*", ubidev, ubidev))) {
  28. let vol_ubiname = read_file(sprintf("%s/name", vol_dir));
  29. if (wildcard(vol_ubiname, wcstring))
  30. return fs.basename(vol_dir);
  31. }
  32. return null;
  33. }
  34. function vol_get_mode(vol_dev, mode) {
  35. let vol_name = read_file(sprintf("/sys/devices/virtual/ubi/%s/%s/name", ubidev, vol_dev));
  36. return substr(vol_name, 5, 2);
  37. }
  38. function mkubifs(vol_dev) {
  39. let temp_mp = mkdtemp();
  40. system(sprintf("mount -t ubifs /dev/%s %s", vol_dev, temp_mp));
  41. system(sprintf("umount %s", temp_mp));
  42. fs.rmdir(temp_mp);
  43. return 0;
  44. }
  45. function block_hotplug(action, devname) {
  46. return system(sprintf("ACTION=%s DEVNAME=%s /sbin/block hotplug", action, devname));
  47. }
  48. function ubi_init(ctx) {
  49. cursor = ctx.cursor;
  50. fs = ctx.fs;
  51. let ubiver = read_file("/sys/class/ubi/version");
  52. if (ubiver != 1)
  53. return false;
  54. let ubidevpath = null;
  55. for (ubidevpath in fs.glob("/sys/devices/virtual/ubi/*"))
  56. break;
  57. if (!ubidevpath)
  58. return false;
  59. ubidev = fs.basename(ubidevpath);
  60. ebsize = read_file(sprintf("%s/eraseblock_size", ubidevpath));
  61. uvol_uci_add = ctx.uci_add;
  62. uvol_uci_commit = ctx.uci_commit;
  63. uvol_uci_remove = ctx.uci_remove;
  64. uvol_uci_init = ctx.uci_init;
  65. return true;
  66. }
  67. function ubi_free() {
  68. let availeb = read_file(sprintf("/sys/devices/virtual/ubi/%s/avail_eraseblocks", ubidev));
  69. return sprintf("%d", availeb * ebsize);
  70. }
  71. function ubi_align() {
  72. return sprintf("%d", ebsize);
  73. }
  74. function ubi_total() {
  75. let totaleb = read_file(sprintf("/sys/devices/virtual/ubi/%s/total_eraseblocks", ubidev));
  76. return sprintf("%d", totaleb * ebsize);
  77. }
  78. function ubi_status(vol_name) {
  79. let vol_dev = ubi_get_dev(vol_name);
  80. if (!vol_dev)
  81. return 2;
  82. let vol_mode = vol_get_mode(vol_dev);
  83. if (vol_mode == "wo") return 22;
  84. if (vol_mode == "wp") return 16;
  85. if (vol_mode == "wd") return 1;
  86. if (vol_mode == "ro" &&
  87. !fs.access(sprintf("/dev/ubiblock%s", substr(vol_dev, 3)), "r")) return 1;
  88. return 0;
  89. }
  90. function ubi_size(vol_name) {
  91. let vol_dev = ubi_get_dev(vol_name);
  92. if (!vol_dev)
  93. return 2;
  94. let vol_size = read_file(sprintf("/sys/devices/virtual/ubi/%s/%s/data_bytes", ubidev, vol_dev));
  95. return sprintf("%d", vol_size);
  96. }
  97. function ubi_device(vol_name) {
  98. let vol_dev = ubi_get_dev(vol_name);
  99. if (!vol_dev)
  100. return 2;
  101. let vol_mode = vol_get_mode(vol_dev);
  102. if (vol_mode == "ro")
  103. return sprintf("/dev/ubiblock%s", substr(vol_dev, 3));
  104. else if (vol_mode == "rw")
  105. return sprintf("/dev/%s", vol_dev);
  106. return null;
  107. }
  108. function ubi_create(vol_name, vol_size, vol_mode) {
  109. let vol_dev = ubi_get_dev(vol_name);
  110. if (vol_dev)
  111. return 17;
  112. let mode;
  113. if (vol_mode == "ro" || vol_mode == "wo")
  114. mode = "wo";
  115. else if (vol_mode == "rw")
  116. mode = "wp";
  117. else
  118. return 22;
  119. let vol_size = +vol_size;
  120. if (vol_size <= 0)
  121. return 22;
  122. let ret = system(sprintf("ubimkvol /dev/%s -N \"uvol-%s-%s\" -s %d", ubidev, mode, vol_name, vol_size));
  123. if (ret != 0)
  124. return ret;
  125. let vol_dev = ubi_get_dev(vol_name);
  126. if (!vol_dev)
  127. return 2;
  128. let ret = system(sprintf("ubiupdatevol -t /dev/%s", vol_dev));
  129. if (ret != 0)
  130. return ret;
  131. if (mode != "wp")
  132. return 0;
  133. let ret = mkubifs(vol_dev);
  134. if (ret != 0)
  135. return ret;
  136. uvol_uci_add(vol_name, sprintf("/dev/%s", vol_dev), "rw");
  137. let ret = system(sprintf("ubirename /dev/%s \"uvol-wp-%s\" \"uvol-wd-%s\"", ubidev, vol_name, vol_name));
  138. if (ret != 0)
  139. return ret;
  140. return 0;
  141. }
  142. function ubi_remove(vol_name) {
  143. let vol_dev = ubi_get_dev(vol_name);
  144. if (!vol_dev)
  145. return 2;
  146. let vol_mode = vol_get_mode(vol_dev);
  147. if (vol_mode == "rw" || vol_mode == "ro")
  148. return 16;
  149. let volnum = split(vol_dev, "_")[1];
  150. let ret = system(sprintf("ubirmvol /dev/%s -n %d", ubidev, volnum));
  151. if (ret != 0)
  152. return ret;
  153. uvol_uci_remove(vol_name);
  154. uvol_uci_commit(vol_name);
  155. return 0;
  156. }
  157. function ubi_up(vol_name) {
  158. let vol_dev = ubi_get_dev(vol_name);
  159. if (!vol_dev)
  160. return 2;
  161. let vol_mode = vol_get_mode(vol_dev);
  162. if (vol_mode == "rw" || vol_mode == "ro")
  163. return 0;
  164. else if (vol_mode == "wo")
  165. return 22;
  166. else if (vol_mode == "wp")
  167. return 16;
  168. uvol_uci_commit(vol_name);
  169. if (vol_mode == "rd") {
  170. let ret = system(sprintf("ubirename /dev/%s \"uvol-rd-%s\" \"uvol-ro-%s\"", ubidev, vol_name, vol_name));
  171. if (ret != 0)
  172. return ret;
  173. return system(sprintf("ubiblock --create /dev/%s", vol_dev));
  174. } else if (vol_mode == "wd") {
  175. let ret = system(sprintf("ubirename /dev/%s \"uvol-wd-%s\" \"uvol-rw-%s\"", ubidev, vol_name, vol_name));
  176. if (ret != 0)
  177. return ret;
  178. return block_hotplug("add", vol_dev);
  179. }
  180. return 0;
  181. }
  182. function ubi_down(vol_name) {
  183. let vol_dev = ubi_get_dev(vol_name);
  184. if (!vol_dev)
  185. return 2;
  186. let vol_mode = vol_get_mode(vol_dev);
  187. if (vol_mode == "rd" || vol_mode == "wd")
  188. return 0;
  189. else if (vol_mode == "wo")
  190. return 22;
  191. else if (vol_mode == "wp")
  192. return 16;
  193. else if (vol_mode == "ro") {
  194. system(sprintf("umount /dev/ubiblock%s 2>&1 >/dev/null", substr(vol_dev, 3)));
  195. system(sprintf("ubiblock --remove /dev/%s", vol_dev));
  196. let ret = system(sprintf("ubirename /dev/%s \"uvol-ro-%s\" \"uvol-rd-%s\"", ubidev, vol_name, vol_name));
  197. return ret;
  198. } else if (vol_mode == "rw") {
  199. system(sprintf("umount /dev/%s 2>&1 >/dev/null", vol_dev));
  200. let ret = system(sprintf("ubirename /dev/%s \"uvol-rw-%s\" \"uvol-wd-%s\"", ubidev, vol_name, vol_name));
  201. block_hotplug("remove", vol_dev);
  202. return ret;
  203. }
  204. return 0;
  205. }
  206. function ubi_list(search_name) {
  207. let volumes = [];
  208. for (vol_dir in fs.glob(sprintf("/sys/devices/virtual/ubi/%s/%s_*", ubidev, ubidev))) {
  209. let vol = {};
  210. let vol_ubiname = read_file(sprintf("%s/name", vol_dir));
  211. if (!wildcard(vol_ubiname, "uvol-[rw][wod]-*"))
  212. continue;
  213. let vol_mode = substr(vol_ubiname, 5, 2);
  214. let vol_name = substr(vol_ubiname, 8);
  215. let vol_size = read_file(sprintf("%s/data_bytes", vol_dir));
  216. if (substr(vol_name, 0, 1) == ".")
  217. continue;
  218. vol.name = vol_name;
  219. vol.mode = vol_mode;
  220. vol.size = vol_size;
  221. push(volumes, vol);
  222. }
  223. return volumes;
  224. }
  225. function ubi_detect() {
  226. let tmpdev = [];
  227. for (vol_dir in fs.glob(sprintf("/sys/devices/virtual/ubi/%s/%s_*", ubidev, ubidev))) {
  228. let vol_ubiname = read_file(sprintf("%s/name", vol_dir));
  229. if (!wildcard(vol_ubiname, "uvol-r[od]-*"))
  230. continue;
  231. let vol_name = substr(vol_ubiname, 8);
  232. let vol_mode = substr(vol_ubiname, 5, 2);
  233. let vol_dev = fs.basename(vol_dir);
  234. ret = system(sprintf("ubiblock --create /dev/%s", vol_dev));
  235. if (ret)
  236. continue;
  237. if (vol_mode == "rd")
  238. push(tmpdev, vol_dev);
  239. }
  240. uvol_uci_init();
  241. for (vol_dir in fs.glob(sprintf("/sys/devices/virtual/ubi/%s/%s_*", ubidev, ubidev))) {
  242. let vol_ubiname = read_file(sprintf("%s/name", vol_dir));
  243. if (!wildcard(vol_ubiname, "uvol-[rw][wod]-*"))
  244. continue;
  245. let vol_dev = fs.basename(vol_dir);
  246. let vol_name = substr(vol_ubiname, 8);
  247. let vol_mode = substr(vol_ubiname, 5, 2);
  248. if (vol_mode == "ro" || vol_mode == "rd")
  249. uvol_uci_add(vol_name, sprintf("/dev/ubiblock%s", substr(vol_dev, 3)), "ro");
  250. else if (vol_mode == "rw" || vol_mode == "wd")
  251. uvol_uci_add(vol_name, sprintf("/dev/%s", vol_dev), "rw");
  252. }
  253. uvol_uci_commit();
  254. for (vol_dev in tmpdev)
  255. system(sprintf("ubiblock --remove /dev/%s", vol_dev));
  256. return 0;
  257. }
  258. function ubi_boot() {
  259. for (vol_dir in fs.glob(sprintf("/sys/devices/virtual/ubi/%s/%s_*", ubidev, ubidev))) {
  260. let vol_dev = fs.basename(vol_dir);
  261. let vol_ubiname = read_file(sprintf("%s/name", vol_dir));
  262. if (!wildcard(vol_ubiname, "uvol-ro-*"))
  263. continue;
  264. system(sprintf("ubiblock --create /dev/%s", vol_dev));
  265. }
  266. }
  267. function ubi_write(vol_name, write_size) {
  268. let vol_dev = ubi_get_dev(vol_name);
  269. if (!vol_dev)
  270. return 2;
  271. write_size = +write_size;
  272. if (write_size <= 0)
  273. return 22;
  274. let vol_mode = vol_get_mode(vol_dev);
  275. if (vol_mode != "wo")
  276. return 22;
  277. let ret = system(sprintf("ubiupdatevol -s %s /dev/%s -", write_size, vol_dev));
  278. if (ret)
  279. return ret;
  280. system(sprintf("ubiblock --create /dev/%s", vol_dev));
  281. uvol_uci_add(vol_name, sprintf("/dev/ubiblock%s", substr(vol_dev, 3)), "ro");
  282. system(sprintf("ubiblock --remove /dev/%s", vol_dev));
  283. system(sprintf("ubirename /dev/%s \"uvol-wo-%s\" \"uvol-rd-%s\"", ubidev, vol_name, vol_name));
  284. return 0;
  285. }
  286. backend.backend = "UBI";
  287. backend.priority = 20;
  288. backend.init = ubi_init;
  289. backend.boot = ubi_boot;
  290. backend.detect = ubi_detect;
  291. backend.free = ubi_free;
  292. backend.align = ubi_align;
  293. backend.total = ubi_total;
  294. backend.list = ubi_list;
  295. backend.size = ubi_size;
  296. backend.status = ubi_status;
  297. backend.device = ubi_device;
  298. backend.up = ubi_up;
  299. backend.down = ubi_down;
  300. backend.create = ubi_create;
  301. backend.remove = ubi_remove;
  302. backend.write = ubi_write;