8 var callBlockDevices
, callMountPoints
, callBlockDetect
;
10 callBlockDevices
= rpc
.declare({
12 method
: 'getBlockDevices',
16 callMountPoints
= rpc
.declare({
18 method
: 'getMountPoints',
19 expect
: { result
: [] }
22 callBlockDetect
= rpc
.declare({
24 method
: 'setBlockDetect',
25 expect
: { result
: false }
28 function device_textvalue(devices
, section_id
) {
29 var v
= (uci
.get('fstab', section_id
, 'uuid') || '').toLowerCase(),
30 e
= Object
.keys(devices
).filter(function(dev
) { return (devices
[dev
].uuid
|| '-').toLowerCase() == v
})[0];
33 this.section
.devices
[section_id
] = devices
[e
];
35 if (e
&& devices
[e
].size
)
36 return E('span', 'UUID: %h (%s, %1024.2mB)'.format(v
, devices
[e
].dev
, devices
[e
].size
));
38 return E('span', 'UUID: %h (%s)'.format(v
, devices
[e
].dev
));
40 return E('span', 'UUID: %h (<em>%s</em>)'.format(v
, _('not present')));
43 v
= uci
.get('fstab', section_id
, 'label');
44 e
= Object
.keys(devices
).filter(function(dev
) { return devices
[dev
].label
== v
})[0];
47 this.section
.devices
[section_id
] = this.section
.devices
[section_id
] || devices
[e
];
49 if (e
&& devices
[e
].size
)
50 return E('span', 'Label: %h (%s, %1024.2mB)'.format(v
, devices
[e
].dev
, devices
[e
].size
));
52 return E('span', 'Label: %h (%s)'.format(v
, devices
[e
].dev
));
54 return E('span', 'Label: %h (<em>%s</em>)'.format(v
, _('not present')));
57 v
= uci
.get('fstab', section_id
, 'device');
58 e
= Object
.keys(devices
).filter(function(dev
) { return devices
[dev
].dev
== v
})[0];
61 this.section
.devices
[section_id
] = this.section
.devices
[section_id
] || devices
[e
];
63 if (e
&& devices
[e
].size
)
64 return E('span', '%h (%1024.2mB)'.format(v
, devices
[e
].size
));
66 return E('span', '%h'.format(v
));
68 return E('span', '%h (<em>%s</em>)'.format(v
, _('not present')));
72 return L
.view
.extend({
73 handleDetect: function(m
, ev
) {
74 return callBlockDetect()
75 .then(L
.bind(uci
.unload
, uci
, 'fstab'))
76 .then(L
.bind(m
.render
, m
));
79 handleMountAll: function(m
, ev
) {
80 return fs
.exec('/sbin/block', ['mount'])
83 ui
.addNotification(null, E('p', _('The <em>block mount</em> command failed with code %d').format(res
.code
)));
85 .then(L
.bind(uci
.unload
, uci
, 'fstab'))
86 .then(L
.bind(m
.render
, m
));
89 handleUmount: function(m
, path
, ev
) {
90 return fs
.exec('/bin/umount', [path
])
91 .then(L
.bind(uci
.unload
, uci
, 'fstab'))
92 .then(L
.bind(m
.render
, m
))
93 .catch(function(e
) { ui
.addNotification(null, E('p', e
.message
)) });
99 fs
.lines('/proc/filesystems'),
100 fs
.lines('/etc/filesystems'),
101 L
.resolveDefault(fs
.stat('/usr/sbin/e2fsck'), null),
102 L
.resolveDefault(fs
.stat('/usr/sbin/fsck.f2fs'), null),
103 L
.resolveDefault(fs
.stat('/usr/sbin/fsck.fat'), null),
104 L
.resolveDefault(fs
.stat('/usr/bin/btrfsck'), null),
105 L
.resolveDefault(fs
.stat('/usr/bin/ntfsfix'), null),
110 render: function(results
) {
111 var devices
= results
[0],
127 var filesystems
= {};
129 for (var i
= 0; i
< procfs
.length
; i
++)
130 if (procfs
[i
].match(/\S/) && !procfs
[i
].match(/^nodev\t/))
131 filesystems
[procfs
[i
].trim()] = true;
133 for (var i
= 0; i
< etcfs
.length
; i
++)
134 if (etcfs
[i
].match(/\S/))
135 filesystems
[etcfs
[i
].trim()] = true;
137 filesystems
= Object
.keys(filesystems
).sort();
140 if (!uci
.sections('fstab', 'global').length
)
141 uci
.add('fstab', 'global');
143 m
= new form
.Map('fstab', _('Mount Points'));
145 s
= m
.section(form
.TypedSection
, 'global', _('Global Settings'));
149 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'));
150 o
.onclick
= this.handleDetect
.bind(this, m
);
151 o
.inputstyle
= 'reload';
153 o
= s
.option(form
.Button
, '_mountall', _('Mount attached devices'), _('Attempt to enable configured mount points for attached devices'));
154 o
.onclick
= this.handleMountAll
.bind(this, m
);
155 o
.inputstyle
= 'reload';
157 o
= s
.option(form
.Flag
, 'anon_swap', _('Anonymous Swap'), _('Mount swap not specifically configured'));
158 o
.default = o
.disabled
;
161 o
= s
.option(form
.Flag
, 'anon_mount', _('Anonymous Mount'), _('Mount filesystems not specifically configured'));
162 o
.default = o
.disabled
;
165 o
= s
.option(form
.Flag
, 'auto_swap', _('Automount Swap'), _('Automatically mount swap on hotplug'));
166 o
.default = o
.enabled
;
169 o
= s
.option(form
.Flag
, 'auto_mount', _('Automount Filesystem'), _('Automatically mount filesystems on hotplug'));
170 o
.default = o
.enabled
;
173 o
= s
.option(form
.Flag
, 'check_fs', _('Check filesystems before mount'), _('Automatically check filesystem for errors before mounting'));
174 o
.default = o
.disabled
;
178 // Mount status table
179 o
= s
.option(form
.DummyValue
, '_mtab');
181 o
.load = function(section_id
) {
182 return callMountPoints().then(L
.bind(function(mounts
) {
183 this.mounts
= mounts
;
187 o
.render
= L
.bind(function(view
, section_id
) {
188 var table
= E('div', { 'class': 'table' }, [
189 E('div', { 'class': 'tr table-titles' }, [
190 E('div', { 'class': 'th' }, _('Filesystem')),
191 E('div', { 'class': 'th' }, _('Mount Point')),
192 E('div', { 'class': 'th center' }, _('Available')),
193 E('div', { 'class': 'th center' }, _('Used')),
194 E('div', { 'class': 'th' }, _('Unmount'))
200 for (var i
= 0; i
< this.mounts
.length
; i
++) {
201 var used
= this.mounts
[i
].size
- this.mounts
[i
].free
,
204 if (/^\/(overlay|rom|tmp(?:\/.+)?|dev(?:\/.+)?|)$/.test(this.mounts
[i
].mount
))
208 this.mounts
[i
].device
,
209 this.mounts
[i
].mount
,
210 '%1024.2mB / %1024.2mB'.format(this.mounts
[i
].avail
, this.mounts
[i
].size
),
211 '%.2f%% (%1024.2mB)'.format(100 / this.mounts
[i
].size
* used
, used
),
212 umount
? E('button', {
213 'class': 'btn cbi-button-remove',
214 'click': ui
.createHandlerFn(view
, 'handleUmount', m
, this.mounts
[i
].mount
)
215 }, [ _('Unmount') ]) : '-'
219 cbi_update_table(table
, rows
, E('em', _('Unable to obtain mount information')));
221 return E([], [ E('h3', _('Mounted file systems')), table
]);
226 s
= m
.section(form
.GridSection
, 'mount', _('Mount Points'), _('Mount Points define at which point a memory device will be attached to the filesystem'));
227 s
.modaltitle
= _('Mount Points - Mount Entry');
233 s
.renderHeaderRows = function(/* ... */) {
234 var trEls
= form
.GridSection
.prototype.renderHeaderRows
.apply(this, arguments
);
235 return trEls
.childNodes
[0];
238 s
.tab('general', _('General Settings'));
239 s
.tab('advanced', _('Advanced Settings'));
241 o
= s
.taboption('general', form
.Flag
, 'enabled', _('Enabled'));
245 o
= s
.taboption('general', form
.DummyValue
, '_device', _('Device'));
248 o
.write = function() {};
249 o
.remove = function() {};
250 o
.textvalue
= device_textvalue
.bind(o
, devices
);
252 o
= s
.taboption('general', form
.Value
, 'uuid', _('UUID'), _('If specified, mount the device by its UUID instead of a fixed device node'));
254 o
.value('', _('-- match by uuid --'));
256 var devs
= Object
.keys(devices
).sort();
257 for (var i
= 0; i
< devs
.length
; i
++) {
258 var dev
= devices
[devs
[i
]];
259 if (dev
.uuid
&& dev
.size
)
260 o
.value(dev
.uuid
, '%s (%s, %1024.2mB)'.format(dev
.uuid
, dev
.dev
, dev
.size
));
262 o
.value(dev
.uuid
, '%s (%s)'.format(dev
.uuid
, dev
.dev
));
265 o
= s
.taboption('general', form
.Value
, 'label', _('Label'), _('If specified, mount the device by the partition label instead of a fixed device node'));
267 o
.depends('uuid', '');
268 o
.value('', _('-- match by label --'));
270 for (var i
= 0; i
< devs
.length
; i
++) {
271 var dev
= devices
[devs
[i
]];
272 if (dev
.label
&& dev
.size
)
273 o
.value(dev
.label
, '%s (%s, %1024.2mB)'.format(dev
.label
, dev
.dev
, dev
.size
));
275 o
.value(dev
.label
, '%s (%s)'.format(dev
.label
, dev
.dev
));
278 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>)'));
280 o
.depends({ uuid
: '', label
: '' });
282 for (var i
= 0; i
< devs
.length
; i
++) {
283 var dev
= devices
[devs
[i
]];
285 o
.value(dev
.dev
, '%s (%1024.2mB)'.format(dev
.dev
, dev
.size
));
290 o
= s
.taboption('general', form
.Value
, 'target', _('Mount point'), _('Specifies the directory the device is attached to'));
291 o
.value('/', _('Use as root filesystem (/)'));
292 o
.value('/overlay', _('Use as external overlay (/overlay)'));
295 o
= s
.taboption('general', form
.DummyValue
, '__notice', _('Root preparation'));
296 o
.depends('target', '/');
300 '<p>%s</p>'.format(_('Make sure to clone the root filesystem using something like the commands below:')) +
302 'mkdir -p /tmp/introot\n' +
303 'mkdir -p /tmp/extroot\n' +
304 'mount --bind / /tmp/introot\n' +
305 'mount /dev/sda1 /tmp/extroot\n' +
306 'tar -C /tmp/introot -cvf - . | tar -C /tmp/extroot -xf -\n' +
307 'umount /tmp/introot\n' +
308 'umount /tmp/extroot\n' +
312 o
= s
.taboption('advanced', form
.ListValue
, 'fstype', _('Filesystem'));
314 o
.textvalue = function(section_id
) {
315 var dev
= this.section
.devices
[section_id
],
316 text
= this.cfgvalue(section_id
) || 'auto';
318 if (dev
&& dev
.type
&& dev
.type
!= text
)
319 text
+= ' (%s)'.format(dev
.type
);
326 for (var i
= 0; i
< filesystems
.length
; i
++)
327 o
.value(filesystems
[i
]);
329 o
= s
.taboption('advanced', form
.Value
, 'options', _('Mount options'), _('See "mount" manpage for details'));
330 o
.textvalue = function(section_id
) { return this.cfgvalue(section_id
) || 'defaults' };
331 o
.placeholder
= 'defaults';
333 s
.taboption('advanced', form
.Flag
, 'enabled_fsck', _('Run filesystem check'), _('Run a filesystem check before mounting the device'));
337 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>.'));
338 s
.modaltitle
= _('Mount Points - Swap Entry');
344 s
.renderHeaderRows = function(/* ... */) {
345 var trEls
= form
.GridSection
.prototype.renderHeaderRows
.apply(this, arguments
);
346 trEls
.childNodes
[0].childNodes
[1].style
.width
= '90%';
347 return trEls
.childNodes
[0];
350 o
= s
.option(form
.Flag
, 'enabled', _('Enabled'));
354 o
= s
.option(form
.DummyValue
, '_device', _('Device'));
356 o
.textvalue
= device_textvalue
.bind(o
, devices
);
358 o
= s
.option(form
.Value
, 'uuid', _('UUID'), _('If specified, mount the device by its UUID instead of a fixed device node'));
360 o
.value('', _('-- match by uuid --'));
362 var devs
= Object
.keys(devices
).sort();
363 for (var i
= 0; i
< devs
.length
; i
++) {
364 var dev
= devices
[devs
[i
]];
365 if (dev
.dev
.match(/^\/dev\/(mtdblock|ubi|ubiblock)\d/))
368 if (dev
.uuid
&& dev
.size
)
369 o
.value(dev
.uuid
, '%s (%s, %1024.2mB)'.format(dev
.uuid
, dev
.dev
, dev
.size
));
371 o
.value(dev
.uuid
, '%s (%s)'.format(dev
.uuid
, dev
.dev
));
374 o
= s
.option(form
.Value
, 'label', _('Label'), _('If specified, mount the device by the partition label instead of a fixed device node'));
376 o
.depends('uuid', '');
377 o
.value('', _('-- match by label --'));
379 for (var i
= 0; i
< devs
.length
; i
++) {
380 var dev
= devices
[devs
[i
]];
381 if (dev
.dev
.match(/^\/dev\/(mtdblock|ubi|ubiblock)\d/))
384 if (dev
.label
&& dev
.size
)
385 o
.value(dev
.label
, '%s (%s, %1024.2mB)'.format(dev
.label
, dev
.dev
, dev
.size
));
387 o
.value(dev
.label
, '%s (%s)'.format(dev
.label
, dev
.dev
));
390 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>)'));
392 o
.depends({ uuid
: '', label
: '' });
394 for (var i
= 0; i
< devs
.length
; i
++) {
395 var dev
= devices
[devs
[i
]];
396 if (dev
.dev
.match(/^\/dev\/(mtdblock|ubi|ubiblock)\d/))
400 o
.value(dev
.dev
, '%s (%1024.2mB)'.format(dev
.dev
, dev
.size
));