6 var callBlockDevices, callMountPoints, callBlockDetect, callUmount,
7 callFileRead, callFileStat, callFileExec;
9 callBlockDevices = rpc.declare({
11 method: 'getBlockDevices',
15 callMountPoints = rpc.declare({
17 method: 'getMountPoints',
18 expect: { result: [] }
21 callBlockDetect = rpc.declare({
23 method: 'setBlockDetect',
24 expect: { result: false }
27 callUmount = rpc.declare({
31 expect: { result: false }
34 callFileRead = rpc.declare({
40 return (s || '').split(/\n/).filter(function(ln) {
41 return ln.match(/\S/) && !ln.match(/^nodev\t/);
48 callFileStat = rpc.declare({
53 filter: function(st) {
54 return (L.isObject(st) && st.path != null);
58 callFileExec = rpc.declare({
61 params: [ 'command', 'params' ],
65 function device_textvalue(devices, section_id) {
66 var v = (uci.get('fstab', section_id, 'uuid') || '').toLowerCase(),
67 e = Object.keys(devices).filter(function(dev) { return (devices[dev].uuid || '-').toLowerCase() == v })[0];
70 this.section.devices[section_id] = devices[e];
72 if (e && devices[e].size)
73 return E('span', 'UUID: %h (%s, %1024.2mB)'.format(v, devices[e].dev, devices[e].size));
75 return E('span', 'UUID: %h (%s)'.format(v, devices[e].dev));
77 return E('span', 'UUID: %h (<em>%s</em>)'.format(v, _('not present')));
80 v = uci.get('fstab', section_id, 'label');
81 e = Object.keys(devices).filter(function(dev) { return devices[dev].label == v })[0];
84 this.section.devices[section_id] = this.section.devices[section_id] || devices[e];
86 if (e && devices[e].size)
87 return E('span', 'Label: %h (%s, %1024.2mB)'.format(v, devices[e].dev, devices[e].size));
89 return E('span', 'Label: %h (%s)'.format(v, devices[e].dev));
91 return E('span', 'Label: %h (<em>%s</em>)'.format(v, _('not present')));
94 v = uci.get('fstab', section_id, 'device');
95 e = Object.keys(devices).filter(function(dev) { return devices[dev].dev == v })[0];
98 this.section.devices[section_id] = this.section.devices[section_id] || devices[e];
100 if (e && devices[e].size)
101 return E('span', '%h (%1024.2mB)'.format(v, devices[e].size));
103 return E('span', '%h'.format(v));
105 return E('span', '%h (<em>%s</em>)'.format(v, _('not present')));
109 return L.view.extend({
110 handleDetect: function(m, ev) {
111 return callBlockDetect()
112 .then(L.bind(uci.unload, uci, 'fstab'))
113 .then(L.bind(m.render, m));
116 handleMountAll: function(m, ev) {
117 return callFileExec('/sbin/block', ['mount'])
120 L.ui.addNotification(null, E('p', _('The <em>block mount</em> command failed with code %d').format(rc)));
122 .then(L.bind(uci.unload, uci, 'fstab'))
123 .then(L.bind(m.render, m));
126 handleUmount: function(m, path, ev) {
127 return callUmount(path)
128 .then(L.bind(uci.unload, uci, 'fstab'))
129 .then(L.bind(m.render, m));
135 callFileRead('/proc/filesystems'),
136 callFileRead('/etc/filesystems'),
137 callFileStat('/usr/sbin/e2fsck'),
138 callFileStat('/usr/sbin/fsck.f2fs'),
139 callFileStat('/usr/sbin/dosfsck'),
140 callFileStat('/usr/bin/btrfsck'),
145 render: function(results) {
146 var devices = results[0],
152 var filesystems = procfs.concat(etcfs.filter(function(fs) {
153 return procfs.indexOf(fs) < 0;
165 if (!uci.sections('fstab', 'global').length)
166 uci.add('fstab', 'global');
168 m = new form.Map('fstab', _('Mount Points'));
170 s = m.section(form.TypedSection, 'global', _('Global Settings'));
174 o = s.option(form.Button, '_detect', _('Generate Config'), _('Find all currently attached filesystems and swap and replace configuration with defaults based on what was detected'));
175 o.onclick = this.handleDetect.bind(this, m);
176 o.inputstyle = 'reload';
178 o = s.option(form.Button, '_mountall', _('Mount attached devices'), _('Attempt to enable configured mount points for attached devices'));
179 o.onclick = this.handleMountAll.bind(this, m);
180 o.inputstyle = 'reload';
182 o = s.option(form.Flag, 'anon_swap', _('Anonymous Swap'), _('Mount swap not specifically configured'));
183 o.default = o.disabled;
186 o = s.option(form.Flag, 'anon_mount', _('Anonymous Mount'), _('Mount filesystems not specifically configured'));
187 o.default = o.disabled;
190 o = s.option(form.Flag, 'auto_swap', _('Automount Swap'), _('Automatically mount swap on hotplug'));
191 o.default = o.enabled;
194 o = s.option(form.Flag, 'auto_mount', _('Automount Filesystem'), _('Automatically mount filesystems on hotplug'));
195 o.default = o.enabled;
198 o = s.option(form.Flag, 'check_fs', _('Check filesystems before mount'), _('Automatically check filesystem for errors before mounting'));
199 o.default = o.disabled;
203 // Mount status table
204 o = s.option(form.DummyValue, '_mtab');
206 o.load = function(section_id) {
207 return callMountPoints().then(L.bind(function(mounts) {
208 this.mounts = mounts;
212 o.render = L.bind(function(view, section_id) {
213 var table = E('div', { 'class': 'table' }, [
214 E('div', { 'class': 'tr table-titles' }, [
215 E('div', { 'class': 'th' }, _('Filesystem')),
216 E('div', { 'class': 'th' }, _('Mount Point')),
217 E('div', { 'class': 'th center' }, _('Available')),
218 E('div', { 'class': 'th center' }, _('Used')),
219 E('div', { 'class': 'th' }, _('Unmount'))
225 for (var i = 0; i < this.mounts.length; i++) {
226 var used = this.mounts[i].size - this.mounts[i].free,
229 if (/^\/(overlay|rom|tmp(?:\/.+)?|dev(?:\/.+)?|)$/.test(this.mounts[i].mount))
233 this.mounts[i].device,
234 this.mounts[i].mount,
235 '%1024.2mB / %1024.2mB'.format(this.mounts[i].avail, this.mounts[i].size),
236 '%.2f%% (%1024.2mB)'.format(100 / this.mounts[i].size * used, used),
237 umount ? E('button', {
238 'class': 'btn cbi-button-remove',
239 'click': L.ui.createHandlerFn(view, 'handleUmount', m, this.mounts[i].mount)
240 }, [ _('Unmount') ]) : '-'
244 cbi_update_table(table, rows, E('em', _('Unable to obtain mount information')));
246 return E([], [ E('h3', _('Mounted file systems')), table ]);
251 s = m.section(form.GridSection, 'mount', _('Mount Points'), _('Mount Points define at which point a memory device will be attached to the filesystem'));
252 s.modaltitle = _('Mount Points - Mount Entry');
258 s.renderHeaderRows = function(/* ... */) {
259 var trEls = form.GridSection.prototype.renderHeaderRows.apply(this, arguments);
260 return trEls.childNodes[0];
263 s.tab('general', _('General Settings'));
264 s.tab('advanced', _('Advanced Settings'));
266 o = s.taboption('general', form.Flag, 'enabled', _('Enabled'));
270 o = s.taboption('general', form.DummyValue, '_device', _('Device'));
273 o.write = function() {};
274 o.remove = function() {};
275 o.textvalue = device_textvalue.bind(o, devices);
277 o = s.taboption('general', form.Value, 'uuid', _('UUID'), _('If specified, mount the device by its UUID instead of a fixed device node'));
279 o.value('', _('-- match by uuid --'));
281 var devs = Object.keys(devices).sort();
282 for (var i = 0; i < devs.length; i++) {
283 var dev = devices[devs[i]];
284 if (dev.uuid && dev.size)
285 o.value(dev.uuid, '%s (%s, %1024.2mB)'.format(dev.uuid, dev.dev, dev.size));
287 o.value(dev.uuid, '%s (%s)'.format(dev.uuid, dev.dev));
290 o = s.taboption('general', form.Value, 'label', _('Label'), _('If specified, mount the device by the partition label instead of a fixed device node'));
292 o.depends('uuid', '');
293 o.value('', _('-- match by label --'));
295 for (var i = 0; i < devs.length; i++) {
296 var dev = devices[devs[i]];
297 if (dev.label && dev.size)
298 o.value(dev.label, '%s (%s, %1024.2mB)'.format(dev.label, dev.dev, dev.size));
300 o.value(dev.label, '%s (%s)'.format(dev.label, dev.dev));
303 o = s.taboption('general', form.Value, 'device', _('Device'), _('The device file of the memory or partition (<abbr title="for example">e.g.</abbr> <code>/dev/sda1</code>)'));
305 o.depends({ uuid: '', label: '' });
307 for (var i = 0; i < devs.length; i++) {
308 var dev = devices[devs[i]];
310 o.value(dev.dev, '%s (%1024.2mB)'.format(dev.dev, dev.size));
315 o = s.taboption('general', form.Value, 'target', _('Mount point'), _('Specifies the directory the device is attached to'));
316 o.value('/', _('Use as root filesystem (/)'));
317 o.value('/overlay', _('Use as external overlay (/overlay)'));
320 o = s.taboption('general', form.DummyValue, '__notice', _('Root preparation'));
321 o.depends('target', '/');
325 '<p>%s</p>'.format(_('Make sure to clone the root filesystem using something like the commands below:')) +
327 'mkdir -p /tmp/introot\n' +
328 'mkdir -p /tmp/extroot\n' +
329 'mount --bind / /tmp/introot\n' +
330 'mount /dev/sda1 /tmp/extroot\n' +
331 'tar -C /tmp/introot -cvf - . | tar -C /tmp/extroot -xf -\n' +
332 'umount /tmp/introot\n' +
333 'umount /tmp/extroot\n' +
337 o = s.taboption('advanced', form.ListValue, 'fstype', _('Filesystem'));
339 o.textvalue = function(section_id) {
340 var dev = this.section.devices[section_id],
341 text = this.cfgvalue(section_id) || 'auto';
343 if (dev && dev.type && dev.type != text)
344 text += ' (%s)'.format(dev.type);
351 for (var i = 0; i < filesystems.length; i++)
352 o.value(filesystems[i]);
354 o = s.taboption('advanced', form.Value, 'options', _('Mount options'), _('See "mount" manpage for details'));
355 o.textvalue = function(section_id) { return this.cfgvalue(section_id) || 'defaults' };
356 o.placeholder = 'defaults';
358 s.taboption('advanced', form.Flag, 'enabled_fsck', _('Run filesystem check'), _('Run a filesystem check before mounting the device'));
362 s = m.section(form.GridSection, 'swap', _('SWAP'), _('If your physical memory is insufficient unused data can be temporarily swapped to a swap-device resulting in a higher amount of usable <abbr title="Random Access Memory">RAM</abbr>. Be aware that swapping data is a very slow process as the swap-device cannot be accessed with the high datarates of the <abbr title="Random Access Memory">RAM</abbr>.'));
363 s.modaltitle = _('Mount Points - Swap Entry');
369 s.renderHeaderRows = function(/* ... */) {
370 var trEls = form.GridSection.prototype.renderHeaderRows.apply(this, arguments);
371 trEls.childNodes[0].childNodes[1].style.width = '90%';
372 return trEls.childNodes[0];
375 o = s.option(form.Flag, 'enabled', _('Enabled'));
379 o = s.option(form.DummyValue, '_device', _('Device'));
381 o.textvalue = device_textvalue.bind(o, devices);
383 o = s.option(form.Value, 'uuid', _('UUID'), _('If specified, mount the device by its UUID instead of a fixed device node'));
385 o.value('', _('-- match by uuid --'));
387 var devs = Object.keys(devices).sort();
388 for (var i = 0; i < devs.length; i++) {
389 var dev = devices[devs[i]];
390 if (dev.dev.match(/^\/dev\/(mtdblock|ubi|ubiblock)\d/))
393 if (dev.uuid && dev.size)
394 o.value(dev.uuid, '%s (%s, %1024.2mB)'.format(dev.uuid, dev.dev, dev.size));
396 o.value(dev.uuid, '%s (%s)'.format(dev.uuid, dev.dev));
399 o = s.option(form.Value, 'label', _('Label'), _('If specified, mount the device by the partition label instead of a fixed device node'));
401 o.depends('uuid', '');
402 o.value('', _('-- match by label --'));
404 for (var i = 0; i < devs.length; i++) {
405 var dev = devices[devs[i]];
406 if (dev.dev.match(/^\/dev\/(mtdblock|ubi|ubiblock)\d/))
409 if (dev.label && dev.size)
410 o.value(dev.label, '%s (%s, %1024.2mB)'.format(dev.label, dev.dev, dev.size));
412 o.value(dev.label, '%s (%s)'.format(dev.label, dev.dev));
415 o = s.option(form.Value, 'device', _('Device'), _('The device file of the memory or partition (<abbr title="for example">e.g.</abbr> <code>/dev/sda1</code>)'));
417 o.depends({ uuid: '', label: '' });
419 for (var i = 0; i < devs.length; i++) {
420 var dev = devices[devs[i]];
421 if (dev.dev.match(/^\/dev\/(mtdblock|ubi|ubiblock)\d/))
425 o.value(dev.dev, '%s (%1024.2mB)'.format(dev.dev, dev.size));