Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| /* | |
| * CDDL HEADER START | |
| * | |
| * The contents of this file are subject to the terms of the | |
| * Common Development and Distribution License, Version 1.0 only | |
| * (the "License"). You may not use this file except in compliance | |
| * with the License. | |
| * | |
| * You can obtain a copy of the license at http://smartos.org/CDDL | |
| * | |
| * See the License for the specific language governing permissions | |
| * and limitations under the License. | |
| * | |
| * When distributing Covered Code, include this CDDL HEADER in each | |
| * file. | |
| * | |
| * If applicable, add the following below this CDDL HEADER, with the | |
| * fields enclosed by brackets "[]" replaced with your own identifying | |
| * information: Portions Copyright [yyyy] [name of copyright owner] | |
| * | |
| * CDDL HEADER END | |
| * | |
| * Copyright 2020 Joyent, Inc. All rights reserved. | |
| * | |
| */ | |
| var assert = require('assert'); | |
| var fs = require('fs'); | |
| var ipaddr = require('/usr/vm/node_modules/ip'); | |
| var macaddr = require('/usr/vm/node_modules/macaddr'); | |
| var net = require('net'); | |
| var sprintf = require('/usr/node/node_modules/sprintf').sprintf; | |
| function addString(buf, str, pos) | |
| { | |
| var len = str.length; | |
| buf.write(str, pos); | |
| return (len + 1); | |
| } | |
| /* | |
| * When you need to access files inside a zoneroot, you need to be careful that | |
| * there are no symlinks in the path. Since we operate from the GZ, these | |
| * symlinks will be evaluated in the GZ context. Eg. a symlink in zone A with | |
| * /var/run -> * /zones/<uuid of zone B>/root/var/run would mean that operating | |
| * on files in zone A's /var/run would actually be touching files in zone B. | |
| * | |
| * To prevent that, only ever modify files inside the zoneroot from the GZ | |
| * *before* first boot. After the zone is booted, it's better to use services | |
| * in the zone to pull values from metadata and write out changes on next boot. | |
| * It's also safe to use zlogin when the zone is running. | |
| * | |
| * This function is intended to be used in those cases we do write things out | |
| * before the zone's first boot but the dataset might have invalid symlinks in | |
| * it even then, so we still need to confirm the paths inside zoneroot before | |
| * using them. It throws an exception if: | |
| * | |
| * - zoneroot is not an absolute path | |
| * - fs.lstatSync fails | |
| * - target path under zoneroot contains symlink | |
| * - a component leading up to the final one is not a directory | |
| * - options.type is set to 'file' and target is not a regular file | |
| * - options.type is set to 'dir' and target references a non-directory | |
| * - options.type is not one of 'file' or 'dir' | |
| * - options.enoent_ok is false and target path doesn't exist | |
| * | |
| * if none of those are the case, it returns true. | |
| */ | |
| function assertSafeZonePath(zoneroot, target, options) | |
| { | |
| var parts; | |
| var root; | |
| var stat; | |
| var test; | |
| assert((zoneroot.length > 0 && zoneroot[0] === '/'), | |
| 'zoneroot must be an absolute path, not: [' + zoneroot + ']'); | |
| parts = trim(target, '/').split('/'); | |
| root = trim(zoneroot, '/'); | |
| test = '/' + root; | |
| while (parts.length > 0) { | |
| test = test + '/' + parts.shift(); | |
| try { | |
| stat = fs.lstatSync(test); | |
| } catch (e) { | |
| if (e.code === 'ENOENT') { | |
| if (!options.hasOwnProperty('enoent_ok') | |
| || options.enoent_ok === false) { | |
| throw e; | |
| } else { | |
| // enoent is ok, return true. This is mostly used when | |
| // deleting files with rm -f <path>. It's ok for <path> to | |
| // not exist (but not ok for any component to be a symlink) | |
| // there's no point continuing though since ENOENT here | |
| // means all subpaths also won't exist. | |
| return true; | |
| } | |
| } else { | |
| throw e; | |
| } | |
| } | |
| if (stat.isSymbolicLink()) { | |
| // it's never ok to have a symlink component | |
| throw new Error(test + ' is a symlink'); | |
| } | |
| // any component other than the last also needs to be a | |
| // directory, last can also be a file. | |
| if (parts.length === 0) { | |
| // last, dir or file | |
| if (!options.hasOwnProperty('type') || options.type === 'dir') { | |
| if (!stat.isDirectory()) { | |
| throw new Error(test + ' is not a directory'); | |
| } | |
| } else if (options.type === 'file') { | |
| if (!stat.isFile()) { | |
| throw new Error(test + ' is not a file'); | |
| } | |
| } else { | |
| throw new Error('this function does not know about ' | |
| + options.type); | |
| } | |
| } else if (!stat.isDirectory()) { | |
| // not last component, only dir is acceptable | |
| throw new Error(test + ' is not a directory'); | |
| } | |
| } | |
| // if we didn't throw, this is valid. | |
| return true; | |
| } | |
| function epochTimestampSecs(str) | |
| { | |
| var secs = Number(str); | |
| var t = new Date(secs * 1000); | |
| return t.toISOString(); | |
| } | |
| function fixBoolean(str) | |
| { | |
| if (str === 'true') { | |
| return true; | |
| } else if (str === 'false') { | |
| return false; | |
| } else { | |
| return str; | |
| } | |
| } | |
| function fixBooleanLoose(str) | |
| { | |
| if (str === 'true' || str === '1' || str === 1) { | |
| return true; | |
| } else if (str === 'false' || str === '0' || str === 0) { | |
| return false; | |
| } else { | |
| return str; | |
| } | |
| } | |
| function generateMAC() | |
| { | |
| var data = [(Math.floor(Math.random() * 15) + 1).toString(16) + 2]; | |
| for (var i = 0; i < 5; i++) { | |
| var oct = (Math.floor(Math.random() * 255) + 1).toString(16); | |
| if (oct.length == 1) { | |
| oct = '0' + oct; | |
| } | |
| data.push(oct); | |
| } | |
| return data.join(':'); | |
| } | |
| function isPrivateIP(str) | |
| { | |
| if (!net.isIPv4(str)) { | |
| return false; | |
| } | |
| function inRange(start, end, prospect) { | |
| if (ipaddr.aton(start) <= ipaddr.aton(prospect) | |
| && ipaddr.aton(prospect) <= ipaddr.aton(end)) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| if (inRange('10.0.0.0', '10.255.255.255', str)) { | |
| return true; | |
| } else if (inRange('172.16.0.0', '172.31.255.255', str)) { | |
| return true; | |
| } else if (inRange('192.168.0.0', '192.168.255.255', str)) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| function isUUID(str) | |
| { | |
| var re = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/; | |
| return typeof (str) === 'string' && re.test(str); | |
| } | |
| function validAttrValue(str) | |
| { | |
| var re = /^[a-zA-Z0-9\-\.\_]+$/; | |
| // attempt to force value to string | |
| switch (typeof (str)) { | |
| case 'number': | |
| str = str.toString(); | |
| break; | |
| case 'string': | |
| break; | |
| default: | |
| return false; | |
| } | |
| if (str && str.match(re)) { | |
| return true; | |
| } else { | |
| return false; | |
| } | |
| } | |
| function validResolver(str) | |
| { | |
| return Boolean(net.isIP(str)); | |
| } | |
| function validMacAddress(str) | |
| { | |
| try { | |
| macaddr.parse(str); | |
| } catch (e) { | |
| return false; | |
| } | |
| return /^[0-9a-fA-F:]+$/.test(str); | |
| } | |
| function validCID(cid) | |
| { | |
| return /^0x[0-9a-fA-F]+$/.test(cid) | |
| && (cid.length % 2) === 0 | |
| && cid.length <= 512; | |
| } | |
| function ltrim(str, chars) | |
| { | |
| chars = chars || '\\s'; | |
| str = str || ''; | |
| return str.replace(new RegExp('^[' + chars + ']+', 'g'), ''); | |
| } | |
| function numberify(str) | |
| { | |
| return Number(str); | |
| } | |
| function rtrim(str, chars) | |
| { | |
| chars = chars || '\\s'; | |
| str = str || ''; | |
| return str.replace(new RegExp('[' + chars + ']+$', 'g'), ''); | |
| } | |
| function separateCommas(str) | |
| { | |
| return str.split(','); | |
| } | |
| function separateCommasAndNumberify(str) | |
| { | |
| var components = str.split(','); | |
| var values = []; | |
| components.forEach(function (component) { | |
| values.push(numberify(component)); | |
| }); | |
| return values; | |
| } | |
| function trim(str, chars) | |
| { | |
| return ltrim(rtrim(str, chars), chars); | |
| } | |
| function unbase64(str) | |
| { | |
| return new Buffer(str, 'base64').toString('utf-8'); | |
| } | |
| function unmangleMem(str) | |
| { | |
| return (Number(str) / (1024 * 1024)); | |
| } | |
| // return the MAC address based on a VRRP Virtual Router ID | |
| function vrrpMAC(vrid) | |
| { | |
| return sprintf('00:00:5e:00:01:%02x', vrid); | |
| } | |
| function isPowerOf2(val) | |
| { | |
| return val && !(val & (val - 1)); | |
| } | |
| function validBhyveSectSize(str) | |
| { | |
| var sizes = str.split('/'); | |
| if (sizes.length === 0 || sizes.length > 2) { | |
| return false; | |
| } | |
| var valid = sizes.filter(function validSize(x) { | |
| return /^\d+$/.test(x) && isPowerOf2(Number(x)); | |
| }); | |
| return valid.length === sizes.length; | |
| } | |
| module.exports = { | |
| addString: addString, | |
| assertSafeZonePath: assertSafeZonePath, | |
| epochTimestampSecs: epochTimestampSecs, | |
| fixBoolean: fixBoolean, | |
| fixBooleanLoose: fixBooleanLoose, | |
| generateMAC: generateMAC, | |
| isPrivateIP: isPrivateIP, | |
| isUUID: isUUID, | |
| ltrim: ltrim, | |
| numberify: numberify, | |
| rtrim: rtrim, | |
| separateCommas: separateCommas, | |
| separateCommasAndNumberify: separateCommasAndNumberify, | |
| trim: trim, | |
| unbase64: unbase64, | |
| unmangleMem: unmangleMem, | |
| validAttrValue: validAttrValue, | |
| validBhyveSectSize: validBhyveSectSize, | |
| validCID: validCID, | |
| validResolver: validResolver, | |
| validMacAddress: validMacAddress, | |
| vrrpMAC: vrrpMAC | |
| }; |