provision.uc 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) 2025 Felix Fietkau <[email protected]>
  4. */
  5. 'use strict';
  6. import * as struct from "struct";
  7. import * as fs from "fs";
  8. const MAGIC = 0xf09f8697;
  9. const HDR_LEN = 9;
  10. let hdr = struct.new(">LLc");
  11. const ubi_proto = {
  12. read: function() {
  13. let file = fs.open(this.dev);
  14. if (!file)
  15. return;
  16. let hdr_data = file.read(HDR_LEN);
  17. if (!hdr_data)
  18. return;
  19. hdr_data = hdr.unpack(hdr_data);
  20. if (!hdr_data)
  21. return;
  22. if (hdr_data[0] != MAGIC)
  23. return;
  24. if (hdr_data[1] > 131072 || hdr_data[2] != 0)
  25. return;
  26. let data = file.read(hdr_data[1]);
  27. if (length(data) != hdr_data[1])
  28. return;
  29. return data;
  30. },
  31. commit: function(data) {
  32. let len = HDR_LEN + length(data);
  33. let file = fs.popen(`ubiupdatevol ${this.dev} -s ${len} -`, "w");
  34. file.write(hdr.pack(MAGIC, length(data), 0));
  35. file.write(data);
  36. return file.close() == 0;
  37. },
  38. destroy: function() {
  39. let dev = replace(this.dev, /_\d+$/, "");
  40. return system(`ubirmvol ${dev} -N provisioning`) == 0;
  41. }
  42. };
  43. function open_ubi()
  44. {
  45. let found = fs.glob("/sys/class/ubi/*/name");
  46. found = filter(found, (v) => trim(fs.readfile(v)) == "provisioning");
  47. if (!length(found))
  48. return;
  49. let dev_name = fs.basename(fs.dirname(found[0]));
  50. return proto({
  51. dev: "/dev/" + dev_name,
  52. }, ubi_proto);
  53. }
  54. function create_ubi()
  55. {
  56. let ctx = open_ubi();
  57. if (ctx)
  58. return ctx;
  59. let found = fs.glob("/sys/class/ubi/*/name");
  60. found = filter(found, (v) => substr(fs.readfile(v), 0, 6) == "rootfs");
  61. if (!length(found))
  62. return;
  63. let dev = fs.basename(fs.dirname(found[0]));
  64. dev = "/dev/" + replace(dev, /_\d+$/, "");
  65. if (system(`ubimkvol ${dev} -N provisioning -s 131072`) != 0)
  66. return;
  67. return open_ubi();
  68. }
  69. function data_path_get(data, path, create)
  70. {
  71. if (!data)
  72. return;
  73. if (!length(path))
  74. return data;
  75. if (type(path) == "string")
  76. path = split(path, ".");
  77. let last = data;
  78. let last_name;
  79. for (let name in path) {
  80. switch (type(data)) {
  81. case "object":
  82. last = data;
  83. last_name = name;
  84. data = data[name];
  85. break;
  86. case "array":
  87. last = data;
  88. last_name = name;
  89. data = data[+name];
  90. break;
  91. default:
  92. return;
  93. }
  94. if (data == null && create)
  95. data = last[last_name] = {};
  96. }
  97. return data;
  98. }
  99. const provision_proto = {
  100. init: function() {
  101. this.data = this.backend.read();
  102. try {
  103. this.data = json(this.data);
  104. } catch(e) {
  105. this.data = null;
  106. }
  107. if (!this.data)
  108. this.reset();
  109. return true;
  110. },
  111. get: function(path) {
  112. return data_path_get(this.data, path);
  113. },
  114. set: function(path, value) {
  115. if (!length(path))
  116. return;
  117. if (type(path) == "string")
  118. path = split(path, ".");
  119. let name = pop(path);
  120. let data = data_path_get(this.data, path, true);
  121. if (type(data) != "object")
  122. return;
  123. if (value == null)
  124. delete data[name];
  125. else
  126. data[name] = value;
  127. return true;
  128. },
  129. reset: function() {
  130. this.data = {};
  131. return true;
  132. },
  133. commit: function() {
  134. if (!this.data)
  135. return;
  136. return this.backend.commit("" + this.data);
  137. },
  138. destroy: function() {
  139. return this.backend.destroy();
  140. }
  141. };
  142. function __open(backend)
  143. {
  144. if (!backend)
  145. return;
  146. return proto({
  147. backend,
  148. }, provision_proto);
  149. }
  150. export function create()
  151. {
  152. return __open(create_ubi());
  153. };
  154. export function open()
  155. {
  156. return __open(open_ubi());
  157. };