Skip to content

Commit 1fe15ef

Browse files
committed
Split code in several files for easier maintenance and testing
1 parent 7f9035c commit 1fe15ef

File tree

4 files changed

+458
-462
lines changed

4 files changed

+458
-462
lines changed

lib/index.js

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
const fs = require('fs')
2+
const resolve = require('path').resolve
3+
4+
const each = require('async/each')
5+
const jocker = require('jocker')
6+
const prompt = require('prompt')
7+
const rimraf = require('rimraf').sync
8+
const utils = require('nodeos-mount-utils')
9+
10+
const jocker_root = require('./jocker_root')
11+
12+
const flags = utils.flags
13+
const MS_NODEV = flags.MS_NODEV
14+
const MS_NOSUID = flags.MS_NOSUID
15+
16+
17+
const HOME = '/tmp'
18+
19+
20+
/**
21+
* This error handler traces the error and starts a node.js repl
22+
* @access private
23+
* @param {Error} error The error that gets traced
24+
*/
25+
function onerror(error)
26+
{
27+
console.trace(error)
28+
utils.startRepl('NodeOS-mount-filesystems')
29+
}
30+
31+
/**
32+
* Filter folders that are valid user `$HOME`
33+
* @access private
34+
* @param {String} user The name of the user
35+
* @return {Boolean} Returns true If the first char is not a dot
36+
* and not `root` and not ´lost+found´
37+
*/
38+
function filterUser(user)
39+
{
40+
return user[0] !== '.' && user !== 'root' && user !== 'lost+found'
41+
}
42+
43+
/**
44+
* This helper waits with a limit of tries until the path exists
45+
* @access private
46+
* @param {String} path The path to check for
47+
* @param {Number} tries A limit of tries
48+
* @param {Function} callback The callback function
49+
* @return {Function} Returns the callback with either nothing
50+
* or with a error
51+
*/
52+
function waitUntilExists(path, tries, callback)
53+
{
54+
fs.exists(path, function(exists)
55+
{
56+
if(exists) return callback()
57+
58+
if(tries-- <= 0) return callback(new Error(path+' not exists'))
59+
60+
setTimeout(waitUntilExists, 1000, path, tries, callback)
61+
})
62+
}
63+
64+
/**
65+
* Starts a prompt and asks for the location of the userfs
66+
* @access private
67+
* @param {Error} error The error will be printed in the console
68+
*/
69+
function askLocation(error)
70+
{
71+
console.warn('Could not find userfs:', error)
72+
73+
var self = this
74+
75+
prompt.start()
76+
prompt.get('path to userfs', function(error, result)
77+
{
78+
if(error) console.warn(error)
79+
80+
self.root = result['path to userfs']
81+
return mountUsersFS(self)
82+
})
83+
}
84+
85+
/**
86+
* If the `single` key is set in the cmdline it starts a admin repl
87+
* If not it just overlays the users filesystem
88+
* @access private
89+
* @param {String} usersFolder The path to folder of the users
90+
*/
91+
function adminOrUsers(usersFolder)
92+
{
93+
function done(error)
94+
{
95+
// Remove the modules from initramfs to free memory
96+
// rimraf('/lib/node_modules')
97+
rimraf('/lib/node_modules/jocker')
98+
99+
// Make '/usr' a opaque folder (OverlayFS feature)
100+
rimraf('/usr')
101+
102+
if(error) onerror(error)
103+
}
104+
105+
// Mount users directories and exec their init files
106+
fs.readdir(usersFolder, function(error, users)
107+
{
108+
if(error) return done(error)
109+
110+
each(users.filter(filterUser), function(username, callback)
111+
{
112+
jocker.run(usersFolder+'/'+username, '/init', {PATH: '/bin'}, callback)
113+
},
114+
done)
115+
})
116+
}
117+
118+
/**
119+
* Prepares the session and checks if the users filesystem has a root account,
120+
* if not check if `/proc/cmdline` has the single key
121+
* It deletes the `root`, `rootfstype` and `vga` environment variables
122+
* and adds `NODE_PATH` to it.
123+
* @access private
124+
* @return {Repl} Returns either a repl or a error if the error contains
125+
* a `ENOENT` code
126+
*/
127+
function prepareSessions()
128+
{
129+
// Update environment variables
130+
var env = process.env
131+
132+
delete env['root']
133+
delete env['rootfstype']
134+
delete env['vga']
135+
136+
env['NODE_PATH'] = '/lib/node_modules'
137+
138+
const upperdir = HOME+'/root'
139+
140+
// Check if users filesystem has an administrator account
141+
fs.readdir(upperdir, function(error)
142+
{
143+
if(error)
144+
{
145+
if(error.code !== 'ENOENT') return onerror(error)
146+
147+
return adminOrUsers(HOME)
148+
}
149+
150+
// There's an administrator account, prepare it first
151+
jocker_root.create(upperdir, function(error, newHome)
152+
{
153+
if(error) return onerror(error)
154+
155+
// Enter administrator mode
156+
if(exports.single) return utils.startRepl('Administrator mode')
157+
158+
// Execute `root` user init in un-priviledged environment
159+
jocker.exec(HOME, '/init', {PATH: '/bin'}, function(error)
160+
{
161+
if(error) console.warn(error)
162+
163+
adminOrUsers(newHome)
164+
})
165+
})
166+
})
167+
}
168+
169+
/**
170+
* This function mounts the userfs
171+
* if the root env variable contains `container` it prepares the session
172+
* and if there is no root env var then it awaits the user device and
173+
* then mounts the user device and then prepares the session
174+
* @access private
175+
* @param {Object} cmdline This objects holds key/value pairs from the
176+
* `/proc/cmdline` file
177+
* @return {Prompt|Error} It returns either a prompt if the
178+
* tries has reached its limit or a error
179+
* if the `mkdirMount` fails to create the user
180+
* device
181+
*/
182+
function mountUsersFS(cmdline)
183+
{
184+
// Allow to override or disable `usersDev`
185+
var usersDev = process.env.root
186+
if(usersDev === undefined) usersDev = cmdline.root
187+
188+
// Running on a container (Docker, vagga), don't mount the users filesystem
189+
if(usersDev === 'container') return prepareSessions()
190+
191+
// Running on real hardware or virtual machine, mount the users filesystem
192+
if(usersDev)
193+
waitUntilExists(usersDev, 5, function(error)
194+
{
195+
if(error) return askLocation.call(cmdline, error)
196+
197+
// Mount users filesystem
198+
var type = process.env.rootfstype || cmdline.rootfstype || 'auto'
199+
var extras = {errors: 'remount-ro', devFile: resolve(usersDev)}
200+
201+
utils.mkdirMount(HOME, type, MS_NODEV | MS_NOSUID, extras, function(error)
202+
{
203+
if(error) return onerror(error)
204+
205+
prepareSessions()
206+
})
207+
})
208+
209+
// Users filesystem is not defined, launch a Node.js REPL
210+
else
211+
fs.readFile('resources/readonly_warning.txt', 'utf8', function(error, data)
212+
{
213+
if(error) return onerror(error)
214+
215+
console.warn(data)
216+
utils.startRepl('NodeOS-mount-filesystems')
217+
})
218+
}
219+
220+
221+
exports.mountUsersFS = mountUsersFS
222+
exports.single = false

0 commit comments

Comments
 (0)