2019-09-21 16:36:31 +00:00
'use strict' ;
2020-04-03 08:00:06 +00:00
'require view' ;
2019-10-31 15:25:49 +00:00
'require fs' ;
2019-11-03 20:54:40 +00:00
'require ui' ;
2019-09-21 16:36:31 +00:00
'require uci' ;
'require rpc' ;
'require form' ;
2019-10-31 15:25:49 +00:00
var callBlockDevices , callMountPoints , callBlockDetect ;
2019-09-21 16:36:31 +00:00
callBlockDevices = rpc . declare ( {
object : 'luci' ,
method : 'getBlockDevices' ,
expect : { '' : { } }
} ) ;
callMountPoints = rpc . declare ( {
object : 'luci' ,
method : 'getMountPoints' ,
expect : { result : [ ] }
} ) ;
callBlockDetect = rpc . declare ( {
object : 'luci' ,
method : 'setBlockDetect' ,
expect : { result : false }
} ) ;
function device _textvalue ( devices , section _id ) {
var v = ( uci . get ( 'fstab' , section _id , 'uuid' ) || '' ) . toLowerCase ( ) ,
e = Object . keys ( devices ) . filter ( function ( dev ) { return ( devices [ dev ] . uuid || '-' ) . toLowerCase ( ) == v } ) [ 0 ] ;
if ( v ) {
this . section . devices [ section _id ] = devices [ e ] ;
if ( e && devices [ e ] . size )
return E ( 'span' , 'UUID: %h (%s, %1024.2mB)' . format ( v , devices [ e ] . dev , devices [ e ] . size ) ) ;
else if ( e )
return E ( 'span' , 'UUID: %h (%s)' . format ( v , devices [ e ] . dev ) ) ;
else
return E ( 'span' , 'UUID: %h (<em>%s</em>)' . format ( v , _ ( 'not present' ) ) ) ;
}
v = uci . get ( 'fstab' , section _id , 'label' ) ;
e = Object . keys ( devices ) . filter ( function ( dev ) { return devices [ dev ] . label == v } ) [ 0 ] ;
if ( v ) {
this . section . devices [ section _id ] = this . section . devices [ section _id ] || devices [ e ] ;
if ( e && devices [ e ] . size )
return E ( 'span' , 'Label: %h (%s, %1024.2mB)' . format ( v , devices [ e ] . dev , devices [ e ] . size ) ) ;
else if ( e )
return E ( 'span' , 'Label: %h (%s)' . format ( v , devices [ e ] . dev ) ) ;
else
return E ( 'span' , 'Label: %h (<em>%s</em>)' . format ( v , _ ( 'not present' ) ) ) ;
}
v = uci . get ( 'fstab' , section _id , 'device' ) ;
e = Object . keys ( devices ) . filter ( function ( dev ) { return devices [ dev ] . dev == v } ) [ 0 ] ;
if ( v ) {
this . section . devices [ section _id ] = this . section . devices [ section _id ] || devices [ e ] ;
if ( e && devices [ e ] . size )
return E ( 'span' , '%h (%1024.2mB)' . format ( v , devices [ e ] . size ) ) ;
else if ( e )
return E ( 'span' , '%h' . format ( v ) ) ;
else
return E ( 'span' , '%h (<em>%s</em>)' . format ( v , _ ( 'not present' ) ) ) ;
}
}
2020-04-03 08:00:06 +00:00
return view . extend ( {
2019-09-21 16:36:31 +00:00
handleDetect : function ( m , ev ) {
return callBlockDetect ( )
. then ( L . bind ( uci . unload , uci , 'fstab' ) )
. then ( L . bind ( m . render , m ) ) ;
} ,
handleMountAll : function ( m , ev ) {
2019-10-31 15:25:49 +00:00
return fs . exec ( '/sbin/block' , [ 'mount' ] )
. then ( function ( res ) {
if ( res . code != 0 )
2019-11-03 20:54:40 +00:00
ui . addNotification ( null , E ( 'p' , _ ( 'The <em>block mount</em> command failed with code %d' ) . format ( res . code ) ) ) ;
2019-09-21 16:36:31 +00:00
} )
. then ( L . bind ( uci . unload , uci , 'fstab' ) )
. then ( L . bind ( m . render , m ) ) ;
} ,
handleUmount : function ( m , path , ev ) {
2019-10-31 15:25:49 +00:00
return fs . exec ( '/bin/umount' , [ path ] )
2019-09-21 16:36:31 +00:00
. then ( L . bind ( uci . unload , uci , 'fstab' ) )
2019-10-31 15:25:49 +00:00
. then ( L . bind ( m . render , m ) )
2019-11-03 20:54:40 +00:00
. catch ( function ( e ) { ui . addNotification ( null , E ( 'p' , e . message ) ) } ) ;
2019-09-21 16:36:31 +00:00
} ,
load : function ( ) {
return Promise . all ( [
callBlockDevices ( ) ,
2019-10-31 15:25:49 +00:00
fs . lines ( '/proc/filesystems' ) ,
fs . lines ( '/etc/filesystems' ) ,
L . resolveDefault ( fs . stat ( '/usr/sbin/e2fsck' ) , null ) ,
L . resolveDefault ( fs . stat ( '/usr/sbin/fsck.f2fs' ) , null ) ,
L . resolveDefault ( fs . stat ( '/usr/sbin/fsck.fat' ) , null ) ,
L . resolveDefault ( fs . stat ( '/usr/bin/btrfsck' ) , null ) ,
L . resolveDefault ( fs . stat ( '/usr/bin/ntfsfix' ) , null ) ,
2019-09-21 16:46:17 +00:00
uci . load ( 'fstab' )
2019-09-21 16:36:31 +00:00
] ) ;
} ,
render : function ( results ) {
var devices = results [ 0 ] ,
procfs = results [ 1 ] ,
etcfs = results [ 2 ] ,
triggers = { } ,
trigger , m , s , o ;
var fsck = {
ext2 : results [ 3 ] ,
ext3 : results [ 3 ] ,
ext4 : results [ 3 ] ,
f2fs : results [ 4 ] ,
vfat : results [ 5 ] ,
2019-10-31 15:25:49 +00:00
btrfs : results [ 6 ] ,
ntfs : results [ 7 ]
2019-09-21 16:36:31 +00:00
} ;
2019-10-31 15:25:49 +00:00
var filesystems = { } ;
for ( var i = 0 ; i < procfs . length ; i ++ )
if ( procfs [ i ] . match ( /\S/ ) && ! procfs [ i ] . match ( /^nodev\t/ ) )
filesystems [ procfs [ i ] . trim ( ) ] = true ;
for ( var i = 0 ; i < etcfs . length ; i ++ )
if ( etcfs [ i ] . match ( /\S/ ) )
filesystems [ etcfs [ i ] . trim ( ) ] = true ;
filesystems = Object . keys ( filesystems ) . sort ( ) ;
2019-09-21 16:43:42 +00:00
if ( ! uci . sections ( 'fstab' , 'global' ) . length )
uci . add ( 'fstab' , 'global' ) ;
2019-09-21 16:36:31 +00:00
m = new form . Map ( 'fstab' , _ ( 'Mount Points' ) ) ;
s = m . section ( form . TypedSection , 'global' , _ ( 'Global Settings' ) ) ;
s . addremove = false ;
s . anonymous = true ;
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' ) ) ;
o . onclick = this . handleDetect . bind ( this , m ) ;
o . inputstyle = 'reload' ;
o = s . option ( form . Button , '_mountall' , _ ( 'Mount attached devices' ) , _ ( 'Attempt to enable configured mount points for attached devices' ) ) ;
o . onclick = this . handleMountAll . bind ( this , m ) ;
o . inputstyle = 'reload' ;
o = s . option ( form . Flag , 'anon_swap' , _ ( 'Anonymous Swap' ) , _ ( 'Mount swap not specifically configured' ) ) ;
o . default = o . disabled ;
o . rmempty = false ;
o = s . option ( form . Flag , 'anon_mount' , _ ( 'Anonymous Mount' ) , _ ( 'Mount filesystems not specifically configured' ) ) ;
o . default = o . disabled ;
o . rmempty = false ;
o = s . option ( form . Flag , 'auto_swap' , _ ( 'Automount Swap' ) , _ ( 'Automatically mount swap on hotplug' ) ) ;
o . default = o . enabled ;
o . rmempty = false ;
o = s . option ( form . Flag , 'auto_mount' , _ ( 'Automount Filesystem' ) , _ ( 'Automatically mount filesystems on hotplug' ) ) ;
o . default = o . enabled ;
o . rmempty = false ;
o = s . option ( form . Flag , 'check_fs' , _ ( 'Check filesystems before mount' ) , _ ( 'Automatically check filesystem for errors before mounting' ) ) ;
o . default = o . disabled ;
o . rmempty = false ;
// Mount status table
o = s . option ( form . DummyValue , '_mtab' ) ;
o . load = function ( section _id ) {
return callMountPoints ( ) . then ( L . bind ( function ( mounts ) {
this . mounts = mounts ;
} , this ) ) ;
} ;
o . render = L . bind ( function ( view , section _id ) {
2020-11-27 19:54:18 +00:00
var table = E ( 'table' , { 'class' : 'table' } , [
E ( 'tr' , { 'class' : 'tr table-titles' } , [
E ( 'th' , { 'class' : 'th' } , _ ( 'Filesystem' ) ) ,
E ( 'th' , { 'class' : 'th' } , _ ( 'Mount Point' ) ) ,
E ( 'th' , { 'class' : 'th center' } , _ ( 'Available' ) ) ,
E ( 'th' , { 'class' : 'th center' } , _ ( 'Used' ) ) ,
E ( 'th' , { 'class' : 'th' } , _ ( 'Unmount' ) )
2019-09-21 16:36:31 +00:00
] )
] ) ;
var rows = [ ] ;
for ( var i = 0 ; i < this . mounts . length ; i ++ ) {
var used = this . mounts [ i ] . size - this . mounts [ i ] . free ,
umount = true ;
if ( /^\/(overlay|rom|tmp(?:\/.+)?|dev(?:\/.+)?|)$/ . test ( this . mounts [ i ] . mount ) )
umount = false ;
rows . push ( [
this . mounts [ i ] . device ,
this . mounts [ i ] . mount ,
'%1024.2mB / %1024.2mB' . format ( this . mounts [ i ] . avail , this . mounts [ i ] . size ) ,
'%.2f%% (%1024.2mB)' . format ( 100 / this . mounts [ i ] . size * used , used ) ,
umount ? E ( 'button' , {
'class' : 'btn cbi-button-remove' ,
2020-04-16 13:35:51 +00:00
'click' : ui . createHandlerFn ( view , 'handleUmount' , m , this . mounts [ i ] . mount ) ,
'disabled' : this . map . readonly || null
2019-09-21 16:36:31 +00:00
} , [ _ ( 'Unmount' ) ] ) : '-'
] ) ;
}
cbi _update _table ( table , rows , E ( 'em' , _ ( 'Unable to obtain mount information' ) ) ) ;
return E ( [ ] , [ E ( 'h3' , _ ( 'Mounted file systems' ) ) , table ] ) ;
} , o , this ) ;
// Mounts
s = m . section ( form . GridSection , 'mount' , _ ( 'Mount Points' ) , _ ( 'Mount Points define at which point a memory device will be attached to the filesystem' ) ) ;
s . modaltitle = _ ( 'Mount Points - Mount Entry' ) ;
s . anonymous = true ;
s . addremove = true ;
s . sortable = true ;
s . devices = { } ;
s . renderHeaderRows = function ( /* ... */ ) {
var trEls = form . GridSection . prototype . renderHeaderRows . apply ( this , arguments ) ;
return trEls . childNodes [ 0 ] ;
}
s . tab ( 'general' , _ ( 'General Settings' ) ) ;
s . tab ( 'advanced' , _ ( 'Advanced Settings' ) ) ;
o = s . taboption ( 'general' , form . Flag , 'enabled' , _ ( 'Enabled' ) ) ;
o . rmempty = false ;
2022-04-12 11:01:14 +00:00
o . default = true ;
2019-09-21 16:36:31 +00:00
o . editable = true ;
o = s . taboption ( 'general' , form . DummyValue , '_device' , _ ( 'Device' ) ) ;
o . rawhtml = true ;
o . modalonly = false ;
o . write = function ( ) { } ;
o . remove = function ( ) { } ;
o . textvalue = device _textvalue . bind ( o , devices ) ;
o = s . taboption ( 'general' , form . Value , 'uuid' , _ ( 'UUID' ) , _ ( 'If specified, mount the device by its UUID instead of a fixed device node' ) ) ;
o . modalonly = true ;
o . value ( '' , _ ( '-- match by uuid --' ) ) ;
var devs = Object . keys ( devices ) . sort ( ) ;
for ( var i = 0 ; i < devs . length ; i ++ ) {
var dev = devices [ devs [ i ] ] ;
if ( dev . uuid && dev . size )
o . value ( dev . uuid , '%s (%s, %1024.2mB)' . format ( dev . uuid , dev . dev , dev . size ) ) ;
else if ( dev . uuid )
o . value ( dev . uuid , '%s (%s)' . format ( dev . uuid , dev . dev ) ) ;
}
o = s . taboption ( 'general' , form . Value , 'label' , _ ( 'Label' ) , _ ( 'If specified, mount the device by the partition label instead of a fixed device node' ) ) ;
o . modalonly = true ;
o . depends ( 'uuid' , '' ) ;
o . value ( '' , _ ( '-- match by label --' ) ) ;
for ( var i = 0 ; i < devs . length ; i ++ ) {
var dev = devices [ devs [ i ] ] ;
if ( dev . label && dev . size )
o . value ( dev . label , '%s (%s, %1024.2mB)' . format ( dev . label , dev . dev , dev . size ) ) ;
else if ( dev . label )
o . value ( dev . label , '%s (%s)' . format ( dev . label , dev . dev ) ) ;
}
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>)' ) ) ;
o . modalonly = true ;
o . depends ( { uuid : '' , label : '' } ) ;
for ( var i = 0 ; i < devs . length ; i ++ ) {
var dev = devices [ devs [ i ] ] ;
if ( dev . size )
o . value ( dev . dev , '%s (%1024.2mB)' . format ( dev . dev , dev . size ) ) ;
else
o . value ( dev . dev ) ;
}
o = s . taboption ( 'general' , form . Value , 'target' , _ ( 'Mount point' ) , _ ( 'Specifies the directory the device is attached to' ) ) ;
o . value ( '/' , _ ( 'Use as root filesystem (/)' ) ) ;
o . value ( '/overlay' , _ ( 'Use as external overlay (/overlay)' ) ) ;
o . rmempty = false ;
o = s . taboption ( 'general' , form . DummyValue , '__notice' , _ ( 'Root preparation' ) ) ;
o . depends ( 'target' , '/' ) ;
o . modalonly = true ;
o . rawhtml = true ;
o . default = '' +
'<p>%s</p>' . format ( _ ( 'Make sure to clone the root filesystem using something like the commands below:' ) ) +
'<pre>' +
'mkdir -p /tmp/introot\n' +
'mkdir -p /tmp/extroot\n' +
'mount --bind / /tmp/introot\n' +
'mount /dev/sda1 /tmp/extroot\n' +
'tar -C /tmp/introot -cvf - . | tar -C /tmp/extroot -xf -\n' +
'umount /tmp/introot\n' +
'umount /tmp/extroot\n' +
'</pre>'
;
o = s . taboption ( 'advanced' , form . ListValue , 'fstype' , _ ( 'Filesystem' ) ) ;
o . textvalue = function ( section _id ) {
var dev = this . section . devices [ section _id ] ,
text = this . cfgvalue ( section _id ) || 'auto' ;
if ( dev && dev . type && dev . type != text )
text += ' (%s)' . format ( dev . type ) ;
return text ;
} ;
o . value ( '' , 'auto' ) ;
for ( var i = 0 ; i < filesystems . length ; i ++ )
o . value ( filesystems [ i ] ) ;
o = s . taboption ( 'advanced' , form . Value , 'options' , _ ( 'Mount options' ) , _ ( 'See "mount" manpage for details' ) ) ;
o . textvalue = function ( section _id ) { return this . cfgvalue ( section _id ) || 'defaults' } ;
o . placeholder = 'defaults' ;
s . taboption ( 'advanced' , form . Flag , 'enabled_fsck' , _ ( 'Run filesystem check' ) , _ ( 'Run a filesystem check before mounting the device' ) ) ;
// Swaps
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>.' ) ) ;
s . modaltitle = _ ( 'Mount Points - Swap Entry' ) ;
s . anonymous = true ;
s . addremove = true ;
s . sortable = true ;
s . devices = { } ;
s . renderHeaderRows = function ( /* ... */ ) {
var trEls = form . GridSection . prototype . renderHeaderRows . apply ( this , arguments ) ;
trEls . childNodes [ 0 ] . childNodes [ 1 ] . style . width = '90%' ;
return trEls . childNodes [ 0 ] ;
}
o = s . option ( form . Flag , 'enabled' , _ ( 'Enabled' ) ) ;
o . rmempty = false ;
o . editable = true ;
o = s . option ( form . DummyValue , '_device' , _ ( 'Device' ) ) ;
o . modalonly = false ;
o . textvalue = device _textvalue . bind ( o , devices ) ;
o = s . option ( form . Value , 'uuid' , _ ( 'UUID' ) , _ ( 'If specified, mount the device by its UUID instead of a fixed device node' ) ) ;
o . modalonly = true ;
o . value ( '' , _ ( '-- match by uuid --' ) ) ;
var devs = Object . keys ( devices ) . sort ( ) ;
for ( var i = 0 ; i < devs . length ; i ++ ) {
var dev = devices [ devs [ i ] ] ;
if ( dev . dev . match ( /^\/dev\/(mtdblock|ubi|ubiblock)\d/ ) )
continue ;
if ( dev . uuid && dev . size )
o . value ( dev . uuid , '%s (%s, %1024.2mB)' . format ( dev . uuid , dev . dev , dev . size ) ) ;
else if ( dev . uuid )
o . value ( dev . uuid , '%s (%s)' . format ( dev . uuid , dev . dev ) ) ;
}
o = s . option ( form . Value , 'label' , _ ( 'Label' ) , _ ( 'If specified, mount the device by the partition label instead of a fixed device node' ) ) ;
o . modalonly = true ;
o . depends ( 'uuid' , '' ) ;
o . value ( '' , _ ( '-- match by label --' ) ) ;
for ( var i = 0 ; i < devs . length ; i ++ ) {
var dev = devices [ devs [ i ] ] ;
if ( dev . dev . match ( /^\/dev\/(mtdblock|ubi|ubiblock)\d/ ) )
continue ;
if ( dev . label && dev . size )
o . value ( dev . label , '%s (%s, %1024.2mB)' . format ( dev . label , dev . dev , dev . size ) ) ;
else if ( dev . label )
o . value ( dev . label , '%s (%s)' . format ( dev . label , dev . dev ) ) ;
}
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>)' ) ) ;
o . modalonly = true ;
o . depends ( { uuid : '' , label : '' } ) ;
for ( var i = 0 ; i < devs . length ; i ++ ) {
var dev = devices [ devs [ i ] ] ;
if ( dev . dev . match ( /^\/dev\/(mtdblock|ubi|ubiblock)\d/ ) )
continue ;
if ( dev . size )
o . value ( dev . dev , '%s (%1024.2mB)' . format ( dev . dev , dev . size ) ) ;
else
o . value ( dev . dev ) ;
}
return m . render ( ) ;
}
} ) ;