├── run.sh ├── tools ├── jsstyle.conf ├── mk │ ├── Makefile.node_deps.targ │ ├── Makefile.smf.targ │ ├── Makefile.smf.defs │ ├── Makefile.deps │ ├── Makefile.node.targ │ ├── Makefile.node_deps.defs │ ├── Makefile.defs │ ├── Makefile.node_prebuilt.targ │ ├── Makefile.node.defs │ ├── Makefile.node_prebuilt.defs │ └── Makefile.targ ├── bashstyle ├── mkrepo ├── runtests.in ├── jsl.web.conf ├── jsl.node.conf └── service_bundle.dtd.1 ├── .gitignore ├── .gitmodules ├── etc ├── template.json └── example.json ├── bin └── manta-nfs ├── svc ├── systemd │ └── mantanfs.service ├── upstart │ └── mantanfs.conf ├── launchd │ └── com.joyent.mantanfs.plist ├── rc │ └── mantanfs └── smf │ └── manta-nfs.xml ├── lib ├── nfs │ ├── mknod.js │ ├── link.js │ ├── readlink.js │ ├── symlink.js │ ├── getattr.js │ ├── commit.js │ ├── fsstat.js │ ├── pathconf.js │ ├── access.js │ ├── write.js │ ├── fsinfo.js │ ├── read.js │ ├── setattr.js │ ├── remove.js │ ├── rmdir.js │ ├── lookup.js │ ├── common.js │ ├── readdir.js │ ├── rename.js │ ├── mkdir.js │ ├── index.js │ ├── create.js │ └── readdirplus.js ├── auth.js ├── portmap.js ├── index.js └── mount.js ├── package.json ├── Makefile ├── server.js ├── LICENSE └── README.md /run.sh: -------------------------------------------------------------------------------- 1 | sudo rm -rf /var/tmp/mfsdb; sudo -E node server.js -d | bunyan 2 | -------------------------------------------------------------------------------- /tools/jsstyle.conf: -------------------------------------------------------------------------------- 1 | indent=4 2 | doxygen 3 | unparenthesized-return=1 4 | blank-after-start-comment=0 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /tmp 3 | build 4 | docs/*.json 5 | docs/*.html 6 | cscope.in.out 7 | cscope.po.out 8 | cscope.out 9 | smf/manifests/*.xml 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/jsstyle"] 2 | path = deps/jsstyle 3 | url = git://github.com/davepacheco/jsstyle.git 4 | [submodule "deps/javascriptlint"] 5 | path = deps/javascriptlint 6 | url = git://github.com/davepacheco/javascriptlint.git 7 | -------------------------------------------------------------------------------- /etc/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "manta": { 3 | "_comment": "the Manta environment variables can be used instead", 4 | "keyFile": "/Users/foo/.ssh/id_rsa", 5 | "keyId": "70:c0:50:d6:9e:1f:0c:74:04:8b:08:c9:12:a2:7c:9f", 6 | "url": "https://us-east.manta.joyent.com", 7 | "user": "foo.bar" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /bin/manta-nfs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | dir=`dirname $0` 4 | 5 | if [ "$MANTA_USER"X == "X" ]; then 6 | echo "Error: the manta environment variables must be defined" 7 | exit 1 8 | fi 9 | 10 | euid=`id -u` 11 | 12 | if [ $euid != 0 ]; then 13 | echo "Error: manta-nfs must be started as root" 14 | exit 1 15 | fi 16 | 17 | node $dir/../server.js 18 | -------------------------------------------------------------------------------- /svc/systemd/mantanfs.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Manta NFS service 3 | After=network.target 4 | ConditionFileIsExecutable=/usr/bin/node 5 | ConditionPathExists=/usr/local/bin/server.js 6 | ConditionPathExists=/usr/local/etc/manta-nfs.json 7 | 8 | [Service] 9 | Type=simple 10 | ExecStart=/usr/bin/node /usr/local/bin/server.js -f /usr/local/etc/manta-nfs.json 11 | Restart=on-failure 12 | -------------------------------------------------------------------------------- /tools/mk/Makefile.node_deps.targ: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | # 5 | # Makefile.node_deps.targ: targets for Makefile.node_deps.defs. 6 | # 7 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped 8 | # into other repos as-is without requiring any modifications. If you find 9 | # yourself changing this file, you should instead update the original copy in 10 | # eng.git and then update your repo to use the new version. 11 | # 12 | 13 | NPM_EXEC ?= $(error NPM_EXEC must be defined for Makefile.node_deps.targ) 14 | 15 | node_modules/%: src/node-% | $(NPM_EXEC) 16 | $(NPM) install $< 17 | -------------------------------------------------------------------------------- /lib/nfs/mknod.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | 9 | var common = require('./common'); 10 | 11 | 12 | 13 | ///-- API 14 | 15 | function mknod(req, res, next) { 16 | req.log.debug('mknod: entered'); 17 | res.error(nfs.NFS3ERR_NOTSUPP); 18 | next(false); 19 | } 20 | 21 | 22 | ///--- Exports 23 | 24 | module.exports = function chain() { 25 | return ([ 26 | mknod 27 | ]); 28 | }; 29 | -------------------------------------------------------------------------------- /lib/nfs/link.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | 9 | var common = require('./common'); 10 | 11 | 12 | 13 | ///-- API 14 | 15 | function link(req, res, next) { 16 | req.log.debug('link(%s): entered', req.file); 17 | res.error(nfs.NFS3ERR_NOTSUPP); 18 | next(false); 19 | } 20 | 21 | 22 | ///--- Exports 23 | 24 | module.exports = function chain() { 25 | return ([ 26 | link 27 | ]); 28 | }; 29 | -------------------------------------------------------------------------------- /lib/nfs/readlink.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | 9 | var common = require('./common'); 10 | 11 | 12 | 13 | ///-- API 14 | 15 | function readlink(req, res, next) { 16 | req.log.debug('readlink: entered'); 17 | res.error(nfs.NFS3ERR_NOTSUPP); 18 | next(false); 19 | } 20 | 21 | 22 | ///--- Exports 23 | 24 | module.exports = function chain() { 25 | return ([ 26 | readlink 27 | ]); 28 | }; 29 | -------------------------------------------------------------------------------- /lib/auth.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | 8 | 9 | ///--- API 10 | 11 | function authorize(req, res, next) { 12 | // Let everything through 13 | // if (!req.is_user(0)) { 14 | // res.status = nfs.NFS3ERR_ACCES; 15 | // res.send(); 16 | // next(false); 17 | // } else { 18 | // next(); 19 | // } 20 | next(); 21 | } 22 | 23 | 24 | 25 | ///--- Exports 26 | 27 | module.exports = { 28 | authorize: authorize 29 | }; 30 | -------------------------------------------------------------------------------- /lib/nfs/symlink.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | 9 | var common = require('./common'); 10 | 11 | 12 | 13 | ///-- API 14 | 15 | function symlink(req, res, next) { 16 | req.log.debug('symlink(%s, %s): entered', req.where.dir, req.where.name); 17 | res.error(nfs.NFS3ERR_NOTSUPP); 18 | next(false); 19 | } 20 | 21 | 22 | ///--- Exports 23 | 24 | module.exports = function chain() { 25 | return ([ 26 | symlink 27 | ]); 28 | }; 29 | -------------------------------------------------------------------------------- /svc/upstart/mantanfs.conf: -------------------------------------------------------------------------------- 1 | # mantanfs - Manta NFS server 2 | # 3 | # The mantanfs server provides a NFS server frontend to Manta. 4 | 5 | description "Manta NFS server" 6 | 7 | start on runlevel [2345] 8 | stop on runlevel [!2345] 9 | 10 | respawn 11 | respawn limit 10 5 12 | 13 | console log 14 | 15 | # Assumes node is installed in /usr/bin 16 | # Assumes server.js is installed in /usr/local/bin 17 | # Adjust these paths as needed 18 | 19 | pre-start script 20 | test -x /usr/bin/node || { stop; exit 0; } 21 | test -e /usr/local/bin/server.js || { stop; exit 0; } 22 | test -e /usr/local/etc/manta-nfs.json || { stop; exit 0; } 23 | end script 24 | 25 | exec node /usr/local/bin/server.js -f /usr/local/etc/manta-nfs.json 26 | -------------------------------------------------------------------------------- /tools/mk/Makefile.smf.targ: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | # 5 | # Makefile.smf.targ: see Makefile.smf.defs. 6 | # 7 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped 8 | # into other repos as-is without requiring any modifications. If you find 9 | # yourself changing this file, you should instead update the original copy in 10 | # eng.git and then update your repo to use the new version. 11 | # 12 | .PHONY: check-manifests 13 | check-manifests: $(SMF_MANIFESTS:%=%.smfchk) 14 | 15 | %.smfchk: % 16 | $(XMLLINT) --path $(dir $(SMF_DTD)) --dtdvalid $(SMF_DTD) $^ 17 | 18 | check: check-manifests 19 | 20 | $(SMF_MANIFESTS): %: %.in 21 | $(SED) -e 's#@@NODE@@#@@PREFIX@@/$(NODE_INSTALL)/bin/node#' $< > $@ 22 | -------------------------------------------------------------------------------- /svc/launchd/com.joyent.mantanfs.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.joyent.mantanfs 7 | ProgramArguments 8 | 9 | /usr/local/bin/node 10 | /usr/local/manta-nfs/server.js 11 | -f 12 | /usr/local/manta-nfs/etc/manta-nfs.json 13 | 14 | StandardOutPath 15 | /var/log/manta-nfs.log 16 | StandardErrorPath 17 | /var/log/manta-nfs.log 18 | KeepAlive 19 | 20 | NetworkState 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /lib/nfs/getattr.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | 9 | var common = require('./common'); 10 | 11 | 12 | 13 | ///-- API 14 | 15 | function getattr(req, res, next) { 16 | var log = req.log; 17 | 18 | log.debug('getattr(%s, %s): entered', req.object, req._filename); 19 | req.fs.stat(req._filename, function (err, stats) { 20 | if (err) { 21 | req.log.warn(err, 'getattr: mantafs.stat failed'); 22 | res.error(nfs.NFS3ERR_IO); 23 | next(false); 24 | return; 25 | } 26 | 27 | log.debug('getattr(%j): stats returned from cache', stats); 28 | 29 | res.setAttributes(stats); 30 | res.send(); 31 | next(); 32 | }); 33 | } 34 | 35 | 36 | 37 | ///--- Exports 38 | 39 | module.exports = function chain() { 40 | return ([ 41 | common.fhandle_to_filename, 42 | getattr 43 | ]); 44 | }; 45 | -------------------------------------------------------------------------------- /lib/nfs/commit.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var assert = require('assert-plus'); 8 | var nfs = require('nfs'); 9 | 10 | var common = require('./common'); 11 | 12 | var fs = require('fs'); 13 | 14 | 15 | 16 | ///-- API 17 | 18 | 19 | function commit(call, reply, next) { 20 | var log = call.log; 21 | var stats = call.stats; 22 | 23 | log.debug('commit(%s): entered', call.object); 24 | 25 | assert.ok(stats); 26 | 27 | call.fs.fsync(stats.fd, function (err) { 28 | if (err) { 29 | log.warn(err, 'commit: fsCache.fsync failed'); 30 | reply.error(nfs.NFS3ERR_SERVERFAULT); 31 | next(false); 32 | return; 33 | } 34 | 35 | reply.send(); 36 | next(); 37 | }); 38 | } 39 | 40 | 41 | 42 | ///--- Exports 43 | 44 | module.exports = function chain() { 45 | return ([ 46 | common.fhandle_to_filename, 47 | common.open, 48 | commit 49 | ]); 50 | }; 51 | -------------------------------------------------------------------------------- /tools/mk/Makefile.smf.defs: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | # 5 | # Makefile.smf.defs: common targets for SMF manifests 6 | # 7 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped 8 | # into other repos as-is without requiring any modifications. If you find 9 | # yourself changing this file, you should instead update the original copy in 10 | # eng.git and then update your repo to use the new version. 11 | # 12 | # This Makefile uses the following definitions: 13 | # 14 | # SMF_MANIFESTS_IN Source files for SMF manifests. The following 15 | # substitutions will be made on these files: 16 | # 17 | # @@NODE@@ path to installed node 18 | # 19 | # It updates SMF_MANIFESTS with the set of files generated by SMF_MANIFESTS_IN. 20 | # It also updates the "check" target to check the XML syntax of all manifests, 21 | # generated or otherwise. 22 | # 23 | # To use this file, be sure to also include Makefile.smf.targ after defining 24 | # targets. 25 | # 26 | 27 | SED ?= sed 28 | SMF_DTD ?= tools/service_bundle.dtd.1 29 | XMLLINT ?= xmllint --noout 30 | 31 | SMF_MANIFESTS += $(SMF_MANIFESTS_IN:%.in=%) 32 | CLEAN_FILES += $(SMF_MANIFESTS_IN:%.in=%) 33 | -------------------------------------------------------------------------------- /tools/mk/Makefile.deps: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | # 5 | # Makefile.deps: Makefile for including common tools as dependencies 6 | # 7 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped 8 | # into other repos as-is without requiring any modifications. If you find 9 | # yourself changing this file, you should instead update the original copy in 10 | # eng.git and then update your repo to use the new version. 11 | # 12 | # This file is separate from Makefile.targ so that teams can choose 13 | # independently whether to use the common targets in Makefile.targ and the 14 | # common tools here. 15 | # 16 | 17 | # 18 | # javascriptlint 19 | # 20 | JSL_EXEC ?= deps/javascriptlint/build/install/jsl 21 | JSL ?= $(JSL_EXEC) 22 | 23 | $(JSL_EXEC): | deps/javascriptlint/.git 24 | cd deps/javascriptlint && make install 25 | 26 | distclean:: 27 | if [[ -f deps/javascriptlint/Makefile ]]; then \ 28 | cd deps/javascriptlint && make clean; \ 29 | fi 30 | 31 | # 32 | # jsstyle 33 | # 34 | JSSTYLE_EXEC ?= deps/jsstyle/jsstyle 35 | JSSTYLE ?= $(JSSTYLE_EXEC) 36 | 37 | $(JSSTYLE_EXEC): | deps/jsstyle/.git 38 | 39 | # 40 | # restdown 41 | # 42 | RESTDOWN_EXEC ?= deps/restdown/bin/restdown 43 | RESTDOWN ?= python $(RESTDOWN_EXEC) 44 | $(RESTDOWN_EXEC): | deps/restdown/.git 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "manta-nfs", 3 | "description": "NFS Gateway for the Joyent Manta Storage Service", 4 | "version": "0.1.0", 5 | "author": { 6 | "name": "Joyent, Inc.", 7 | "url": "http://joyent.com/" 8 | }, 9 | "license": "MPL-2.0", 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/joyent/manta-nfs.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/joyent/manta-nfs/issues" 16 | }, 17 | "dependencies": { 18 | "assert-plus": "0.1.5", 19 | "bunyan": "0.22.0", 20 | "clone": "0.1.11", 21 | "dashdash": "1.3.2", 22 | "lru-cache": "2.5.0", 23 | "lstream": "0.0.3", 24 | "manta": "1.2.6", 25 | "mantafs": "0.1.0", 26 | "mkdirp": "0.3.5", 27 | "nfs": "0.1.0", 28 | "once": "1.2.0", 29 | "oncrpc": "0.1.0", 30 | "statvfs": "2.1.0", 31 | "userid": "0.1.1", 32 | "vasync": "1.4.0" 33 | }, 34 | "devDependencies": { 35 | "nodeunit": "0.8.2", 36 | "nodeunit-plus": "0.0.1", 37 | "rimraf": "2.2.2" 38 | }, 39 | "scripts": { 40 | "start:": "node ./server.js | ./node_modules/.bin/bunyan", 41 | "test": "nodeunit test/*.test.js" 42 | }, 43 | "engines": { 44 | "node" : ">=0.10.18 <0.12" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tools/mk/Makefile.node.targ: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | # 5 | # Makefile.node.targ: See Makefile.node.defs. 6 | # 7 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped 8 | # into other repos as-is without requiring any modifications. If you find 9 | # yourself changing this file, you should instead update the original copy in 10 | # eng.git and then update your repo to use the new version. 11 | # 12 | 13 | ifneq ($(shell uname -s),SunOS) 14 | NODE_PREBUILT_VERSION ?= $(error You must define NODE_PREBUILT_VERSION to use Makefile.node.targ on non-SunOS) 15 | endif 16 | 17 | ifeq ($(shell uname -s),SunOS) 18 | $(NODE_EXEC) $(NPM_EXEC) $(NODE_WAF_EXEC): | deps/node/.git 19 | (cd deps/node; ./configure $(NODE_CONFIG_FLAGS) && $(MAKE) && $(MAKE) install) 20 | else 21 | $(NODE_EXEC) $(NPM_EXEC) $(NODE_WAF_EXEC): 22 | (mkdir -p $(BUILD) \ 23 | && cd $(BUILD) \ 24 | && [[ -d src-node ]] && (cd src-node && git checkout master && git pull) || git clone git://github.com/joyent/node.git src-node \ 25 | && cd src-node \ 26 | && git checkout $(NODE_PREBUILT_VERSION) \ 27 | && ./configure $(NODE_CONFIG_FLAGS) \ 28 | && $(MAKE) && $(MAKE) install) 29 | endif 30 | 31 | DISTCLEAN_FILES += $(NODE_INSTALL) $(BUILD)/src-node 32 | 33 | distclean:: 34 | -([[ ! -d deps/node ]] || (cd deps/node && $(MAKE) distclean)) 35 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014, Joyent, Inc. All rights reserved. 3 | # 4 | # Makefile: for the manta nfs service 5 | # 6 | 7 | # 8 | # Tools 9 | # 10 | NODEUNIT := ./node_modules/.bin/nodeunit 11 | 12 | # 13 | # Files 14 | # 15 | DOC_FILES = index.restdown 16 | JS_FILES := $(shell ls *.js) $(shell find lib test -name '*.js') 17 | JSON_FILES = package.json 18 | JSL_CONF_NODE = tools/jsl.node.conf 19 | JSL_FILES_NODE = $(JS_FILES) 20 | JSSTYLE_FILES = $(JS_FILES) 21 | JSSTYLE_FLAGS = -f tools/jsstyle.conf 22 | 23 | NODE_PREBUILT_VERSION=v0.10.21 24 | 25 | ifeq ($(shell uname -s),SunOS) 26 | NODE_PREBUILT_CC_VERSION=4.6.2 27 | NODE_PREBUILT_TAG=zone 28 | endif 29 | 30 | include ./tools/mk/Makefile.defs 31 | #ifeq ($(shell uname -s),SunOS) 32 | # include ./tools/mk/Makefile.node_prebuilt.defs 33 | #else 34 | NPM := npm 35 | #endif 36 | include ./tools/mk/Makefile.smf.defs 37 | 38 | # 39 | # Repo-specific targets 40 | # 41 | .PHONY: all 42 | all: $(NODEUNIT) $(REPO_DEPS) 43 | $(NPM) rebuild 44 | 45 | $(NODEUNIT): | $(NPM_EXEC) 46 | $(NPM) install 47 | 48 | CLEAN_FILES += ./node_modules 49 | 50 | .PHONY: test 51 | test: $(NODEUNIT) 52 | 53 | # XXX write new tests 54 | # $(NODEUNIT) test/*.test.js 55 | 56 | include ./tools/mk/Makefile.deps 57 | ifeq ($(shell uname -s),SunOS) 58 | include ./tools/mk/Makefile.node_prebuilt.targ 59 | endif 60 | include ./tools/mk/Makefile.smf.targ 61 | include ./tools/mk/Makefile.targ 62 | -------------------------------------------------------------------------------- /lib/nfs/fsstat.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | var statvfs = require('statvfs'); 9 | 10 | var common = require('./common'); 11 | 12 | 13 | 14 | ///--- API 15 | 16 | // Stolen from: http://goo.gl/fBLulQ (IBM) 17 | function fsstat(call, reply, next) { 18 | var log = call.log; 19 | 20 | log.debug('fsstat(%s): entered', call.cachepath); 21 | 22 | statvfs(call.cachepath, function (err, stats) { 23 | if (err) { 24 | log.warn(err, 'fs_stat: statvfs failed'); 25 | reply.error(nfs.NFS3ERR_IO); 26 | next(false); 27 | } else { 28 | reply.tbytes = stats.blocks * stats.bsize; 29 | reply.fbytes = stats.bfree * stats.bsize; 30 | reply.abytes = stats.bavail * stats.bsize; 31 | reply.tfiles = stats.files; 32 | reply.ffiles = stats.ffree; 33 | reply.afiles = stats.favail; 34 | reply.invarsec = 0; 35 | 36 | log.debug('fsstat(%s): done', call.cachepath); 37 | reply.send(); 38 | next(); 39 | } 40 | }); 41 | } 42 | 43 | 44 | 45 | ///--- Exports 46 | 47 | module.exports = function chain() { 48 | return ([ 49 | fsstat 50 | ]); 51 | }; 52 | -------------------------------------------------------------------------------- /lib/nfs/pathconf.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | 9 | var common = require('./common'); 10 | 11 | 12 | 13 | ///--- API 14 | 15 | // Stolen from: http://goo.gl/fBLulQ (IBM) 16 | function pathconf(call, reply, next) { 17 | var log = call.log; 18 | 19 | log.debug('pathconf(%s): entered', call.object); 20 | 21 | call.fs.stat(call._filename, function (err, stats) { 22 | if (err) { 23 | log.debug(err, 'pathconf(%s): stat failed', call._filename); 24 | reply.error(nfs.NFS3ERR_STALE); 25 | reply.send(); 26 | next(false); 27 | } else { 28 | reply.setAttributes(stats); 29 | 30 | reply.linkmax = 0; 31 | reply.name_max = 1024; 32 | reply.no_trunc = true; 33 | reply.chown_restricted = true; 34 | reply.case_insensitive = false; 35 | reply.case_preserving = true; 36 | 37 | log.debug('pathconf(%s): done', call.object); 38 | reply.send(); 39 | next(); 40 | } 41 | }); 42 | } 43 | 44 | 45 | 46 | ///--- Exports 47 | 48 | module.exports = function chain() { 49 | return ([ 50 | common.fhandle_to_filename, 51 | pathconf 52 | ]); 53 | }; 54 | -------------------------------------------------------------------------------- /lib/nfs/access.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | 9 | var common = require('./common'); 10 | 11 | 12 | 13 | ///-- API 14 | 15 | function access_getattr(req, res, next) { 16 | var log = req.log; 17 | 18 | log.debug('access_getattr(%s, %s): entered', req.object, req._filename); 19 | req.fs.stat(req._filename, function (err, stats) { 20 | if (err) { 21 | req.log.warn(err, 'access_getattr: mantafs.stat failed'); 22 | res.error(nfs.NFS3ERR_IO); 23 | next(false); 24 | return; 25 | } 26 | 27 | log.debug('access_getattr(%j): stats returned from cache', stats); 28 | 29 | res.setAttributes(stats); 30 | next(); 31 | }); 32 | } 33 | 34 | 35 | function access(call, reply, next) { 36 | reply.access = 37 | nfs.ACCESS3_READ | 38 | nfs.ACCESS3_LOOKUP | 39 | nfs.ACCESS3_MODIFY | 40 | nfs.ACCESS3_EXTEND | 41 | nfs.ACCESS3_DELETE | 42 | nfs.ACCESS3_EXECUTE; 43 | reply.send(); 44 | next(); 45 | } 46 | 47 | 48 | 49 | ///--- Exports 50 | 51 | module.exports = function chain() { 52 | return ([ 53 | common.fhandle_to_filename, 54 | access_getattr, 55 | access 56 | ]); 57 | }; 58 | -------------------------------------------------------------------------------- /lib/nfs/write.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var assert = require('assert-plus'); 8 | var nfs = require('nfs'); 9 | 10 | var common = require('./common'); 11 | 12 | var fs = require('fs'); 13 | 14 | 15 | ///-- API 16 | 17 | function write(call, reply, next) { 18 | var c = call; 19 | var log = call.log; 20 | var stats = call.stats; 21 | 22 | log.debug('write(%s, %d, %d): entered', c.object, c.offset, c.count); 23 | 24 | assert.ok(stats); 25 | 26 | c.fs.write(stats.fd, c.data, 0, c.count, c.offset, function (err, n, b) { 27 | if (err) { 28 | log.warn(err, 'write: failed'); 29 | reply.error(nfs.NFS3ERR_SERVERFAULT); 30 | next(false); 31 | return; 32 | } 33 | 34 | reply.count = n; 35 | reply.committed = nfs.stable_how.FILE_SYNC; 36 | reply.send(); 37 | 38 | // This is a cache reference, so just updating it here will be enough 39 | if (c.offset + n > stats.size) 40 | stats.size = c.offset + n; 41 | 42 | log.debug('write(%d): done', n); 43 | next(); 44 | }); 45 | } 46 | 47 | 48 | 49 | ///--- Exports 50 | 51 | module.exports = function chain() { 52 | return ([ 53 | common.fhandle_to_filename, 54 | common.open, 55 | write 56 | ]); 57 | }; 58 | -------------------------------------------------------------------------------- /tools/mk/Makefile.node_deps.defs: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | # 5 | # Makefile.node_deps.defs: Makefile for including npm modules whose sources 6 | # reside inside the repo. This should NOT be used for modules in the npm 7 | # public repo or modules that could be specified with git SHAs. 8 | # 9 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped 10 | # into other repos as-is without requiring any modifications. If you find 11 | # yourself changing this file, you should instead update the original copy in 12 | # eng.git and then update your repo to use the new version. 13 | # 14 | 15 | # 16 | # This Makefile takes as input the following make variable: 17 | # 18 | # REPO_MODULES List of relative paths to node modules (i.e., npm 19 | # packages) inside this repo. For example: 20 | # src/node-canative, where there's a binary npm package 21 | # in src/node-canative. 22 | # 23 | # Based on the above, this Makefile defines the following new variables: 24 | # 25 | # REPO_DEPS List of relative paths to the installed modules. For 26 | # example: "node_modules/canative". 27 | # 28 | # The accompanying Makefile.node_deps.targ defines a target that will install 29 | # each of REPO_MODULES into REPO_DEPS and remove REPO_DEPS with "make clean". 30 | # The top-level Makefile is responsible for depending on REPO_DEPS where 31 | # appropriate (usually the "deps" or "all" target). 32 | # 33 | 34 | REPO_DEPS = $(REPO_MODULES:src/node-%=node_modules/%) 35 | CLEAN_FILES += $(REPO_DEPS) 36 | -------------------------------------------------------------------------------- /svc/rc/mantanfs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # mantanfs This shell script takes care of starting and stopping 4 | # the Manta NFS service. 5 | 6 | ### BEGIN INIT INFO 7 | # Provides: mantanfs 8 | # Default-Start: 3 4 5 9 | # Default-Stop: 0 1 6 10 | # Short-Description: Start up the Manta NFS server 11 | # Description: NFS is a popular protocol for file sharing across \ 12 | # networks. This service provides NFS service with Manta \ 13 | # as the backing store. 14 | ### END INIT INFO 15 | 16 | NODE="/usr/bin/node" 17 | SERVER="/usr/local/bin/server.js" 18 | CONFIG="/usr/local/etc/manta-nfs.json" 19 | 20 | uid=`id | cut -d\( -f1 | cut -d= -f2` 21 | RETVAL=0 22 | 23 | start() { 24 | [ -x $NODE ] || exit 5 25 | [ -e $SERVER ] || exit 5 26 | [ -e $CONFIG ] || exit 5 27 | 28 | # Only root can start the service 29 | [ $uid -ne 0 ] && exit 4 30 | 31 | # Make sure the server is not already running. 32 | if pgrep -f 'node.*server.js' > /dev/null ; then 33 | exit 0 34 | fi 35 | 36 | echo -n $"Starting Manta NFS server: " 37 | $NODE $SERVER -f $CONFIG >/var/log/mantanfs.log 2>&1 & 38 | RETVAL=$? 39 | echo 40 | return $RETVAL 41 | } 42 | 43 | stop() { 44 | # Only root can stop the service 45 | [ $uid -ne 0 ] && exit 4 46 | 47 | echo -n $"Stopping Manta NFS server: " 48 | pkill -f 'node.*server.js' 49 | RETVAL=$? 50 | echo 51 | return $RETVAL 52 | } 53 | 54 | # See how we were called. 55 | case "$1" in 56 | start) 57 | start 58 | ;; 59 | stop) 60 | stop 61 | ;; 62 | *) 63 | echo $"Usage: $0 {start|stop}" 64 | RETVAL=2 65 | ;; 66 | esac 67 | 68 | exit $RETVAL 69 | 70 | -------------------------------------------------------------------------------- /tools/mk/Makefile.defs: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | # 5 | # Makefile.defs: common defines. 6 | # 7 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped 8 | # into other repos as-is without requiring any modifications. If you find 9 | # yourself changing this file, you should instead update the original copy in 10 | # eng.git and then update your repo to use the new version. 11 | # 12 | # This makefile defines some useful defines. Include it at the top of 13 | # your Makefile. 14 | # 15 | # Definitions in this Makefile: 16 | # 17 | # TOP The absolute path to the project directory. The top dir. 18 | # BRANCH The current git branch. 19 | # TIMESTAMP The timestamp for the build. This can be set via 20 | # the TIMESTAMP envvar (used by MG-based builds). 21 | # STAMP A build stamp to use in built package names. 22 | # 23 | 24 | TOP := $(shell pwd) 25 | 26 | # 27 | # Mountain Gorilla-spec'd versioning. 28 | # See "Package Versioning" in MG's README.md: 29 | # 30 | # 31 | # Need GNU awk for multi-char arg to "-F". 32 | _AWK := $(shell (which gawk >/dev/null && echo gawk) \ 33 | || (which nawk >/dev/null && echo nawk) \ 34 | || echo awk) 35 | BRANCH := $(shell git symbolic-ref HEAD | $(_AWK) -F/ '{print $$3}') 36 | ifeq ($(TIMESTAMP),) 37 | TIMESTAMP := $(shell date -u "+%Y%m%dT%H%M%SZ") 38 | endif 39 | _GITDESCRIBE := g$(shell git describe --all --long --dirty | $(_AWK) -F'-g' '{print $$NF}') 40 | STAMP := $(BRANCH)-$(TIMESTAMP)-$(_GITDESCRIBE) 41 | 42 | # node-gyp will print build info useful for debugging with V=1 43 | export V=1 44 | -------------------------------------------------------------------------------- /tools/mk/Makefile.node_prebuilt.targ: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | # 5 | # Makefile.node_prebuilt.targ: Makefile for including a prebuilt Node.js 6 | # build. 7 | # 8 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped 9 | # into other repos as-is without requiring any modifications. If you find 10 | # yourself changing this file, you should instead update the original copy in 11 | # eng.git and then update your repo to use the new version. 12 | 13 | 14 | NODE_PREBUILT_TARBALL ?= $(error NODE_PREBUILT_TARBALL is not set: was Makefile.node_prebuilt.defs included?) 15 | 16 | 17 | # TODO: remove this limitation 18 | # Limitation: currently presuming that the NODE_INSTALL basename is 19 | # 'node' and that sdcnode tarballs have a 'node' top-level dir. 20 | $(NODE_EXEC) $(NPM_EXEC) $(NODE_WAF_EXEC): 21 | [[ $(shell basename $(NODE_INSTALL)) == "node" ]] \ 22 | || (echo "Limitation: 'basename NODE_INSTALL' is not 'node'" && exit 1) 23 | rm -rf $(NODE_INSTALL) \ 24 | $(BUILD)/prebuilt-node-* $(BUILD)/prebuilt-npm-* 25 | mkdir -p $(shell dirname $(NODE_INSTALL)) 26 | if [[ $(shell echo $(NODE_PREBUILT_TARBALL) | cut -c 1-4) == "http" ]]; then \ 27 | echo "Downloading '$(NODE_PREBUILT_BASE)'."; \ 28 | curl -ksS --fail --connect-timeout 5 -o $(shell dirname $(NODE_INSTALL))/$(NODE_PREBUILT_BASE) $(NODE_PREBUILT_TARBALL); \ 29 | (cd $(shell dirname $(NODE_INSTALL)) && $(TAR) xf $(NODE_PREBUILT_BASE)); \ 30 | else \ 31 | (cd $(shell dirname $(NODE_INSTALL)) && $(TAR) xf $(NODE_PREBUILT_TARBALL)); \ 32 | fi 33 | ln -s $(TOP)/$(NODE_INSTALL)/bin/node $(NODE_EXEC) 34 | ln -s $(TOP)/$(NODE_INSTALL)/bin/npm $(NPM_EXEC) 35 | -------------------------------------------------------------------------------- /lib/nfs/fsinfo.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | 9 | var common = require('./common'); 10 | 11 | 12 | 13 | ///--- API 14 | 15 | // Values obtained from: http://goo.gl/fBLulQ (IBM) 16 | function fsinfo(call, reply, next) { 17 | var log = call.log; 18 | 19 | log.debug('fsinfo(%s): entered', call.object); 20 | 21 | call.fs.stat(call._filename, function (err, stats) { 22 | if (err) { 23 | log.debug(err, 'fsinfo(%s): stat failed', call._filename); 24 | reply.error(nfs.NFS3ERR_STALE); 25 | reply.send(); 26 | next(false); 27 | } else { 28 | reply.setAttributes(stats); 29 | 30 | reply.wtmax = reply.rtmax = 65536; 31 | reply.wtpref = reply.rtpref = 32768; 32 | reply.wtmult = reply.rtmult = 4096; 33 | reply.dtpref = 8192; 34 | reply.maxfilesize = 1099511627776; // 1T 35 | reply.time_delta = { 36 | seconds: 0, 37 | nseconds: 1000000 38 | }; // milliseconds 39 | reply.properties = nfs.FSF3_HOMOGENOUS; 40 | 41 | log.debug('fsinfo(%s): done', call.object); 42 | reply.send(); 43 | next(); 44 | } 45 | }); 46 | } 47 | 48 | 49 | 50 | ///--- Exports 51 | 52 | module.exports = function chain() { 53 | return ([ 54 | common.fhandle_to_filename, 55 | fsinfo 56 | ]); 57 | }; 58 | -------------------------------------------------------------------------------- /svc/smf/manta-nfs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 15 | 16 | 21 | 22 | 23 | 24 | 29 | 30 | 31 | 32 | 37 | 38 | 39 | 40 | 45 | 46 | 51 | 52 | 53 | 54 | 55 | 56 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /lib/nfs/read.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var assert = require('assert-plus'); 8 | var nfs = require('nfs'); 9 | 10 | var common = require('./common'); 11 | 12 | var fs = require('fs'); 13 | 14 | 15 | 16 | ///-- API 17 | 18 | 19 | 20 | 21 | function read(call, reply, next) { 22 | var data = new Buffer(call.count); 23 | var log = call.log; 24 | var stats = call.stats; 25 | 26 | log.debug('read(%s, %d, %d): entered', 27 | call.object, call.offset, call.count); 28 | 29 | assert.ok(stats); 30 | 31 | call.fs.read(stats.fd, data, 0, call.count, call.offset, function (err, n) { 32 | if (err) { 33 | log.warn(err, 'read: fsCache.read failed'); 34 | reply.error(nfs.NFS3ERR_IO); 35 | next(false); 36 | return; 37 | } 38 | 39 | // use stat.size to determine eof 40 | var eof = false; 41 | if (stats.size <= (call.offset + n)) { 42 | eof = true; 43 | 44 | // If we're at EOF, we assume we can close the FD out 45 | if (call.fd_cache.has(call.object)) 46 | call.fd_cache.del(call.object); 47 | 48 | } 49 | 50 | // some NFS clients verify that the returned buffer 51 | // length matches the result count 52 | if (n < call.count) 53 | data = data.slice(0, n); 54 | 55 | log.debug('read(%s): done => %d', call.object, n); 56 | 57 | reply.count = n; 58 | reply.data = data; 59 | reply.eof = eof; 60 | reply.send(); 61 | next(); 62 | }); 63 | } 64 | 65 | 66 | 67 | ///--- Exports 68 | 69 | module.exports = function chain() { 70 | return ([ 71 | common.fhandle_to_filename, 72 | common.open, 73 | read 74 | ]); 75 | }; 76 | -------------------------------------------------------------------------------- /etc/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "manta": { 3 | "_comment": "the Manta environment variables can be used instead", 4 | "keyFile": "/Users/foo/.ssh/id_rsa", 5 | "keyId": "70:c0:50:d6:9e:1f:0c:74:04:8b:08:c9:12:a2:7c:9f", 6 | "url": "https://us-east.manta.joyent.com", 7 | "user": "foo.bar" 8 | }, 9 | "database": { 10 | "_comment": "the default DB and cache is under /var/tmp/mfsdb", 11 | "location": "/var/tmp/mfsdb", 12 | "_comment2": "how much local disk space to use for caching", 13 | "sizeMB": 5120, 14 | "_comment3": "time-to-live in cache before cheking if stale (secs)", 15 | "ttl": 43200, 16 | "_comment4": "time for dirty file to be cached before writeback (secs)", 17 | "wbtime": 60, 18 | "_comment5": "number of parallel writebacks", 19 | "num_par": 2 20 | }, 21 | "portmap": { 22 | "_comment": "usehost forces use of the system's portmapper", 23 | "usehost": 1 24 | }, 25 | "mount": { 26 | "_comment": "set address if serving network beyond localhost", 27 | "address": "0.0.0.0", 28 | "_comment2": "can deny/allow mounting by given host addresses", 29 | "_comment3": "normally use only one of these lists", 30 | "hosts_deny": { 31 | "192.168.0.12": {}, 32 | "192.168.0.13": {} 33 | }, 34 | "hosts_allow": { 35 | "127.0.0.1": {}, 36 | "192.168.0.10": {}, 37 | "192.168.0.11": {} 38 | }, 39 | "_comment4": "can limit access to user's Manta namespace", 40 | "exports": { 41 | "/foo.bar/stor/project": {}, 42 | "/foo.bar/public": {} 43 | } 44 | }, 45 | "nfs": { 46 | "_comment": "can specify uid/gid for 'nobody' on clients", 47 | "uid": 65534, 48 | "gid": 65534, 49 | "_comment2": "tunables for file descriptor cache", 50 | "fd_cache": { 51 | "max": 50, 52 | "ttl": 15 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/nfs/setattr.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | 9 | var common = require('./common'); 10 | 11 | 12 | 13 | ///-- API 14 | 15 | // We implement setattr only for the special cases of: 16 | // - truncating an existing file down to 0 (i.e. when its being overwritten). 17 | // - chmod/chown (we just lie) 18 | // 19 | function setattr(req, res, next) { 20 | var attrs = req.new_attributes; 21 | var log = req.log; 22 | 23 | log.debug('setattr(%s, %d): entered', req.object, req.new_attributes.size); 24 | 25 | if (attrs.how_m_time === nfs.time_how.SET_TO_CLIENT_TIME) { 26 | // The client is touching the file. We use this as an indication to 27 | // refresh the cached data. We don't need to worry about supplying 28 | // valid times for atime and mtime here, and we still tell the client 29 | // that this is an error. 30 | req.fs.utimes(req._filename, 0, 0, function (err) { 31 | if (err) { 32 | log.warn(err, 'setattr: mantafs.utimes failed'); 33 | res.error(nfs.NFS3ERR_SERVERFAULT); 34 | } else { 35 | res.error(nfs.NFS3ERR_ACCES); 36 | } 37 | next(false); 38 | }); 39 | return; 40 | } 41 | 42 | // If attrs.how_a_time or attrs.how_m_time is SET_TO_SERVER_TIME we just 43 | // ignore that. Some apps, such as vi, seem to initiate that. 44 | // We also ignore attrs.mode, attrs.uid and attrs.gid since we can't 45 | // change those either but various apps try. 46 | 47 | // OK, we're setting the file size 48 | if (attrs.size !== null) { 49 | req.fs.truncate(req._filename, attrs.size, function (err) { 50 | if (err) { 51 | log.warn(err, 'setattr: mantafs.truncate failed'); 52 | res.error(nfs.NFS3ERR_SERVERFAULT); 53 | next(false); 54 | return; 55 | } 56 | 57 | log.debug('setattr: done'); 58 | res.send(); 59 | next(); 60 | }); 61 | } else { 62 | log.debug('setattr: done'); 63 | res.send(); 64 | next(); 65 | } 66 | } 67 | 68 | 69 | ///--- Exports 70 | 71 | module.exports = function chain() { 72 | return ([ 73 | common.fhandle_to_filename, 74 | setattr 75 | ]); 76 | }; 77 | -------------------------------------------------------------------------------- /tools/bashstyle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* 4 | * Copyright (c) 2012, Joyent, Inc. All rights reserved. 5 | * 6 | * bashstyle: check bash scripts for adherence to style guidelines, including: 7 | * 8 | * o no lines longer than 80 characters 9 | * o file does not end with a blank line 10 | * 11 | * Future enhancements could include: 12 | * o indents consistent with respect to tabs, spaces 13 | * o indents consistently sized (all are some multiple of the smallest 14 | * indent, which must be a tab or 4 or 8 spaces) 15 | */ 16 | 17 | var mod_assert = require('assert'); 18 | var mod_fs = require('fs'); 19 | 20 | var nerrors = 0; 21 | 22 | main(); 23 | process.exit(0); 24 | 25 | function main() 26 | { 27 | var files = process.argv.slice(2); 28 | 29 | if (files.length === 0) { 30 | console.error('usage: %s file1 [...]', 31 | process.argv.slice(0, 2).join(' ')); 32 | process.exit(2); 33 | } 34 | 35 | files.forEach(checkFile); 36 | 37 | if (nerrors != 0) 38 | process.exit(1); 39 | } 40 | 41 | function checkFile(filename) 42 | { 43 | var text = mod_fs.readFileSync(filename, 'utf-8'); 44 | var lines = text.split('\n'); 45 | var i; 46 | 47 | mod_assert.ok(lines.length > 0); 48 | 49 | /* 50 | * Expand tabs in each line and check for long lines. 51 | */ 52 | for (i = 1; i <= lines.length; i++) { 53 | var line = expandTabs(lines[i - 1]); 54 | 55 | if (line.length > 80) { 56 | nerrors++; 57 | console.log('%s: %d: line exceeds 80 columns', 58 | filename, i); 59 | } 60 | } 61 | 62 | /* 63 | * No sane editor lets you save a file without a newline at the very end. 64 | */ 65 | if (lines[lines.length - 1].length !== 0) { 66 | nerrors++; 67 | console.log('%s: %d: file does not end with newline', 68 | filename, lines.length); 69 | } 70 | 71 | /* 72 | * Since the file will always end with a newline, the last entry of 73 | * "lines" will actually be blank. 74 | */ 75 | if (lines.length > 1 && lines[lines.length - 2].length === 0) { 76 | nerrors++; 77 | console.log('%s: %d: file ends with a blank line', 78 | filename, lines.length - 1); 79 | } 80 | } 81 | 82 | function expandTabs(text) 83 | { 84 | var out = ''; 85 | var col = 0; 86 | var j, k; 87 | 88 | for (j = 0; j < text.length; j++) { 89 | if (text[j] != '\t') { 90 | out += text[j]; 91 | col++; 92 | continue; 93 | } 94 | 95 | k = 8 - (col % 8); 96 | col += k; 97 | 98 | do { 99 | out += ' '; 100 | } while (--k > 0); 101 | 102 | col += k; 103 | } 104 | 105 | return (out); 106 | } 107 | -------------------------------------------------------------------------------- /lib/nfs/remove.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | var path = require('path'); 9 | 10 | var common = require('./common'); 11 | 12 | 13 | 14 | ///-- API 15 | 16 | function remove_lookup_dir(call, reply, next) { 17 | var log = call.log; 18 | 19 | log.debug('remove_lookup_dir(%s): entered', call._object.dir); 20 | call.fs.fhandle(call._object.dir, function (err, name) { 21 | if (err) { 22 | log.warn(err, 'remove_lookup_dir(%s): fhandle notfound', 23 | call._object.dir); 24 | reply.error(nfs.NFS3ERR_STALE); 25 | next(false); 26 | } else { 27 | call._dirname = name; 28 | call._filename = path.join(name, call._object.name); 29 | log.debug('remove_lookup_dir(%s): done -> %s', call._object.dir, 30 | name); 31 | next(); 32 | } 33 | }); 34 | } 35 | 36 | 37 | function remove_stat_dir(call, reply, next) { 38 | var log = call.log; 39 | 40 | log.debug('remove_stat_dir(%s): entered', call._filename); 41 | call.fs.stat(call._filename, function (err, stats) { 42 | if (err) { 43 | log.warn(err, 'remove_stat_dir(%s): failed', call._filename); 44 | reply.error(nfs.NFS3ERR_IO); 45 | next(false); 46 | return; 47 | } 48 | if (stats.isDirectory()) { 49 | log.warn(err, 'remove_stat_dir(%s): is a directory', 50 | call._filename); 51 | reply.error(nfs.NFS3ERR_NOTDIR); 52 | next(false); 53 | return; 54 | } 55 | 56 | log.debug('remove_stat_dir(%s): done', call._filename); 57 | next(); 58 | }); 59 | } 60 | 61 | 62 | function remove(call, reply, next) { 63 | var log = call.log; 64 | 65 | log.debug('remove(%s): entered', call._filename); 66 | call.fs.unlink(call._filename, function (err) { 67 | if (err) { 68 | log.warn(err, 'remove(%s): failed', call._filename); 69 | common.handle_error(err, call, reply, next); 70 | return; 71 | } 72 | 73 | log.debug('remove(%s): done', call._filename); 74 | reply.send(); 75 | next(); 76 | }); 77 | } 78 | 79 | 80 | 81 | ///--- Exports 82 | 83 | module.exports = function chain() { 84 | return ([ 85 | remove_lookup_dir, 86 | remove_stat_dir, 87 | remove 88 | ]); 89 | }; 90 | -------------------------------------------------------------------------------- /lib/nfs/rmdir.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | var path = require('path'); 9 | 10 | var common = require('./common'); 11 | 12 | 13 | 14 | ///-- API 15 | 16 | function rmdir_lookup_dir(call, reply, next) { 17 | var log = call.log; 18 | 19 | log.debug('rmdir_lookup_dir(%s): entered', call._object.dir); 20 | call.fs.fhandle(call._object.dir, function (err, name) { 21 | if (err) { 22 | log.warn(err, 'rmdir_lookup_dir(%s): fhandle notfound', 23 | call._object.dir); 24 | reply.error(nfs.NFS3ERR_STALE); 25 | next(false); 26 | } else { 27 | call._dirname = name; 28 | call._filename = path.join(name, call._object.name); 29 | log.debug('rmdir_lookup_dir(%s): done -> %s', call._object.dir, 30 | name); 31 | next(); 32 | } 33 | }); 34 | } 35 | 36 | 37 | function rmdir_stat_dir(call, reply, next) { 38 | var log = call.log; 39 | 40 | log.debug('rmdir_stat_dir(%s): entered', call._filename); 41 | call.fs.stat(call._filename, function (err, stats) { 42 | if (err) { 43 | log.warn(err, 'rmdir_stat_dir(%s): failed', call._filename); 44 | reply.error(nfs.NFS3ERR_IO); 45 | next(false); 46 | return; 47 | } 48 | if (!stats.isDirectory()) { 49 | log.warn(err, 'rmdir_stat_dir(%s): not a directory', 50 | call._filename); 51 | reply.error(nfs.NFS3ERR_NOTDIR); 52 | next(false); 53 | return; 54 | } 55 | 56 | log.debug('rmdir_stat_dir(%s): done', call._filename); 57 | next(); 58 | }); 59 | } 60 | 61 | 62 | function rmdir(call, reply, next) { 63 | var log = call.log; 64 | 65 | log.debug('rmdir(%s): entered', call._filename); 66 | call.fs.rmdir(call._filename, function (err) { 67 | if (err && err.code !== 'ENOENT') { 68 | if (err.code === 'ENOTEMPTY') { 69 | log.info('rmdir(%s): directory not empty', call._filename); 70 | } else { 71 | log.warn(err, 'rmdir(%s): failed', call._filename); 72 | } 73 | common.handle_error(err, call, reply, next); 74 | return; 75 | } 76 | 77 | log.debug('rmdir(%s): done', call._filename); 78 | reply.send(); 79 | next(); 80 | }); 81 | } 82 | 83 | 84 | 85 | ///--- Exports 86 | 87 | module.exports = function chain() { 88 | return ([ 89 | rmdir_lookup_dir, 90 | rmdir_stat_dir, 91 | rmdir 92 | ]); 93 | }; 94 | -------------------------------------------------------------------------------- /tools/mkrepo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // -*- mode: js -*- 3 | // Copyright 2012 Joyent, Inc. All rights reserved. 4 | 5 | var child_process = require('child_process'); 6 | var fs = require('fs'); 7 | var path = require('path'); 8 | 9 | 10 | 11 | ///--- Globals 12 | 13 | var DIRS = [ 14 | 'deps', 15 | 'docs', 16 | 'docs/media', 17 | 'lib', 18 | 'smf', 19 | 'smf/manifests', 20 | 'test', 21 | 'tools', 22 | 'tools/mk' 23 | ]; 24 | 25 | var SUBMODULES = { 26 | 'javascriptlint': 'git://github.com/davepacheco/javascriptlint.git', 27 | 'jsstyle': 'git://github.com/davepacheco/jsstyle.git', 28 | 'restdown': 'git://github.com/trentm/restdown.git' 29 | }; 30 | 31 | 32 | 33 | ///--- Internal Functions 34 | 35 | function usage(code, message) { 36 | if (message) 37 | console.error(message); 38 | 39 | console.error('usage: %s [repo ...]', path.basename(process.argv[1])); 40 | process.exit(code); 41 | } 42 | 43 | 44 | function ensureDirectoryNotExists(dir) { 45 | try { 46 | var stats = fs.statSync(dir); 47 | usage(1, dir + ' already exists'); 48 | } catch (e) { 49 | return false; 50 | } 51 | } 52 | 53 | 54 | function cp(src, dest) { 55 | fs.createReadStream(src).pipe(fs.createWriteStream(dest)); 56 | } 57 | 58 | 59 | function exec(cmd, dir, cb) { 60 | child_process.exec(cmd, {cwd: dir}, function (err, stdout, stderr) { 61 | if (err) 62 | process.exit(err.code || 1); 63 | 64 | if (typeof (cb) === 'function') 65 | return cb(null); 66 | }); 67 | } 68 | 69 | 70 | function mkdir(d) { 71 | fs.mkdirSync(d, '0750'); 72 | } 73 | 74 | function gitify(dir, repo) { 75 | exec('git init', dir, function () { 76 | exec('git remote add origin git@git.joyent.com:' + repo + '.git', dir); 77 | 78 | Object.keys(SUBMODULES).forEach(function (k) { 79 | // stub out the git submodule call 80 | console.error('Cloning into deps/' + k + '...'); 81 | exec('git submodule add ' + SUBMODULES[k] + ' ./deps/' + k, dir); 82 | }); 83 | }); 84 | } 85 | 86 | 87 | 88 | ///--- Mainline 89 | 90 | if (process.argv.length < 3) 91 | usage(1, 'repo required'); 92 | 93 | process.argv.slice(2).forEach(function (arg) { 94 | var repo = path.resolve(arg); 95 | ensureDirectoryNotExists(repo); 96 | mkdir(repo); 97 | DIRS.concat('.').forEach(function (d) { 98 | var dir = repo + '/' + d; 99 | if (d != '.') 100 | mkdir(dir); 101 | 102 | fs.readdirSync('./' + d).forEach(function (f) { 103 | var src = './' + d + '/' + f; 104 | var dest = dir + '/' + f; 105 | if (fs.statSync(src).isFile() && !/^\..*/.test(f)) 106 | cp(src, dest); 107 | }); 108 | }); 109 | 110 | cp('./.gitignore', repo + '/.gitignore'); 111 | gitify(repo, arg); 112 | }); 113 | -------------------------------------------------------------------------------- /lib/portmap.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var assert = require('assert-plus'); 8 | var rpc = require('oncrpc'); 9 | 10 | 11 | 12 | ///--- API 13 | 14 | function createPortmapClient(opts) { 15 | assert.object(opts, 'options'); 16 | assert.object(opts.log, 'options.log'); 17 | assert.string(opts.url, 'options.url'); 18 | 19 | var c = rpc.createPortmapClient({ 20 | log: opts.log, 21 | url: opts.url 22 | }); 23 | 24 | return (c); 25 | } 26 | 27 | 28 | function createPortmapServer(opts) { 29 | assert.object(opts, 'options'); 30 | assert.object(opts.log, 'options.log'); 31 | assert.object(opts.mappings, 'options.mappings'); 32 | 33 | var mappings = {}; 34 | var s = rpc.createPortmapServer({ 35 | log: opts.log 36 | }); 37 | 38 | Object.keys(opts.mappings).forEach(function (k) { 39 | mappings[k] = opts.mappings[k].map(function (i) { 40 | assert.number(i.prog, k + '.prog'); 41 | assert.number(i.vers, k + '.vers'); 42 | assert.number(i.prot, k + '.prot'); 43 | assert.number(i.port, k + '.port'); 44 | 45 | return ({ 46 | name: k, 47 | prog: i.prog, 48 | vers: i.vers, 49 | prot: i.prot, 50 | port: i.port 51 | }); 52 | }); 53 | }); 54 | 55 | s.dump(function dump(req, res, next) { 56 | Object.keys(mappings).forEach(function (k) { 57 | mappings[k].forEach(function (i) { 58 | res.addMapping(i); 59 | }); 60 | }); 61 | 62 | res.send(); 63 | next(); 64 | }); 65 | 66 | s.get_port(function get_port(req, res, next) { 67 | var m = req.mapping; 68 | Object.keys(mappings).forEach(function (k) { 69 | mappings[k].some(function (i) { 70 | if (i.prog === m.prog && 71 | i.vers === m.vers && 72 | i.prot === m.prot) { 73 | 74 | res.port = i.port; 75 | return (true); 76 | } 77 | 78 | return (false); 79 | }); 80 | }); 81 | 82 | 83 | res.send(); 84 | next(); 85 | }); 86 | 87 | s.on('after', function (name, call, reply, err) { 88 | opts.log.debug({ 89 | procedure: name, 90 | rpc_call: call, 91 | rpc_reply: reply, 92 | err: err 93 | }, 'portmapd: %s handled', name); 94 | }); 95 | 96 | return (s); 97 | } 98 | 99 | 100 | 101 | ///--- Exports 102 | 103 | module.exports = { 104 | createPortmapServer: createPortmapServer, 105 | createPortmapClient: createPortmapClient 106 | }; 107 | -------------------------------------------------------------------------------- /tools/runtests.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | # 5 | # Run the TODONAME tests. 6 | # Run `./runtests -h` for usage info. 7 | # 8 | 9 | if [ "$TRACE" != "" ]; then 10 | export PS4='${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' 11 | set -o xtrace 12 | fi 13 | set -o errexit 14 | set -o pipefail 15 | 16 | 17 | 18 | #---- guard 19 | 20 | if [[ ! -f "/lib/sdc/.sdc-test-no-production-data" ]]; then 21 | cat < %s', call.what.dir, name); 30 | next(); 31 | } 32 | }); 33 | } 34 | 35 | 36 | function stat_file(call, reply, next) { 37 | var log = call.log; 38 | 39 | log.debug('stat_file(%s): entered', call._filename); 40 | call.fs.stat(call._filename, function (err, stats) { 41 | if (err) { 42 | log.debug(err, 'stat_file(%s): failed', call._filename); 43 | reply.error(nfs.NFS3ERR_NOENT); 44 | next(false); 45 | } else { 46 | // reply.object = uuid; 47 | reply.setAttributes(stats); 48 | log.debug({stats: stats}, 'stat_file(%s): done', call._filename); 49 | next(); 50 | } 51 | }); 52 | } 53 | 54 | 55 | function stat_dir(call, reply, next) { 56 | var log = call.log; 57 | 58 | log.debug('stat_dir(%s): entered', call._dirname); 59 | call.fs.stat(call._dirname, function (err, stats) { 60 | if (err) { 61 | log.debug(err, 'stat_dir(%s): failed', call._dirname); 62 | reply.error(nfs.NFS3ERR_IO); 63 | next(false); 64 | } else { 65 | reply.setDirAttributes(stats); 66 | log.debug({stats: stats}, 'stat_dir(%s): done', call._dirname); 67 | next(); 68 | } 69 | }); 70 | } 71 | 72 | 73 | function lookup(call, reply, next) { 74 | var log = call.log; 75 | 76 | log.debug('lookup(%s): entered', call._filename); 77 | call.fs.lookup(call._filename, function (err, fhandle) { 78 | if (err) { 79 | log.debug(err, 'lookup(%s): failed', call._filename); 80 | reply.error(nfs.NFS3ERR_NOENT); 81 | next(false); 82 | return; 83 | } 84 | reply.object = fhandle; 85 | reply.send(); 86 | next(); 87 | }); 88 | } 89 | 90 | 91 | 92 | ///--- Exports 93 | 94 | module.exports = function chain() { 95 | return ([ 96 | function cheat(call, reply, next) { 97 | call._object_override = call.what.dir; 98 | next(); 99 | }, 100 | lookup_dir, 101 | stat_dir, 102 | stat_file, 103 | lookup 104 | ]); 105 | }; 106 | -------------------------------------------------------------------------------- /lib/nfs/common.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var assert = require('assert-plus'); 8 | var nfs = require('nfs'); 9 | var once = require('once'); 10 | 11 | 12 | 13 | ///-- API 14 | 15 | function fhandle_to_filename(call, reply, next) { 16 | var fhandle = call.fhandle || call.object; 17 | var log = call.log; 18 | 19 | log.debug('fhandle_to_filename(%s): entered', fhandle); 20 | assert.string(fhandle, 'call.fhandle'); 21 | 22 | 23 | call.fs.fhandle(fhandle, function (err, name) { 24 | if (err) { 25 | log.warn(err, 'fhandle_to_filename(%s): failed', fhandle); 26 | reply.error(nfs.NFS3ERR_BADHANDLE); 27 | next(false); 28 | } else { 29 | call._filename = name; 30 | log.debug('fhandle_to_filename(%s): done: %s', fhandle, name); 31 | next(); 32 | } 33 | }); 34 | } 35 | 36 | function handle_error(err, req, res, next) { 37 | switch (err.code) { 38 | case 'EACCESS': 39 | res.error(nfs.NFS3ERR_ACCES); 40 | break; 41 | 42 | case 'ENOENT': 43 | res.error(nfs.NFS3ERR_NOENT); 44 | break; 45 | 46 | case 'ENOTDIR': 47 | res.error(nfs.NFS3ERR_NOTDIR); 48 | break; 49 | 50 | case 'ENOTEMPTY': 51 | res.error(nfs.NFS3ERR_NOTEMPTY); 52 | break; 53 | 54 | default: 55 | res.error(nfs.NFS3ERR_SERVERFAULT); 56 | break; 57 | } 58 | next(false); 59 | } 60 | 61 | 62 | function open(call, reply, next) { 63 | var log = call.log; 64 | 65 | log.debug('open(%s): entered', call.object); 66 | 67 | if (call.fd_cache.has(call.object)) { 68 | call.stats = call.fd_cache.get(call.object); 69 | next(); 70 | return; 71 | } 72 | 73 | call.fs.stat(call._filename, function (st_err, stats) { 74 | if (st_err) { 75 | log.warn(st_err, 'open: fsCache.stat failed'); 76 | reply.error(nfs.NFS3ERR_IO); 77 | next(false); 78 | return; 79 | } 80 | 81 | call.fs.open(call._filename, 'r+', function (err, fd) { 82 | if (err) { 83 | log.warn(err, 'open: failed'); 84 | reply.error(nfs.NFS3ERR_SERVERFAULT); 85 | next(false); 86 | return; 87 | } 88 | 89 | call.stats = { 90 | fd: fd, 91 | size: stats.size 92 | }; 93 | call.fd_cache.set(call.object, call.stats); 94 | 95 | log.debug('open(%s): done => %j', call.object, call.stats); 96 | next(); 97 | }); 98 | }); 99 | } 100 | 101 | 102 | // based on http://stackoverflow.com/a/7616484 103 | function hash(s) { 104 | var h = 0, i, c; 105 | 106 | var l = s.length; 107 | if (l === 0) 108 | return (h); 109 | for (i = 0; i < l; i++) { 110 | c = s.charCodeAt(i); 111 | h = ((h << 5) - h) + c; 112 | h |= 0; // Convert to 32bit integer 113 | } 114 | return (Math.abs(h)); 115 | } 116 | 117 | 118 | ///--- Exports 119 | 120 | module.exports = { 121 | fhandle_to_filename: fhandle_to_filename, 122 | handle_error: handle_error, 123 | open: open, 124 | hash: hash 125 | }; 126 | -------------------------------------------------------------------------------- /lib/nfs/readdir.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var path = require('path'); 8 | 9 | var nfs = require('nfs'); 10 | 11 | var common = require('./common'); 12 | 13 | var rpc = require('oncrpc'); 14 | var XDR = rpc.XDR; 15 | 16 | 17 | ///--- Helpers 18 | 19 | function rand() { 20 | return (Math.floor((Math.random() * Math.pow(2, 31)) + 1)); 21 | } 22 | 23 | 24 | 25 | ///-- API 26 | 27 | function readdir(call, reply, next) { 28 | var log = call.log; 29 | log.debug('readdir(%s): entered', call._filename); 30 | 31 | call.fs.readdir(call._filename, function (err, files) { 32 | if (err) { 33 | log.warn(err, 'readdir(%s): failed', call.dir); 34 | reply.error(err.code === 'ENOTDIR' ? 35 | nfs.NFS3ERR_NOTDIR : 36 | nfs.NFS3ERR_IO); 37 | next(false); 38 | return; 39 | } 40 | 41 | // The cookieverf will be 0 on the initial call. 42 | var h = common.hash(call._filename); 43 | if (call.cookieverf.readUInt32LE(0) != 0) { 44 | // This is a follow-up call, confirm cookie. 45 | if (call.cookieverf.readUInt32LE(0) != h) { 46 | reply.error(nfs.NFS3ERR_BAD_COOKIE); 47 | next(false); 48 | return; 49 | } 50 | } 51 | 52 | reply.eof = true; 53 | 54 | var cook = 1; 55 | // Track the returned data size 56 | // status (4) + bool_dir_attrs (4) + fattr3.XDR_SIZE + 57 | // cookieverf3 (8) + bool_eof (4) + final_list_false (4) 58 | // See nfs readdir_reply.js. 59 | var sz = 116; 60 | files.every(function (f) { 61 | // The call cookie will be 0 on the initial call 62 | if (call.cookie != 0 && call.cookie >= cook) { 63 | // we need to scan the dir until we reach the right entry 64 | cook++; 65 | return (true); 66 | } 67 | 68 | // We need to track the returned data size to be sure we fit in 69 | // call.count bytes. 70 | // list_true (4) + fileid (8) + cookie (8) + name_len 71 | var delta = 20 + XDR.byteLength(f); 72 | if ((sz + delta) > call.count) { 73 | reply.eof = false; 74 | return (false); 75 | } 76 | 77 | reply.addEntry({ 78 | fileid: common.hash(path.join(call._filename, f)), 79 | name: f, 80 | cookie: cook++ 81 | }); 82 | sz += delta; 83 | return (true); 84 | }); 85 | 86 | reply.cookieverf = new Buffer(8); 87 | reply.cookieverf.fill(0); 88 | reply.cookieverf.writeUInt32LE(h, 0, true); 89 | 90 | call.fs.stat(call._filename, function (err2, stats) { 91 | if (err2) { 92 | log.warn(err2, 'readdir(%s): stat failed', call._filename); 93 | } else { 94 | reply.setDirAttributes(stats); 95 | } 96 | reply.send(); 97 | next(); 98 | }); 99 | }); 100 | } 101 | 102 | 103 | ///--- Exports 104 | 105 | module.exports = function chain() { 106 | return ([ 107 | common.fhandle_to_filename, 108 | readdir 109 | ]); 110 | }; 111 | -------------------------------------------------------------------------------- /lib/nfs/rename.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | var path = require('path'); 9 | 10 | var common = require('./common'); 11 | 12 | 13 | 14 | ///-- API 15 | 16 | function rename_get_from_dir(call, reply, next) { 17 | var log = call.log; 18 | 19 | log.debug('rename_get_from_dir(%s): entered', call.from.dir); 20 | call.fs.fhandle(call.from.dir, function (err, name) { 21 | if (err) { 22 | log.warn(err, 'rename_get_from_dir(%s): fhandle notfound', 23 | call.from.dir); 24 | reply.error(nfs.NFS3ERR_STALE); 25 | next(false); 26 | return; 27 | } 28 | 29 | call._from_dirname = name; 30 | call._from_filename = path.join(name, call.from.name); 31 | log.debug('rename_get_from_dir(%s): done -> %s', call.from.dir, name); 32 | next(); 33 | }); 34 | } 35 | 36 | 37 | function rename_get_to_dir(call, reply, next) { 38 | var log = call.log; 39 | 40 | log.debug('rename_get_to_dir(%s): entered', call.to.dir); 41 | call.fs.fhandle(call.to.dir, function (err, name) { 42 | if (err) { 43 | log.warn(err, 'rename_get_to_dir(%s): fhandle notfound', 44 | call.to.dir); 45 | reply.error(nfs.NFS3ERR_STALE); 46 | next(false); 47 | return; 48 | } 49 | 50 | call._to_dirname = name; 51 | call._to_filename = path.join(name, call.to.name); 52 | log.debug('rename_get_to_dir(%s): done -> %s', call.to.dir, name); 53 | next(); 54 | }); 55 | } 56 | 57 | 58 | // Check if trying to rename a directory. This is not supported. 59 | function rename_stat_from(call, reply, next) { 60 | var log = call.log; 61 | 62 | log.debug('rename_stat_from(%s): entered', call._dirname); 63 | call.fs.stat(call._from_filename, function (err, stats) { 64 | if (err) { 65 | log.warn(err, 'rename_stat_from(%s): failed', call._from_filename); 66 | reply.error(nfs.NFS3ERR_IO); 67 | next(false); 68 | return; 69 | } 70 | 71 | if (stats.isDirectory()) { 72 | log.debug('rename_stat_from(%s): is a directory', 73 | call._from_filename); 74 | reply.error(nfs.NFS3ERR_ISDIR); 75 | next(false); 76 | return; 77 | } 78 | 79 | log.debug('rename_stat_from(%s): done', call._from_filename); 80 | next(); 81 | }); 82 | } 83 | 84 | function rename(call, reply, next) { 85 | var log = call.log; 86 | 87 | log.debug('rename(%s -> %s): entered', 88 | call._from_filename, call._to_filename); 89 | call.fs.rename(call._from_filename, call._to_filename, function (err) { 90 | if (err) { 91 | log.warn(err, 'rename(%s, %s): failed', call._from_filename, 92 | call._to_filename); 93 | reply.error(nfs.NFS3ERR_NOENT); 94 | next(false); 95 | return; 96 | } 97 | log.debug('rename: done'); 98 | reply.send(); 99 | next(); 100 | }); 101 | } 102 | 103 | 104 | 105 | ///--- Exports 106 | 107 | module.exports = function chain() { 108 | return ([ 109 | rename_get_from_dir, 110 | rename_get_to_dir, 111 | rename_stat_from, 112 | rename 113 | ]); 114 | }; 115 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var fs = require('fs'); 8 | var path = require('path'); 9 | 10 | var assert = require('assert-plus'); 11 | var bunyan = require('bunyan'); 12 | var manta = require('manta'); 13 | var once = require('once'); 14 | 15 | 16 | 17 | ///--- Helpers 18 | 19 | function _export(obj) { 20 | Object.keys(obj).forEach(function (k) { 21 | module.exports[k] = obj[k]; 22 | }); 23 | } 24 | 25 | 26 | 27 | ///--- API 28 | 29 | var BUNYAN_SERIALIZERS = { 30 | err: bunyan.stdSerializers.err, 31 | rpc_call: function serialize_rpc_call(c) { 32 | return (c ? c.toString() : null); 33 | }, 34 | rpc_reply: function serialize_rpc_reply(r) { 35 | return (r ? r.toString() : null); 36 | } 37 | }; 38 | 39 | 40 | function createLogger(name, stream) { 41 | var l = bunyan.createLogger({ 42 | name: name || path.basename(process.argv[1]), 43 | level: process.env.LOG_LEVEL || 'error', 44 | stream: stream || process.stdout, 45 | serializers: BUNYAN_SERIALIZERS 46 | }); 47 | 48 | return (l); 49 | } 50 | 51 | 52 | function createMantaClient(opts) { 53 | assert.object(opts, 'options'); 54 | assert.object(opts.log, 'options.log'); 55 | assert.optionalString(opts.keyId, 'options.keyId'); 56 | assert.optionalString(opts.keyFile, 'options.keyFile'); 57 | assert.optionalString(opts.url, 'options.url'); 58 | assert.optionalString(opts.user, 'options.user'); 59 | 60 | var sign; 61 | 62 | if (opts.keyFile) { 63 | // assume if opts.keyFile is set, all 4 opts are set in config file 64 | 65 | opts.log.info('createMantaClient: using privateKeySigner (%s)', 66 | opts.keyFile); 67 | try { 68 | sign = manta.privateKeySigner({ 69 | key: fs.readFileSync(opts.keyFile, 'utf8'), 70 | keyId: opts.keyId, 71 | user: opts.user 72 | }); 73 | } catch (e) { 74 | opts.log.error('createMantaClient: unable to read %s', 75 | opts.keyFile); 76 | process.exit(1); 77 | } 78 | } else { 79 | // use the manta config from the environment 80 | 81 | if (process.env.MANTA_NO_AUTH === 'true') { 82 | opts.log.info('createMantaClient: Not using signer'); 83 | sign = null; 84 | } else { 85 | if (!process.env.MANTA_USER || !process.env.MANTA_KEY_ID || 86 | !process.env.MANTA_URL) { 87 | opts.log.error('no manta configuration and no manta ' + 88 | 'environment variables'); 89 | process.exit(1); 90 | } 91 | 92 | opts.log.info('createMantaClient: using CLI signer'); 93 | sign = manta.cliSigner({ 94 | keyId: process.env.MANTA_KEY_ID, 95 | user: process.env.MANTA_USER 96 | }); 97 | } 98 | 99 | opts.url = process.env.MANTA_URL; 100 | } 101 | 102 | var client = manta.createClient({ 103 | sign: sign, 104 | log: opts.log.child({component: 'MantaClient'}, true), 105 | url: opts.url, 106 | user: opts.user 107 | }); 108 | 109 | return (client); 110 | } 111 | 112 | 113 | 114 | ///--- Exports 115 | 116 | module.exports = { 117 | bunyan: { 118 | createLogger: createLogger, 119 | serializers: BUNYAN_SERIALIZERS 120 | }, 121 | createLogger: createLogger, 122 | createMantaClient: createMantaClient 123 | }; 124 | 125 | _export(require('./mount')); 126 | _export(require('./nfs')); 127 | _export(require('./portmap')); 128 | -------------------------------------------------------------------------------- /lib/nfs/mkdir.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | var path = require('path'); 9 | 10 | var common = require('./common'); 11 | 12 | 13 | 14 | ///-- API 15 | 16 | function mkdir_lookup_dir(call, reply, next) { 17 | var log = call.log; 18 | 19 | log.debug('mkdir_lookup_dir(%s): entered', call.where.dir); 20 | call.fs.fhandle(call.where.dir, function (err, name) { 21 | if (err) { 22 | log.warn(err, 'mkdir_lookup_dir(%s): fhandle notfound', 23 | call.where.dir); 24 | reply.error(nfs.NFS3ERR_STALE); 25 | next(false); 26 | } else { 27 | call._dirname = name; 28 | call._filename = path.join(name, call.where.name); 29 | log.debug('mkdir_lookup_dir(%s): done -> %s', call.where.dir, name); 30 | next(); 31 | } 32 | }); 33 | } 34 | 35 | 36 | function mkdir_stat_dir(call, reply, next) { 37 | var log = call.log; 38 | 39 | log.debug('mkdir_stat_dir(%s): entered', call._dirname); 40 | call.fs.stat(call._dirname, function (err, stats) { 41 | if (err) { 42 | log.warn(err, 'mkdir_stat_dir(%s): failed', call._dirname); 43 | reply.error(nfs.NFS3ERR_IO); 44 | next(false); 45 | return; 46 | } 47 | if (!stats.isDirectory()) { 48 | log.warn(err, 'mkdir_stat_dir(%s): not a directory', call._dirname); 49 | reply.error(nfs.NFS3ERR_NOTDIR); 50 | next(false); 51 | return; 52 | } 53 | 54 | log.debug('mkdir_stat_dir(%s): done', call._dirname); 55 | next(); 56 | }); 57 | } 58 | 59 | 60 | function mkdir(call, reply, next) { 61 | var log = call.log; 62 | var mode; 63 | 64 | if (call.attributes.mode !== null) 65 | mode = call.attributes.mode; 66 | else 67 | mode = 0755; 68 | 69 | log.debug('mkdir(%s, %d): entered', call._filename, mode); 70 | call.fs.mkdir(call._filename, mode, function (err) { 71 | if (err) { 72 | log.warn(err, 'mkdir(%s): failed', call._filename); 73 | reply.error(nfs.NFS3ERR_NOTDIR); 74 | next(false); 75 | return; 76 | } 77 | 78 | log.debug('mkdir(%s): done', call._filename); 79 | next(); 80 | }); 81 | } 82 | 83 | 84 | function mkdir_lookup(call, reply, next) { 85 | var log = call.log; 86 | 87 | log.debug('mkdir_lookup(%s): entered', call._filename); 88 | call.fs.lookup(call._filename, function (err, fhandle) { 89 | if (err) { 90 | log.warn(err, 'mkdir_lookup(%s): failed', call._filename); 91 | reply.error(nfs.NFS3ERR_NOENT); 92 | next(false); 93 | return; 94 | } 95 | 96 | log.debug('mkdir_lookup(%s): done', fhandle); 97 | reply.obj = fhandle; 98 | next(); 99 | }); 100 | } 101 | 102 | 103 | function mkdir_stat_newdir(call, reply, next) { 104 | var log = call.log; 105 | 106 | log.debug('mkdir_stat_newdir(%s): entered', call._filename); 107 | call.fs.stat(call._filename, function (err, stats) { 108 | if (err) { 109 | log.warn(err, 'mkdir_stat_newdir(%s): failed', call._filename); 110 | reply.error(nfs.NFS3ERR_NOENT); 111 | next(false); 112 | return; 113 | } 114 | 115 | reply.setObjAttributes(stats); 116 | log.debug({stats: stats}, 'mkdir_stat_newdir(%s): done', 117 | call._filename); 118 | reply.send(); 119 | next(); 120 | }); 121 | } 122 | 123 | 124 | 125 | ///--- Exports 126 | 127 | module.exports = function chain() { 128 | return ([ 129 | mkdir_lookup_dir, 130 | mkdir_stat_dir, 131 | mkdir, 132 | mkdir_lookup, 133 | mkdir_stat_newdir 134 | ]); 135 | }; 136 | -------------------------------------------------------------------------------- /tools/mk/Makefile.node.defs: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | # 5 | # Makefile.node.defs: Makefile for building and bundling your own Node.js. 6 | # 7 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped 8 | # into other repos as-is without requiring any modifications. If you find 9 | # yourself changing this file, you should instead update the original copy in 10 | # eng.git and then update your repo to use the new version. 11 | # 12 | 13 | # 14 | # This Makefile facilitates building and bundling your own copy of Node.js in 15 | # your repo. All it does is define variables for node, node-waf, and npm for 16 | # you to use elsewhere in your Makefile and rules to build these tools when 17 | # needed. 18 | # 19 | # To use this facility, include "Makefile.node.defs", use the variables as 20 | # described below to define targets, and then include "Makefile.node.targ". 21 | # 22 | # There are two use cases addressed here: 23 | # 24 | # (1) Invoking node, node-waf, or npm as part of the build process, as in "npm 25 | # install" and "node-waf configure build". To facilitate this, this 26 | # Makefile defines Make variables NODE, NODE_WAF, and NPM that you can use 27 | # to invoke these commands during the build process. You MUST NOT assume 28 | # that these variables just evaluate to the filenames themselves, as they 29 | # may have environment variable definitions and other things that prevent 30 | # you from using them directly as a filename. If you want that, see (2). 31 | # 32 | # Wherever you use one of these variables, you MUST include a dependency on 33 | # the corresponding *_EXEC variable as well, like so: 34 | # 35 | # node_modules/restify: deps/restify $(NPM_EXEC) 36 | # $(NPM) install deps/restify 37 | # 38 | # or better, use an order-only dependency to avoid spurious rebuilds: 39 | # 40 | # node_modules/restify: deps/restify | $(NPM_EXEC) 41 | # $(NPM) install deps/restify 42 | # 43 | # Otherwise, the underlying file will not get built. We don't 44 | # automatically build them as part of "all" because that approach is 45 | # brittle. 46 | # 47 | # (2) Specifying paths for invoking node, node-waf, or npm at RUNTIME, as in 48 | # specifying the path to node used for the start method of your service's 49 | # SMF manifest. For this, this Makefile defines variables NODE_EXEC, 50 | # NODE_WAF_EXEC, and NPM_EXEC, which represent the relative paths of these 51 | # files from the root of the workspace. You MUST NOT use these variables 52 | # to invoke these commands during the build process. See (1) instead. 53 | # 54 | # However, in order to work at runtime, you must build the tool as well. 55 | # That is, if you use NODE_EXEC to specify the path to node, you must 56 | # depend on NODE_EXEC somewhere. This usually happens anyway because you 57 | # usually need them during the build process too, but if you don't then 58 | # you need to explicitly add NODE_EXEC (or whichever) to your "all" 59 | # target. 60 | # 61 | # When including this Makefile, you MAY also specify: 62 | # 63 | # BUILD top-level directory for built binaries 64 | # (default: "build") 65 | # 66 | # NODE_INSTALL where node should install its built items 67 | # (default: "$BUILD/node") 68 | # 69 | # NODE_CONFIG_FLAGS extra flags to pass to Node's "configure" 70 | # (default: "--with-dtrace" on SmartOS; empty 71 | # otherwise.) 72 | # 73 | 74 | TOP ?= $(error You must include Makefile.defs before this makefile) 75 | 76 | BUILD ?= build 77 | NODE_INSTALL ?= $(BUILD)/node 78 | DISTCLEAN_FILES += $(NODE_INSTALL) 79 | 80 | NODE_CONFIG_FLAGS += --prefix=$(TOP)/$(NODE_INSTALL) 81 | 82 | ifeq ($(shell uname -s),SunOS) 83 | NODE_CONFIG_FLAGS += --with-dtrace \ 84 | --openssl-libpath=/opt/local/lib \ 85 | --openssl-includes=/opt/local/include 86 | endif 87 | 88 | NODE_EXEC = $(NODE_INSTALL)/bin/node 89 | NODE_WAF_EXEC = $(NODE_INSTALL)/bin/node-waf 90 | NPM_EXEC = $(NODE_INSTALL)/bin/npm 91 | 92 | # Ensure these use absolute paths to the executables to allow running 93 | # from a dir other than the project top. 94 | NODE := $(TOP)/$(NODE_EXEC) 95 | NODE_WAF := $(TOP)/$(NODE_WAF_EXEC) 96 | NPM := PATH=$(TOP)/$(NODE_INSTALL)/bin:$(PATH) node $(TOP)/$(NPM_EXEC) 97 | -------------------------------------------------------------------------------- /lib/nfs/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var path = require('path'); 8 | 9 | var assert = require('assert-plus'); 10 | var nfs = require('nfs'); 11 | 12 | var auth = require('../auth'); 13 | 14 | var access = require('./access'); 15 | var create = require('./create'); 16 | var commit = require('./commit'); 17 | var fsinfo = require('./fsinfo'); 18 | var fsstat = require('./fsstat'); 19 | var getattr = require('./getattr'); 20 | var link = require('./link'); 21 | var lookup = require('./lookup'); 22 | var mkdir = require('./mkdir'); 23 | var mknod = require('./mknod'); 24 | var pathconf = require('./pathconf'); 25 | var read = require('./read'); 26 | var readdir = require('./readdir'); 27 | var readdirplus = require('./readdirplus'); 28 | var readlink = require('./readlink'); 29 | var remove = require('./remove'); 30 | var rename = require('./rename'); 31 | var rmdir = require('./rmdir'); 32 | var setattr = require('./setattr'); 33 | var symlink = require('./symlink'); 34 | var write = require('./write'); 35 | 36 | 37 | 38 | ///--- API 39 | 40 | function createNfsServer(opts) { 41 | assert.object(opts, 'options'); 42 | assert.object(opts.fd_cache, 'options.fd_cache'); 43 | assert.object(opts.fs, 'options.fs'); 44 | assert.object(opts.log, 'options.log'); 45 | assert.object(opts.manta, 'options.manta'); 46 | assert.string(opts.cachepath, 'options.cachepath'); 47 | assert.optionalObject(opts.hosts_allow, 'options.hosts_allow'); 48 | assert.optionalObject(opts.hosts_deny, 'options.hosts_deny'); 49 | 50 | // We have to check that each incoming NFS request is from an acceptable 51 | // host since each request is independent and there nothing that ties 52 | // the check we did in mountd to a request. 53 | function host_allowed(req, res) { 54 | assert.object(req.connection, 'req.connection'); 55 | 56 | var ipaddr = req.connection.remoteAddress; 57 | 58 | // hosts_deny entries are optional 59 | // if the address is present, disallow the mount 60 | if (req.hosts_deny && req.hosts_deny[ipaddr]) { 61 | req.log.warn('nfsd request from (%s) denied', ipaddr); 62 | res.error(nfs.MNT3ERR_ACCES); 63 | return (false); 64 | } 65 | 66 | // hosts_allow entries are optional 67 | // if hosts_allow exists, then the address must be preset or we disallow 68 | // the mount 69 | if (req.hosts_allow && !req.hosts_allow[ipaddr]) { 70 | req.log.warn('nfsd request from (%s) was not allowed', ipaddr); 71 | res.error(nfs.MNT3ERR_ACCES); 72 | return (false); 73 | } 74 | 75 | return (true); 76 | } 77 | 78 | var s = nfs.createNfsServer({ 79 | log: opts.log 80 | }); 81 | 82 | s.use(auth.authorize); 83 | s.use(function setup(req, res, next) { 84 | req.hosts_allow = opts.hosts_allow; 85 | req.hosts_deny = opts.hosts_deny; 86 | if (!host_allowed(req, res)) { 87 | next(false); 88 | return; 89 | } 90 | 91 | req.fd_cache = opts.fd_cache; 92 | req.manta = opts.manta; 93 | req.fs = opts.fs; 94 | req.cachepath = opts.cachepath; // needed for fsstat 95 | next(); 96 | }); 97 | 98 | s.access(access()); 99 | s.create(create()); 100 | s.commit(commit()); 101 | s.fsinfo(fsinfo()); 102 | s.fsstat(fsstat()); 103 | s.getattr(getattr()); 104 | s.link(link()); 105 | s.lookup(lookup()); 106 | s.mkdir(mkdir()); 107 | s.mknod(mknod()); 108 | s.pathconf(pathconf()); 109 | s.read(read()); 110 | s.readdir(readdir()); 111 | s.readdirplus(readdirplus()); 112 | s.readlink(readlink()); 113 | s.remove(remove()); 114 | s.rename(rename()); 115 | s.rmdir(rmdir()); 116 | s.setattr(setattr()); 117 | s.symlink(symlink()); 118 | s.write(write()); 119 | 120 | s.on('after', function (name, call, reply, err) { 121 | opts.log.debug({ 122 | procedure: name, 123 | rpc_call: call, 124 | rpc_reply: reply, 125 | err: err 126 | }, 'nfsd: %s handled', name); 127 | }); 128 | 129 | return (s); 130 | } 131 | 132 | 133 | 134 | ///--- Exports 135 | 136 | module.exports = { 137 | createNfsServer: createNfsServer 138 | }; 139 | -------------------------------------------------------------------------------- /lib/nfs/create.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var nfs = require('nfs'); 8 | var path = require('path'); 9 | 10 | var common = require('./common'); 11 | 12 | var fs = require('fs'); 13 | 14 | 15 | ///-- API 16 | 17 | function create_lookup_dir(call, reply, next) { 18 | var log = call.log; 19 | 20 | log.debug('create_lookup_dir(%s): entered', call.where.dir); 21 | call.fs.fhandle(call.where.dir, function (err, name) { 22 | if (err) { 23 | log.warn(err, 'create_lookup_dir(%s): fhandle notfound', 24 | call.where.dir); 25 | reply.error(nfs.NFS3ERR_STALE); 26 | next(false); 27 | } else { 28 | call._dirname = name; 29 | call._filename = path.join(name, call.where.name); 30 | log.debug('create_lookup_dir(%s): done -> %s', call.where.dir, 31 | name); 32 | next(); 33 | } 34 | }); 35 | } 36 | 37 | 38 | function do_create(flags, call, reply, next) { 39 | // ignore the passed in mode (call.obj_attributes.mode) since manta does 40 | // not support modes 41 | var mode = 0644; 42 | 43 | // We don't use the fd cache here since that only works with existing files 44 | // and always opens with the 'r+' flag. We need to create the file here 45 | // with either the 'w' or 'wx' flags. 46 | call.fs.open(call._filename, flags, mode, function (open_err, fd) { 47 | if (open_err) { 48 | call.log.warn(open_err, 'create: open failed'); 49 | reply.error(nfs.NFS3ERR_SERVERFAULT); 50 | next(false); 51 | return; 52 | } 53 | 54 | call.fs.close(fd, function (close_err) { 55 | // we're ignoring errors on close 56 | next(); 57 | }); 58 | }); 59 | } 60 | 61 | 62 | function create(call, reply, next) { 63 | var log = call.log; 64 | 65 | log.debug('create(%s, %d): entered', call.object, call.how); 66 | 67 | if (call.how === nfs.create_how.EXCLUSIVE) { 68 | call.fs.stat(call._filename, function (err, stats) { 69 | if (err && err.code === 'ENOENT') { 70 | // This is the "normal" code path (i.e. non-error) 71 | do_create('wx', call, reply, next); 72 | } else { 73 | log.debug('create (exclusive) file exists'); 74 | reply.error(nfs.NFS3ERR_EXIST); 75 | next(false); 76 | } 77 | }); 78 | } else if (call.how === nfs.create_how.UNCHECKED) { 79 | do_create('w', call, reply, next); 80 | } else { // call.how === nfs.create_how.GUARDED 81 | call.fs.stat(call._filename, function (err, stats) { 82 | if (err && err.code === 'ENOENT') { 83 | // This is the "normal" code path (i.e. non-error) 84 | do_create('w', call, reply, next); 85 | } else { 86 | log.debug('create (guarded) file exists'); 87 | reply.error(nfs.NFS3ERR_EXIST); 88 | next(false); 89 | } 90 | }); 91 | } 92 | } 93 | 94 | 95 | function create_lookup(call, reply, next) { 96 | var log = call.log; 97 | 98 | log.debug('create_lookup(%s): entered', call._filename); 99 | call.fs.lookup(call._filename, function (err, fhandle) { 100 | if (err) { 101 | log.warn(err, 'create_lookup(%s): failed', call._filename); 102 | reply.error(nfs.NFS3ERR_NOENT); 103 | next(false); 104 | return; 105 | } 106 | 107 | log.debug('create_lookup(%s): done', fhandle); 108 | reply.obj = fhandle; 109 | 110 | next(); 111 | }); 112 | } 113 | 114 | 115 | function create_stat(call, reply, next) { 116 | var log = call.log; 117 | 118 | log.debug('create_stat(%s): entered', call._filename); 119 | call.fs.stat(call._filename, function (err, stats) { 120 | if (err) { 121 | log.warn(err, 'create_stat(%s): failed', call._filename); 122 | reply.error(nfs.NFS3ERR_NOENT); 123 | next(false); 124 | return; 125 | } 126 | 127 | reply.setObjAttributes(stats); 128 | log.debug({stats: stats}, 'create_stat(%s): done', call._filename); 129 | reply.send(); 130 | next(); 131 | }); 132 | } 133 | 134 | 135 | ///--- Exports 136 | 137 | module.exports = function chain() { 138 | return ([ 139 | create_lookup_dir, 140 | create, 141 | create_lookup, 142 | create_stat 143 | ]); 144 | }; 145 | -------------------------------------------------------------------------------- /lib/nfs/readdirplus.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var path = require('path'); 8 | 9 | var nfs = require('nfs'); 10 | var vasync = require('vasync'); 11 | 12 | var common = require('./common'); 13 | 14 | var rpc = require('oncrpc'); 15 | var XDR = rpc.XDR; 16 | 17 | ///--- Helpers 18 | 19 | function rand() { 20 | return (Math.floor((Math.random() * Math.pow(2, 31)) + 1)); 21 | } 22 | 23 | 24 | 25 | ///-- API 26 | 27 | function readdirplus(call, reply, next) { 28 | var log = call.log; 29 | log.debug('readdirplus(%s): entered', call._filename); 30 | 31 | var error = null; 32 | var cook = 1; 33 | 34 | // Track the returned data size 35 | // status (4) + bool_dir_attrs (4) + fattr3.XDR_SIZE + 36 | // cookieverf3 (8) + bool_eof (4) + final_list_false (4) 37 | // See nfs readdirplus_reply.js. 38 | var totsz = 116; 39 | 40 | // total dir entries size, not including attributes and file handle portion 41 | var sz = 0; 42 | 43 | function process_entry(fname, nextent) { 44 | // The call cookie will be 0 on the initial call 45 | if (call.cookie != 0 && call.cookie >= cook) { 46 | // we need to scan the dir until we reach the right entry 47 | cook++; 48 | nextent(); 49 | return; 50 | } 51 | 52 | if (reply.eof === false || error) { 53 | // We hit our return limit on a previous entry, skip the rest 54 | nextent(); 55 | return; 56 | } 57 | 58 | // We need to track the basic returned data size to be sure we fit in 59 | // call.dircount bytes. 60 | // list_true (4) + fileid (8) + cookie (8) + name_len 61 | var delta = 20 + XDR.byteLength(fname); 62 | if ((sz + delta) > call.dircount) { 63 | reply.eof = false; 64 | nextent(); 65 | return; 66 | } 67 | sz += delta; 68 | 69 | // We also need to track the total returned data size to be sure we 70 | // fit in call.maxcount bytes. 71 | // list_true (4) + fileid (8) + cookie (8) + name_len + 72 | // bool_name_attr (4) + name_attr_len + 73 | // bool_name_handle (4) + name_handle_len 74 | delta = 28 + XDR.byteLength(fname) + 84 + 64; 75 | if ((totsz + delta) > call.maxcount) { 76 | reply.eof = false; 77 | nextent(); 78 | return; 79 | } 80 | totsz += delta; 81 | 82 | var p = path.join(call._filename, fname); 83 | call.fs.stat(p, function (err2, stats) { 84 | if (err2) { 85 | log.warn(err2, 'readdirplus(%s): stat failed', p); 86 | error = error || nfs.NFS3ERR_IO; 87 | nextent(); 88 | } else { 89 | call.fs.lookup(p, function (err3, fhandle) { 90 | if (err3) { 91 | log.warn(err3, 'readdirplus(%s): lu failed', p); 92 | error = error || nfs.NFS3ERR_IO; 93 | } else { 94 | reply.addEntry({ 95 | fileid: common.hash(p), 96 | name: fname, 97 | cookie: cook++, 98 | name_attributes: nfs.create_fattr3(stats), 99 | name_handle: fhandle 100 | }); 101 | } 102 | nextent(); 103 | }); 104 | } 105 | }); 106 | } 107 | 108 | function all_done() { 109 | if (error) { 110 | reply.error(error); 111 | next(false); 112 | } else { 113 | call.fs.stat(call._filename, function (err, stats) { 114 | if (err) { 115 | log.warn(err, 'readdirplus(%s): dir stat failed', 116 | call._filename); 117 | } else { 118 | reply.setDirAttributes(stats); 119 | } 120 | log.debug('readdirplus(%s): done', call._filename); 121 | reply.send(); 122 | next(); 123 | }); 124 | } 125 | } 126 | 127 | call.fs.readdir(call._filename, function (err1, files) { 128 | if (err1) { 129 | log.warn(err1, 'readdirplus(%s): rd failed', call._filename); 130 | error = (err1.code === 'ENOTDIR' ? 131 | nfs.NFS3ERR_NOTDIR : 132 | nfs.NFS3ERR_IO); 133 | reply.error(error); 134 | next(false); 135 | return; 136 | } 137 | 138 | // The cookieverf will be 0 on the initial call. 139 | var h = common.hash(call._filename); 140 | if (call.cookieverf.readUInt32LE(0) != 0) { 141 | // This is a follow-up call, confirm cookie. 142 | if (call.cookieverf.readUInt32LE(0) != h) { 143 | reply.error(nfs.NFS3ERR_BAD_COOKIE); 144 | next(false); 145 | return; 146 | } 147 | } 148 | 149 | reply.eof = true; 150 | 151 | reply.cookieverf = new Buffer(8); 152 | reply.cookieverf.fill(0); 153 | reply.cookieverf.writeUInt32LE(h, 0, true); 154 | 155 | vasync.forEachPipeline({ 156 | 'func': process_entry, 157 | 'inputs': files 158 | }, all_done); 159 | }); 160 | } 161 | 162 | 163 | 164 | ///--- Exports 165 | 166 | module.exports = function chain() { 167 | return ([ 168 | common.fhandle_to_filename, 169 | readdirplus 170 | ]); 171 | }; 172 | -------------------------------------------------------------------------------- /tools/mk/Makefile.node_prebuilt.defs: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | # 5 | # Makefile.node_prebuilt.defs: Makefile for including a prebuilt Node.js build. 6 | # 7 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped 8 | # into other repos as-is without requiring any modifications. If you find 9 | # yourself changing this file, you should instead update the original copy in 10 | # eng.git and then update your repo to use the new version. 11 | # 12 | 13 | # 14 | # This Makefile facilitates downloading and bundling a prebuilt node.js 15 | # build (using the 'sdcnode' distro builds). This is an alternative to 16 | # the "Makefile.node.*" makefiles for *building* a node from source. 17 | # 18 | # Usage: 19 | # 20 | # - Define `NODE_PREBUILT_VERSION` in your Makefile to choose a node version. 21 | # E.g.: `NODE_PREBUILT_VERSION=v0.6.19`. See other optional variables 22 | # below. 23 | # - `include tools/mk/Makefile.node_prebuilt.defs` after this in your Makefile. 24 | # - `include tools/mk/Makefile.node_prebuilt.targ` near the end of your 25 | # Makefile. 26 | # - Have at least one of your Makefile targets depend on either `$(NODE_EXEC)` 27 | # or `$(NPM_EXEC)`. E.g.: 28 | # 29 | # node_modules/restify: deps/restify $(NPM_EXEC) 30 | # $(NPM) install deps/restify 31 | # 32 | # or better, use an order-only dependency to avoid spurious rebuilds: 33 | # 34 | # node_modules/restify: deps/restify | $(NPM_EXEC) 35 | # $(NPM) install deps/restify 36 | # 37 | # - Use `$(NPM)` or `$(NODE)` to use your node build. 38 | # - Include the "$(NODE_INSTALL)" tree in your release package. 39 | # 40 | # 41 | # When including this Makefile, you MUST also specify: 42 | # 43 | # NODE_PREBUILT_VERSION The node version in the prebuilt 'sdcnode' 44 | # package to use. Typically this is one of the 45 | # node version tags, e.g. "v0.6.18" but it 46 | # can be any commitish. 47 | # 48 | # When including this Makefile, you MAY also specify: 49 | # 50 | # NODE_PREBUILT_DIR The dir in which to find sdcnode builds. This 51 | # can either be a *local directory* or *a 52 | # URL* dir (with trailing '/') which serves 53 | # Apache/Nginx dir listing HTML. 54 | # (default: sdcnode master build dir on stuff) 55 | # 56 | # NODE_PREBUILT_TAG The 'sdcnode' project supports special 57 | # configuration builds of node, e.g. say a 58 | # build configured `--without-ssl`. These 59 | # special configurations are given a tag, e.g. 60 | # 'gz', that is used in the filename. Optionally 61 | # specify a tag name here. 62 | # (default: empty) 63 | # 64 | # NODE_PREBUILT_BRANCH Specify a particular branch of 'sdcnode' builds 65 | # from which to pull. Generally one should stick 66 | # with the default. 67 | # (default: master) 68 | # 69 | # NODE_PREBUILT_IMAGE If you have a zone image that differs from that 70 | # for an sdcnode build that you want to use (potential compat 71 | # issues be damned), then set this to the UUID of the sdcnode 72 | # build you want. See here for available build image uuids: 73 | # 74 | # 75 | # BUILD top-level directory for built binaries 76 | # (default: "build") 77 | # 78 | # NODE_INSTALL where node should install its built items 79 | # (default: "$BUILD/node") 80 | # 81 | # 82 | # Dev Notes: 83 | # 84 | # This works by getting "NODE_PREBUILT_NAME" from the provided "NODE_PREBUILT_*" 85 | # vars and the image version (via 'mdata-get sdc:image_uuid'). The image uuid is 86 | # included to ensure an exact match with the build machine. This name (e.g. 87 | # "v0.6.18-zone-$uuid") is used to find a matching "sdcnode-$name-*.tgz" build 88 | # in "NODE_PREBUILT_DIR" (either a local directory or a URL). That tarball is 89 | # downloaded and extracted into "NODE_INSTALL". 90 | # 91 | # The "*_EXEC" vars are set to named symlinks, e.g. 92 | # "build/prebuilt-node-v0.6.18-$uuid", so that a change of selected node 93 | # build (say the developer changes NODE_PREBUILT_VERSION) will recreate the 94 | # node install. 95 | # 96 | # See for details on 'sdcnode-*' 97 | # package naming. 98 | # 99 | 100 | TOP ?= $(error You must include Makefile.defs before this makefile) 101 | NODE_PREBUILT_VERSION ?= $(error NODE_PREBUILT_VERSION is not set.) 102 | 103 | 104 | BUILD ?= build 105 | NODE_INSTALL ?= $(BUILD)/node 106 | DISTCLEAN_FILES += $(NODE_INSTALL) \ 107 | $(BUILD)/prebuilt-node-* $(BUILD)/prebuilt-npm-* 108 | 109 | NODE_PREBUILT_BRANCH ?= master 110 | NODE_PREBUILT_IMAGE ?= $(shell pfexec mdata-get sdc:image_uuid) 111 | ifeq ($(NODE_PREBUILT_TAG),) 112 | NODE_PREBUILT_NAME := $(NODE_PREBUILT_VERSION)-$(NODE_PREBUILT_IMAGE) 113 | else 114 | NODE_PREBUILT_NAME := $(NODE_PREBUILT_VERSION)-$(NODE_PREBUILT_TAG)-$(NODE_PREBUILT_IMAGE) 115 | endif 116 | NODE_PREBUILT_PATTERN := sdcnode-$(NODE_PREBUILT_NAME)-$(NODE_PREBUILT_BRANCH)-.*\.tgz 117 | NODE_PREBUILT_DIR ?= https://download.joyent.com/pub/build/sdcnode/$(NODE_PREBUILT_IMAGE)/$(NODE_PREBUILT_BRANCH)-latest/sdcnode/ 118 | ifeq ($(shell echo $(NODE_PREBUILT_DIR) | cut -c 1-4),http) 119 | NODE_PREBUILT_BASE := $(shell curl -ksS --fail --connect-timeout 5 $(NODE_PREBUILT_DIR) | grep 'href=' | cut -d'"' -f2 | grep "^$(NODE_PREBUILT_PATTERN)$$" | sort | tail -1) 120 | ifneq ($(NODE_PREBUILT_BASE),) 121 | NODE_PREBUILT_TARBALL := $(NODE_PREBUILT_DIR)$(NODE_PREBUILT_BASE) 122 | endif 123 | else 124 | NODE_PREBUILT_BASE := $(shell ls -1 $(NODE_PREBUILT_DIR)/ | grep "^$(NODE_PREBUILT_PATTERN)$$" 2>/dev/null | sort | tail -1) 125 | ifneq ($(NODE_PREBUILT_BASE),) 126 | NODE_PREBUILT_TARBALL := $(NODE_PREBUILT_DIR)/$(NODE_PREBUILT_BASE) 127 | endif 128 | endif 129 | ifeq ($(NODE_PREBUILT_TARBALL),) 130 | NODE_PREBUILT_TARBALL = $(error NODE_PREBUILT_TARBALL is empty: no '$(NODE_PREBUILT_DIR)/$(NODE_PREBUILT_PATTERN)' found) 131 | endif 132 | 133 | 134 | # Prebuild-specific paths for the "*_EXEC" vars to ensure that 135 | # a prebuild change (e.g. if master Makefile's NODE_PREBUILT_VERSION 136 | # choice changes) causes a install of the new node. 137 | NODE_EXEC := $(BUILD)/prebuilt-node-$(NODE_PREBUILT_NAME) 138 | NODE_WAF_EXEC := $(BUILD)/prebuilt-node-waf-$(NODE_PREBUILT_NAME) 139 | NPM_EXEC := $(BUILD)/prebuilt-npm-$(NODE_PREBUILT_NAME) 140 | 141 | # Ensure these use absolute paths to the executables to allow running 142 | # from a dir other than the project top. 143 | NODE := $(TOP)/$(NODE_INSTALL)/bin/node 144 | NODE_WAF := $(TOP)/$(NODE_INSTALL)/bin/node-waf 145 | NPM := PATH=$(TOP)/$(NODE_INSTALL)/bin:$(PATH) node $(TOP)/$(NODE_INSTALL)/bin/npm 146 | -------------------------------------------------------------------------------- /lib/mount.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var fs = require('fs'); 8 | var path = require('path'); 9 | var util = require('util'); 10 | 11 | var assert = require('assert-plus'); 12 | var clone = require('clone'); 13 | var nfs = require('nfs'); 14 | var once = require('once'); 15 | var statvfs = require('statvfs'); 16 | 17 | var auth = require('./auth'); 18 | var common = require('./nfs/common'); 19 | 20 | 21 | 22 | ///--- Globals 23 | 24 | var sprintf = util.format; 25 | 26 | var MNTPATHLEN = 1024; // This is defined by the RFC 27 | 28 | 29 | 30 | ///--- API 31 | 32 | function ensure_allowed(req, res, next) { 33 | assert.object(req.connection, 'req.connection'); 34 | 35 | var ipaddr = req.connection.remoteAddress; 36 | 37 | // hosts_deny entries are optional 38 | // if the address is present, disallow the mount 39 | if (req.hosts_deny && req.hosts_deny[ipaddr]) { 40 | req.log.warn('mountd request from (%s) denied', ipaddr); 41 | res.error(nfs.MNT3ERR_ACCES); 42 | next(false); 43 | return; 44 | } 45 | 46 | // hosts_allow entries are optional 47 | // if hosts_allow exists, then the address must be preset or we disallow 48 | // the mount 49 | if (req.hosts_allow && !req.hosts_allow[ipaddr]) { 50 | req.log.warn('mountd request from (%s) was not allowed', ipaddr); 51 | res.error(nfs.MNT3ERR_ACCES); 52 | next(false); 53 | return; 54 | } 55 | 56 | next(); 57 | } 58 | 59 | 60 | function ensure_exports(req, res, next) { 61 | assert.string(req.dirpath, 'req.dirpath'); 62 | 63 | var p = path.normalize(req.dirpath); 64 | 65 | if (p.length > MNTPATHLEN) { 66 | res.error(nfs.MNT3ERR_NAMETOOLONG); 67 | next(false); 68 | return; 69 | } 70 | 71 | // export entries are optional 72 | if (req.exports) { 73 | // since we have exports, we must check each one since the client may 74 | // be trying to mount a subdir of an export 75 | var found = false; 76 | for (var i in req.exports) { 77 | if (p.indexOf(i) === 0) { 78 | found = true; 79 | break; 80 | } 81 | } 82 | 83 | if (!found) { 84 | req.log.warn('mountd (%s) is not exported', p); 85 | res.error(nfs.MNT3ERR_ACCES); 86 | next(false); 87 | return; 88 | } 89 | } 90 | 91 | req._dirpath = p; 92 | next(); 93 | } 94 | 95 | 96 | function ensure_manta_dir(req, res, next) { 97 | var p = req._dirpath; 98 | 99 | req.log.debug('ensure_manta_directory(%s): entered', p); 100 | req.manta.info(p, function (err, info) { 101 | if (err) { 102 | req.log.warn(err, 'ensure_manta_directory: info(%s) failed', p); 103 | res.error(nfs.MNT3ERR_SERVERFAULT); 104 | next(false); 105 | return; 106 | } 107 | 108 | if (info.extension !== 'directory') { 109 | req.log.warn({ 110 | path: p, 111 | info: info 112 | }, 'mount: manta location is not a directory'); 113 | res.error(nfs.MNT3ERR_NOTDIR); 114 | next(false); 115 | return; 116 | } 117 | 118 | req.log.debug({ 119 | info: info 120 | }, 'ensure_manta_directory(%s): done', p); 121 | 122 | req.info = info; 123 | next(); 124 | }); 125 | } 126 | 127 | 128 | function mount(call, reply, next) { 129 | var log = call.log; 130 | 131 | log.debug('mount(%s): entered', call._dirpath); 132 | call.fs.stat(call._dirpath, function (serr, dummystats) { 133 | if (serr) { 134 | log.warn(serr, 'mount(%s): failed to stat', call._dirpath); 135 | reply.error(nfs.MNT3ERR_SERVERFAULT); 136 | next(false); 137 | return; 138 | } 139 | 140 | call.fs.lookup(call._dirpath, function (lerr, fhandle) { 141 | if (lerr) { 142 | log.warn(lerr, 'mount(%s): failed to lookup', call._dirpath); 143 | reply.error(nfs.MNT3ERR_SERVERFAULT); 144 | next(false); 145 | return; 146 | } 147 | 148 | var ipaddr = call.connection.remoteAddress; 149 | 150 | reply.setFileHandle(fhandle); 151 | log.info('mount(%s) from (%s): done -> %s', call._dirpath, ipaddr, 152 | fhandle); 153 | reply.send(); 154 | next(); 155 | 156 | // We assume the client is going to immediately do an ls, so just 157 | // cache the root directory 158 | call.fs.readdir(call._dirpath, function () {}); 159 | }); 160 | }); 161 | } 162 | 163 | 164 | function umount(call, reply, next) { 165 | var log = call.log; 166 | 167 | // We don't invoke call.fs.shutdown here since the server is still running 168 | // and they may want to mount again later. 169 | 170 | var ipaddr = call.connection.remoteAddress; 171 | log.info('umount(%s) from (%s) done', call._dirpath, ipaddr); 172 | reply.send(); 173 | next(); 174 | } 175 | 176 | 177 | function createMountServer(opts) { 178 | assert.object(opts, 'options'); 179 | assert.optionalObject(opts.exports, 'options.exports'); 180 | assert.optionalObject(opts.hosts_allow, 'options.hosts_allow'); 181 | assert.optionalObject(opts.hosts_deny, 'options.hosts_deny'); 182 | assert.object(opts.log, 'options.log'); 183 | assert.object(opts.manta, 'options.manta'); 184 | assert.object(opts.fs, 'options.fs'); 185 | 186 | var s = nfs.createMountServer({ 187 | log: opts.log 188 | }); 189 | 190 | s.use(auth.authorize); 191 | s.use(function setup(req, res, next) { 192 | req.exports = opts.exports; 193 | req.hosts_allow = opts.hosts_allow; 194 | req.hosts_deny = opts.hosts_deny; 195 | req.manta = opts.manta; 196 | req.fs = opts.fs; 197 | next(); 198 | }); 199 | s.mnt(ensure_allowed, 200 | ensure_exports, 201 | ensure_manta_dir, 202 | mount); 203 | 204 | s.umnt(ensure_allowed, 205 | ensure_exports, 206 | umount); 207 | 208 | s.on('after', function (name, call, reply, err) { 209 | opts.log.debug({ 210 | procedure: name, 211 | rpc_call: call, 212 | rpc_reply: reply, 213 | err: err 214 | }, 'mountd: %s handled', name); 215 | }); 216 | 217 | return (s); 218 | } 219 | 220 | 221 | 222 | ///--- Exports 223 | 224 | module.exports = { 225 | createMountServer: createMountServer 226 | }; 227 | -------------------------------------------------------------------------------- /tools/jsl.web.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Configuration File for JavaScript Lint 3 | # Developed by Matthias Miller (http://www.JavaScriptLint.com) 4 | # 5 | # This configuration file can be used to lint a collection of scripts, or to enable 6 | # or disable warnings for scripts that are linted via the command line. 7 | # 8 | 9 | ### Warnings 10 | # Enable or disable warnings based on requirements. 11 | # Use "+WarningName" to display or "-WarningName" to suppress. 12 | # 13 | +ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent 14 | +ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity 15 | +ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement 16 | +anon_no_return_value # anonymous function does not always return value 17 | +assign_to_function_call # assignment to a function call 18 | -block_without_braces # block statement without curly braces 19 | +comma_separated_stmts # multiple statements separated by commas (use semicolons?) 20 | +comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) 21 | +default_not_at_end # the default case is not at the end of the switch statement 22 | +dup_option_explicit # duplicate "option explicit" control comment 23 | +duplicate_case_in_switch # duplicate case in switch statement 24 | +duplicate_formal # duplicate formal argument {name} 25 | +empty_statement # empty statement or extra semicolon 26 | +identifier_hides_another # identifer {name} hides an identifier in a parent scope 27 | +inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement 28 | +incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version. 29 | +invalid_fallthru # unexpected "fallthru" control comment 30 | +invalid_pass # unexpected "pass" control comment 31 | +jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax 32 | +leading_decimal_point # leading decimal point may indicate a number or an object member 33 | +legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax 34 | +meaningless_block # meaningless block; curly braces have no impact 35 | +mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence 36 | +misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma 37 | +missing_break # missing break statement 38 | +missing_break_for_last_case # missing break statement for last case in switch 39 | +missing_default_case # missing default case in switch statement 40 | +missing_option_explicit # the "option explicit" control comment is missing 41 | +missing_semicolon # missing semicolon 42 | +missing_semicolon_for_lambda # missing semicolon for lambda assignment 43 | +multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs 44 | +nested_comment # nested comment 45 | +no_return_value # function {name} does not always return a value 46 | +octal_number # leading zeros make an octal number 47 | +parseint_missing_radix # parseInt missing radix parameter 48 | +partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag 49 | +redeclared_var # redeclaration of {name} 50 | +trailing_comma_in_array # extra comma is not recommended in array initializers 51 | +trailing_decimal_point # trailing decimal point may indicate a number or an object member 52 | +undeclared_identifier # undeclared identifier: {name} 53 | +unreachable_code # unreachable code 54 | +unreferenced_argument # argument declared but never referenced: {name} 55 | +unreferenced_function # function is declared but never referenced: {name} 56 | +unreferenced_variable # variable is declared but never referenced: {name} 57 | +unsupported_version # JavaScript {version} is not supported 58 | +use_of_label # use of label 59 | +useless_assign # useless assignment 60 | +useless_comparison # useless comparison; comparing identical expressions 61 | +useless_quotes # the quotation marks are unnecessary 62 | +useless_void # use of the void type may be unnecessary (void is always undefined) 63 | +var_hides_arg # variable {name} hides argument 64 | +want_assign_or_call # expected an assignment or function call 65 | +with_statement # with statement hides undeclared variables; use temporary variable instead 66 | 67 | 68 | ### Output format 69 | # Customize the format of the error message. 70 | # __FILE__ indicates current file path 71 | # __FILENAME__ indicates current file name 72 | # __LINE__ indicates current line 73 | # __COL__ indicates current column 74 | # __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__) 75 | # __ERROR_NAME__ indicates error name (used in configuration file) 76 | # __ERROR_PREFIX__ indicates error prefix 77 | # __ERROR_MSG__ indicates error message 78 | # 79 | # For machine-friendly output, the output format can be prefixed with 80 | # "encode:". If specified, all items will be encoded with C-slashes. 81 | # 82 | # Visual Studio syntax (default): 83 | +output-format __FILE__(__LINE__): __ERROR__ 84 | # Alternative syntax: 85 | #+output-format __FILE__:__LINE__: __ERROR__ 86 | 87 | 88 | ### Context 89 | # Show the in-line position of the error. 90 | # Use "+context" to display or "-context" to suppress. 91 | # 92 | +context 93 | 94 | 95 | ### Control Comments 96 | # Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for 97 | # the /*@keyword@*/ control comments and JScript conditional comments. (The latter is 98 | # enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, 99 | # although legacy control comments are enabled by default for backward compatibility. 100 | # 101 | -legacy_control_comments 102 | 103 | 104 | ### Defining identifiers 105 | # By default, "option explicit" is enabled on a per-file basis. 106 | # To enable this for all files, use "+always_use_option_explicit" 107 | +always_use_option_explicit 108 | 109 | # Define certain identifiers of which the lint is not aware. 110 | # (Use this in conjunction with the "undeclared identifier" warning.) 111 | # 112 | # Common uses for webpages might be: 113 | +define JSON 114 | +define Math 115 | +define $ 116 | +define XMLHttpRequest 117 | +define alert 118 | +define clearInterval 119 | +define clearTimeout 120 | +define confirm 121 | +define document 122 | +define setInterval 123 | +define setTimeout 124 | +define window 125 | 126 | ### JavaScript Version 127 | # To change the default JavaScript version: 128 | #+default-type text/javascript;version=1.5 129 | #+default-type text/javascript;e4x=1 130 | 131 | ### Files 132 | # Specify which files to lint 133 | # Use "+recurse" to enable recursion (disabled by default). 134 | # To add a set of files, use "+process FileName", "+process Folder\Path\*.js", 135 | # or "+process Folder\Path\*.htm". 136 | # 137 | 138 | -------------------------------------------------------------------------------- /tools/jsl.node.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Configuration File for JavaScript Lint 3 | # 4 | # This configuration file can be used to lint a collection of scripts, or to enable 5 | # or disable warnings for scripts that are linted via the command line. 6 | # 7 | 8 | ### Warnings 9 | # Enable or disable warnings based on requirements. 10 | # Use "+WarningName" to display or "-WarningName" to suppress. 11 | # 12 | +ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent 13 | +ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity 14 | +ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement 15 | +anon_no_return_value # anonymous function does not always return value 16 | +assign_to_function_call # assignment to a function call 17 | -block_without_braces # block statement without curly braces 18 | +comma_separated_stmts # multiple statements separated by commas (use semicolons?) 19 | +comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) 20 | +default_not_at_end # the default case is not at the end of the switch statement 21 | +dup_option_explicit # duplicate "option explicit" control comment 22 | +duplicate_case_in_switch # duplicate case in switch statement 23 | +duplicate_formal # duplicate formal argument {name} 24 | +empty_statement # empty statement or extra semicolon 25 | +identifier_hides_another # identifer {name} hides an identifier in a parent scope 26 | -inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement 27 | +incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version. 28 | +invalid_fallthru # unexpected "fallthru" control comment 29 | +invalid_pass # unexpected "pass" control comment 30 | +jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax 31 | +leading_decimal_point # leading decimal point may indicate a number or an object member 32 | +legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax 33 | +meaningless_block # meaningless block; curly braces have no impact 34 | +mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence 35 | +misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma 36 | +missing_break # missing break statement 37 | +missing_break_for_last_case # missing break statement for last case in switch 38 | +missing_default_case # missing default case in switch statement 39 | +missing_option_explicit # the "option explicit" control comment is missing 40 | +missing_semicolon # missing semicolon 41 | +missing_semicolon_for_lambda # missing semicolon for lambda assignment 42 | +multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs 43 | +nested_comment # nested comment 44 | +no_return_value # function {name} does not always return a value 45 | -octal_number # leading zeros make an octal number 46 | +parseint_missing_radix # parseInt missing radix parameter 47 | +partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag 48 | +redeclared_var # redeclaration of {name} 49 | +trailing_comma_in_array # extra comma is not recommended in array initializers 50 | +trailing_decimal_point # trailing decimal point may indicate a number or an object member 51 | +undeclared_identifier # undeclared identifier: {name} 52 | +unreachable_code # unreachable code 53 | -unreferenced_argument # argument declared but never referenced: {name} 54 | -unreferenced_function # function is declared but never referenced: {name} 55 | +unreferenced_variable # variable is declared but never referenced: {name} 56 | +unsupported_version # JavaScript {version} is not supported 57 | +use_of_label # use of label 58 | +useless_assign # useless assignment 59 | +useless_comparison # useless comparison; comparing identical expressions 60 | -useless_quotes # the quotation marks are unnecessary 61 | +useless_void # use of the void type may be unnecessary (void is always undefined) 62 | +var_hides_arg # variable {name} hides argument 63 | +want_assign_or_call # expected an assignment or function call 64 | +with_statement # with statement hides undeclared variables; use temporary variable instead 65 | 66 | 67 | ### Output format 68 | # Customize the format of the error message. 69 | # __FILE__ indicates current file path 70 | # __FILENAME__ indicates current file name 71 | # __LINE__ indicates current line 72 | # __COL__ indicates current column 73 | # __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__) 74 | # __ERROR_NAME__ indicates error name (used in configuration file) 75 | # __ERROR_PREFIX__ indicates error prefix 76 | # __ERROR_MSG__ indicates error message 77 | # 78 | # For machine-friendly output, the output format can be prefixed with 79 | # "encode:". If specified, all items will be encoded with C-slashes. 80 | # 81 | # Visual Studio syntax (default): 82 | +output-format __FILE__(__LINE__): __ERROR__ 83 | # Alternative syntax: 84 | #+output-format __FILE__:__LINE__: __ERROR__ 85 | 86 | 87 | ### Context 88 | # Show the in-line position of the error. 89 | # Use "+context" to display or "-context" to suppress. 90 | # 91 | +context 92 | 93 | 94 | ### Control Comments 95 | # Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for 96 | # the /*@keyword@*/ control comments and JScript conditional comments. (The latter is 97 | # enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, 98 | # although legacy control comments are enabled by default for backward compatibility. 99 | # 100 | -legacy_control_comments 101 | 102 | 103 | ### Defining identifiers 104 | # By default, "option explicit" is enabled on a per-file basis. 105 | # To enable this for all files, use "+always_use_option_explicit" 106 | -always_use_option_explicit 107 | 108 | # Define certain identifiers of which the lint is not aware. 109 | # (Use this in conjunction with the "undeclared identifier" warning.) 110 | # 111 | # Common uses for webpages might be: 112 | +define before 113 | +define after 114 | +define test 115 | +define __dirname 116 | +define __filename 117 | +define clearInterval 118 | +define clearTimeout 119 | +define console 120 | +define exports 121 | +define global 122 | +define module 123 | +define process 124 | +define require 125 | +define setImmediate 126 | +define setInterval 127 | +define setTimeout 128 | +define Buffer 129 | +define JSON 130 | +define Math 131 | 132 | ### JavaScript Version 133 | # To change the default JavaScript version: 134 | #+default-type text/javascript;version=1.5 135 | #+default-type text/javascript;e4x=1 136 | 137 | ### Files 138 | # Specify which files to lint 139 | # Use "+recurse" to enable recursion (disabled by default). 140 | # To add a set of files, use "+process FileName", "+process Folder\Path\*.js", 141 | # or "+process Folder\Path\*.htm". 142 | # 143 | 144 | -------------------------------------------------------------------------------- /tools/mk/Makefile.targ: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | # 5 | # Makefile.targ: common targets. 6 | # 7 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped 8 | # into other repos as-is without requiring any modifications. If you find 9 | # yourself changing this file, you should instead update the original copy in 10 | # eng.git and then update your repo to use the new version. 11 | # 12 | # This Makefile defines several useful targets and rules. You can use it by 13 | # including it from a Makefile that specifies some of the variables below. 14 | # 15 | # Targets defined in this Makefile: 16 | # 17 | # check Checks JavaScript files for lint and style 18 | # Checks bash scripts for syntax 19 | # Checks SMF manifests for validity against the SMF DTD 20 | # 21 | # clean Removes built files 22 | # 23 | # docs Builds restdown documentation in docs/ 24 | # 25 | # prepush Depends on "check" and "test" 26 | # 27 | # test Does nothing (you should override this) 28 | # 29 | # xref Generates cscope (source cross-reference index) 30 | # 31 | # For details on what these targets are supposed to do, see the Joyent 32 | # Engineering Guide. 33 | # 34 | # To make use of these targets, you'll need to set some of these variables. Any 35 | # variables left unset will simply not be used. 36 | # 37 | # BASH_FILES Bash scripts to check for syntax 38 | # (paths relative to top-level Makefile) 39 | # 40 | # CLEAN_FILES Files to remove as part of the "clean" target. Note 41 | # that files generated by targets in this Makefile are 42 | # automatically included in CLEAN_FILES. These include 43 | # restdown-generated HTML and JSON files. 44 | # 45 | # DOC_FILES Restdown (documentation source) files. These are 46 | # assumed to be contained in "docs/", and must NOT 47 | # contain the "docs/" prefix. 48 | # 49 | # JSL_CONF_NODE Specify JavaScriptLint configuration files 50 | # JSL_CONF_WEB (paths relative to top-level Makefile) 51 | # 52 | # Node.js and Web configuration files are separate 53 | # because you'll usually want different global variable 54 | # configurations. If no file is specified, none is given 55 | # to jsl, which causes it to use a default configuration, 56 | # which probably isn't what you want. 57 | # 58 | # JSL_FILES_NODE JavaScript files to check with Node config file. 59 | # JSL_FILES_WEB JavaScript files to check with Web config file. 60 | # 61 | # JSON_FILES JSON files to be validated 62 | # 63 | # JSSTYLE_FILES JavaScript files to be style-checked 64 | # 65 | # You can also override these variables: 66 | # 67 | # BASH Path to bash (default: "bash") 68 | # 69 | # CSCOPE_DIRS Directories to search for source files for the cscope 70 | # index. (default: ".") 71 | # 72 | # JSL Path to JavaScriptLint (default: "jsl") 73 | # 74 | # JSL_FLAGS_NODE Additional flags to pass through to JSL 75 | # JSL_FLAGS_WEB 76 | # JSL_FLAGS 77 | # 78 | # JSON Path to json tool (default: "json") 79 | # 80 | # JSSTYLE Path to jsstyle (default: "jsstyle") 81 | # 82 | # JSSTYLE_FLAGS Additional flags to pass through to jsstyle 83 | # 84 | # RESTDOWN_EXT By default '.restdown' is required for DOC_FILES 85 | # (see above). If you want to use, say, '.md' instead, then 86 | # set 'RESTDOWN_EXT=.md' in your Makefile. 87 | # 88 | 89 | # 90 | # Defaults for the various tools we use. 91 | # 92 | BASH ?= bash 93 | BASHSTYLE ?= tools/bashstyle 94 | CP ?= cp 95 | CSCOPE ?= cscope 96 | CSCOPE_DIRS ?= . 97 | JSL ?= jsl 98 | JSON ?= json 99 | JSSTYLE ?= jsstyle 100 | MKDIR ?= mkdir -p 101 | MV ?= mv 102 | RESTDOWN_FLAGS ?= 103 | RESTDOWN_EXT ?= .restdown 104 | RMTREE ?= rm -rf 105 | JSL_FLAGS ?= --nologo --nosummary 106 | 107 | ifeq ($(shell uname -s),SunOS) 108 | TAR ?= gtar 109 | else 110 | TAR ?= tar 111 | endif 112 | 113 | 114 | # 115 | # Defaults for other fixed values. 116 | # 117 | BUILD = build 118 | DISTCLEAN_FILES += $(BUILD) 119 | DOC_BUILD = $(BUILD)/docs/public 120 | 121 | # 122 | # Configure JSL_FLAGS_{NODE,WEB} based on JSL_CONF_{NODE,WEB}. 123 | # 124 | ifneq ($(origin JSL_CONF_NODE), undefined) 125 | JSL_FLAGS_NODE += --conf=$(JSL_CONF_NODE) 126 | endif 127 | 128 | ifneq ($(origin JSL_CONF_WEB), undefined) 129 | JSL_FLAGS_WEB += --conf=$(JSL_CONF_WEB) 130 | endif 131 | 132 | # 133 | # Targets. For descriptions on what these are supposed to do, see the 134 | # Joyent Engineering Guide. 135 | # 136 | 137 | # 138 | # Instruct make to keep around temporary files. We have rules below that 139 | # automatically update git submodules as needed, but they employ a deps/*/.git 140 | # temporary file. Without this directive, make tries to remove these .git 141 | # directories after the build has completed. 142 | # 143 | .SECONDARY: $($(wildcard deps/*):%=%/.git) 144 | 145 | # 146 | # This rule enables other rules that use files from a git submodule to have 147 | # those files depend on deps/module/.git and have "make" automatically check 148 | # out the submodule as needed. 149 | # 150 | deps/%/.git: 151 | git submodule update --init deps/$* 152 | 153 | # 154 | # These recipes make heavy use of dynamically-created phony targets. The parent 155 | # Makefile defines a list of input files like BASH_FILES. We then say that each 156 | # of these files depends on a fake target called filename.bashchk, and then we 157 | # define a pattern rule for those targets that runs bash in check-syntax-only 158 | # mode. This mechanism has the nice properties that if you specify zero files, 159 | # the rule becomes a noop (unlike a single rule to check all bash files, which 160 | # would invoke bash with zero files), and you can check individual files from 161 | # the command line with "make filename.bashchk". 162 | # 163 | .PHONY: check-bash 164 | check-bash: $(BASH_FILES:%=%.bashchk) $(BASH_FILES:%=%.bashstyle) 165 | 166 | %.bashchk: % 167 | $(BASH) -n $^ 168 | 169 | %.bashstyle: % 170 | $(BASHSTYLE) $^ 171 | 172 | .PHONY: check-json 173 | check-json: $(JSON_FILES:%=%.jsonchk) 174 | 175 | %.jsonchk: % 176 | $(JSON) --validate -f $^ 177 | 178 | # 179 | # The above approach can be slow when there are many files to check because it 180 | # requires that "make" invoke the check tool once for each file, rather than 181 | # passing in several files at once. For the JavaScript check targets, we define 182 | # a variable for the target itself *only if* the list of input files is 183 | # non-empty. This avoids invoking the tool if there are no files to check. 184 | # 185 | JSL_NODE_TARGET = $(if $(JSL_FILES_NODE), check-jsl-node) 186 | .PHONY: check-jsl-node 187 | check-jsl-node: $(JSL_EXEC) 188 | $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_NODE) $(JSL_FILES_NODE) 189 | 190 | JSL_WEB_TARGET = $(if $(JSL_FILES_WEB), check-jsl-web) 191 | .PHONY: check-jsl-web 192 | check-jsl-web: $(JSL_EXEC) 193 | $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_WEB) $(JSL_FILES_WEB) 194 | 195 | .PHONY: check-jsl 196 | check-jsl: $(JSL_NODE_TARGET) $(JSL_WEB_TARGET) 197 | 198 | JSSTYLE_TARGET = $(if $(JSSTYLE_FILES), check-jsstyle) 199 | .PHONY: check-jsstyle 200 | check-jsstyle: $(JSSTYLE_EXEC) 201 | $(JSSTYLE) $(JSSTYLE_FLAGS) $(JSSTYLE_FILES) 202 | 203 | .PHONY: check 204 | check: check-jsl check-json $(JSSTYLE_TARGET) check-bash 205 | @echo check ok 206 | 207 | .PHONY: clean 208 | clean:: 209 | -$(RMTREE) $(CLEAN_FILES) 210 | 211 | .PHONY: distclean 212 | distclean:: clean 213 | -$(RMTREE) $(DISTCLEAN_FILES) 214 | 215 | CSCOPE_FILES = cscope.in.out cscope.out cscope.po.out 216 | CLEAN_FILES += $(CSCOPE_FILES) 217 | 218 | .PHONY: xref 219 | xref: cscope.files 220 | $(CSCOPE) -bqR 221 | 222 | .PHONY: cscope.files 223 | cscope.files: 224 | find $(CSCOPE_DIRS) -name '*.c' -o -name '*.h' -o -name '*.cc' \ 225 | -o -name '*.js' -o -name '*.s' -o -name '*.cpp' > $@ 226 | 227 | # 228 | # The "docs" target is complicated because we do several things here: 229 | # 230 | # (1) Use restdown to build HTML and JSON files from each of DOC_FILES. 231 | # 232 | # (2) Copy these files into $(DOC_BUILD) (build/docs/public), which 233 | # functions as a complete copy of the documentation that could be 234 | # mirrored or served over HTTP. 235 | # 236 | # (3) Then copy any directories and media from docs/media into 237 | # $(DOC_BUILD)/media. This allows projects to include their own media, 238 | # including files that will override same-named files provided by 239 | # restdown. 240 | # 241 | # Step (3) is the surprisingly complex part: in order to do this, we need to 242 | # identify the subdirectories in docs/media, recreate them in 243 | # $(DOC_BUILD)/media, then do the same with the files. 244 | # 245 | DOC_MEDIA_DIRS := $(shell find docs/media -type d 2>/dev/null | grep -v "^docs/media$$") 246 | DOC_MEDIA_DIRS := $(DOC_MEDIA_DIRS:docs/media/%=%) 247 | DOC_MEDIA_DIRS_BUILD := $(DOC_MEDIA_DIRS:%=$(DOC_BUILD)/media/%) 248 | 249 | DOC_MEDIA_FILES := $(shell find docs/media -type f 2>/dev/null) 250 | DOC_MEDIA_FILES := $(DOC_MEDIA_FILES:docs/media/%=%) 251 | DOC_MEDIA_FILES_BUILD := $(DOC_MEDIA_FILES:%=$(DOC_BUILD)/media/%) 252 | 253 | # 254 | # Like the other targets, "docs" just depends on the final files we want to 255 | # create in $(DOC_BUILD), leveraging other targets and recipes to define how 256 | # to get there. 257 | # 258 | .PHONY: docs 259 | docs: \ 260 | $(DOC_FILES:%$(RESTDOWN_EXT)=$(DOC_BUILD)/%.html) \ 261 | $(DOC_FILES:%$(RESTDOWN_EXT)=$(DOC_BUILD)/%.json) \ 262 | $(DOC_MEDIA_FILES_BUILD) 263 | 264 | # 265 | # We keep the intermediate files so that the next build can see whether the 266 | # files in DOC_BUILD are up to date. 267 | # 268 | .PRECIOUS: \ 269 | $(DOC_FILES:%$(RESTDOWN_EXT)=docs/%.html) \ 270 | $(DOC_FILES:%$(RESTDOWN_EXT)=docs/%json) 271 | 272 | # 273 | # We do clean those intermediate files, as well as all of DOC_BUILD. 274 | # 275 | CLEAN_FILES += \ 276 | $(DOC_BUILD) \ 277 | $(DOC_FILES:%$(RESTDOWN_EXT)=docs/%.html) \ 278 | $(DOC_FILES:%$(RESTDOWN_EXT)=docs/%.json) 279 | 280 | # 281 | # Before installing the files, we must make sure the directories exist. The | 282 | # syntax tells make that the dependency need only exist, not be up to date. 283 | # Otherwise, it might try to rebuild spuriously because the directory itself 284 | # appears out of date. 285 | # 286 | $(DOC_MEDIA_FILES_BUILD): | $(DOC_MEDIA_DIRS_BUILD) 287 | 288 | $(DOC_BUILD)/%: docs/% | $(DOC_BUILD) 289 | $(CP) $< $@ 290 | 291 | docs/%.json docs/%.html: docs/%$(RESTDOWN_EXT) | $(DOC_BUILD) $(RESTDOWN_EXEC) 292 | $(RESTDOWN) $(RESTDOWN_FLAGS) -m $(DOC_BUILD) $< 293 | 294 | $(DOC_BUILD): 295 | $(MKDIR) $@ 296 | 297 | $(DOC_MEDIA_DIRS_BUILD): 298 | $(MKDIR) $@ 299 | 300 | # 301 | # The default "test" target does nothing. This should usually be overridden by 302 | # the parent Makefile. It's included here so we can define "prepush" without 303 | # requiring the repo to define "test". 304 | # 305 | .PHONY: test 306 | test: 307 | 308 | .PHONY: prepush 309 | prepush: check test 310 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Joyent, Inc. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | var fs = require('fs'); 8 | var os = require('os'); 9 | var path = require('path'); 10 | var util = require('util'); 11 | 12 | var userid = require('userid'); 13 | var assert = require('assert-plus'); 14 | var bunyan = require('bunyan'); 15 | var clone = require('clone'); 16 | var dashdash = require('dashdash'); 17 | var LRU = require('lru-cache'); 18 | var nfs = require('nfs'); 19 | var rpc = require('oncrpc'); 20 | var mantafs = require('mantafs'); 21 | var vasync = require('vasync'); 22 | 23 | var app = require('./lib'); 24 | 25 | // uid/gid for 'nobody' on non-windows systems 26 | var uid = 0; 27 | var gid = 0; 28 | var os_platform; 29 | 30 | ///--- Globals 31 | 32 | var LOG = app.bunyan.createLogger(); 33 | 34 | var OPTIONS_PARSER = dashdash.createParser({ 35 | options: [ 36 | { 37 | names: ['file', 'f'], 38 | type: 'string', 39 | help: 'configuration file to use', 40 | helpArg: 'FILE' 41 | }, 42 | { 43 | names: ['debug', 'd'], 44 | type: 'bool', 45 | help: 'turn on debug bunyan logging' 46 | }, 47 | { 48 | names: ['verbose', 'v'], 49 | type: 'bool', 50 | help: 'turn on verbose bunyan logging' 51 | } 52 | ] 53 | }); 54 | 55 | 56 | 57 | ///--- Functions 58 | 59 | function usage(msg) { 60 | var help = OPTIONS_PARSER.help({ 61 | includeEnv: true 62 | }).trimRight(); 63 | 64 | if (msg) 65 | console.error(util.format.apply(util, arguments)); 66 | console.error('usage: nfsd [OPTIONS]\noptions:\n' + help); 67 | 68 | process.exit(msg ? 1 : 0); 69 | } 70 | 71 | 72 | function configure() { 73 | var opts; 74 | 75 | try { 76 | opts = OPTIONS_PARSER.parse(process.argv); 77 | } catch (e) { 78 | usage(e.message); 79 | } 80 | 81 | if (opts.verbose) { 82 | LOG = LOG.child({ 83 | level: 'trace', 84 | src: true 85 | }); 86 | } else if (opts.debug) { 87 | LOG = LOG.child({ 88 | level: 'debug', 89 | src: true 90 | }); 91 | } else { 92 | LOG = LOG.child({ 93 | level: 'info', 94 | src: true 95 | }); 96 | } 97 | 98 | if (opts.help) 99 | usage(); 100 | 101 | var cfg; 102 | if (opts.file) { 103 | try { 104 | cfg = JSON.parse(fs.readFileSync(opts.file, 'utf8')); 105 | } catch (e) { 106 | usage('unable to load %s:\n%s\n', opts.file, e.toString()); 107 | } 108 | } else { 109 | cfg = {}; 110 | } 111 | 112 | // If no config, let createMantaClient handle setup using env 113 | if (!cfg.manta) 114 | cfg.manta = {}; 115 | 116 | if (cfg.database) { 117 | assert.object(cfg.database, 'config.database'); 118 | } else { 119 | cfg.database = {}; 120 | } 121 | 122 | // default local cache config values if any are not provided 123 | if (!cfg.database.location) 124 | cfg.database.location = '/var/tmp/mfsdb'; 125 | 126 | if (!cfg.database.sizeMB) 127 | cfg.database.sizeMB = 5120; 128 | 129 | if (!cfg.database.ttl) 130 | cfg.database.ttl = 43200; 131 | 132 | if (!cfg.database.wbtime) 133 | cfg.database.wbtime = 60; 134 | 135 | if (!cfg.database.num_par) 136 | cfg.database.num_par = 2; 137 | 138 | if (cfg.portmap) { 139 | assert.object(cfg.portmap, 'config.portmap'); 140 | // Normally only define this section if setting 141 | // 'usehost': 1 142 | } else { 143 | // our built-in portmapper just hardcodes the standard info 144 | cfg.portmap = { 145 | 'port': 111, 146 | 'mappings': { 147 | 'mountd': [ { 148 | 'prog': 100005, 149 | 'vers': 3, 150 | 'prot': 6, 151 | 'port': 1892 152 | }, { 153 | 'prog': 100005, 154 | 'vers': 1, 155 | 'prot': 6, 156 | 'port': 1892 157 | }], 158 | 'nfsd': [ { 159 | 'prog': 100003, 160 | 'vers': 3, 161 | 'prot': 6, 162 | 'port': 2049 163 | }], 164 | 'portmapd': [ { 165 | 'prog': 100000, 166 | 'vers': 2, 167 | 'prot': 6, 168 | 'port': 111 169 | }] 170 | } 171 | }; 172 | } 173 | 174 | // Can set 'address' to enable the mountd server to listen on an IP address 175 | // other than the loopback. 176 | // Can define hosts_allow and hosts_deny to list the addresses of hosts 177 | // which can/cannot mount. e.g. 178 | // 'hosts_allow': { 179 | // '192.168.0.10': {}, 180 | // '192.168.0.11': {} 181 | // }, 182 | // 'hosts_deny': { 183 | // '192.168.0.12': {}, 184 | // '192.168.0.13': {} 185 | // } 186 | // Can set exports if you want to limit what parts of the manta namespace 187 | // can be mounted: 188 | // 'exports': { 189 | // '/user/stor/project': {}, 190 | // '/user/public': {} 191 | // } 192 | cfg.mount = cfg.mount || {}; 193 | assert.object(cfg.mount, 'config.mount'); 194 | 195 | // Can set uid and gid to specify the uid/gid for 'nobody' on the client. 196 | // If not provided, the server's values for 'nobody' will be used. 197 | if (!cfg.nfs) { 198 | var t_uid; 199 | var t_gid; 200 | 201 | try { 202 | t_uid = convert_neg_id(userid.uid('nobody')); 203 | } catch (e1) { 204 | t_uid = 65534; 205 | } 206 | 207 | try { 208 | t_gid = convert_neg_id(userid.gid('nobody')); 209 | } catch (e1) { 210 | // Linux uses 'nogroup' instead of 'nobody' 211 | try { 212 | t_gid = convert_neg_id(userid.gid('nogroup')); 213 | } catch (e2) { 214 | t_gid = t_uid; 215 | } 216 | } 217 | 218 | cfg.nfs = { 219 | 'uid': t_uid, 220 | 'gid': t_gid 221 | }; 222 | } 223 | 224 | assert.object(cfg.nfs, 'config.nfs'); 225 | cfg.nfs.fd_cache = cfg.nfs.fd_cache || { 226 | max: 10000, 227 | ttl: 60 228 | }; 229 | cfg.nfs.hosts_allow = cfg.mount.hosts_allow; 230 | cfg.nfs.hosts_deny = cfg.mount.hosts_deny; 231 | 232 | cfg.log = LOG; 233 | cfg.manta.log = LOG; 234 | cfg.mount.log = LOG; 235 | cfg.nfs.log = LOG; 236 | cfg.portmap.log = LOG; 237 | 238 | cfg.manta = app.createMantaClient(cfg.manta); 239 | cfg.mount.manta = cfg.manta; 240 | cfg.nfs.manta = cfg.manta; 241 | 242 | return (cfg); 243 | } 244 | 245 | 246 | function step_down() { 247 | try { 248 | process.setgid(gid); 249 | process.setuid(uid); 250 | LOG.info('server now running as \'nobody\''); 251 | } catch (e) { 252 | LOG.fatal(e, 'unable to setuid/setgid to nobody'); 253 | process.exit(1); 254 | } 255 | } 256 | 257 | // Runs the mountd and nfsd servers. Called once we're registered with the 258 | // system's portmapper or once we've started our own portmapper. 259 | function run_servers(log, cfg_mount, cfg_nfs) { 260 | var barrier = vasync.barrier(); 261 | var mountd = app.createMountServer(cfg_mount); 262 | var nfsd = app.createNfsServer(cfg_nfs); 263 | 264 | barrier.on('drain', function onRunning() { 265 | var ma = mountd.address(); 266 | var na = nfsd.address(); 267 | 268 | log.info('mountd: listening on: tcp://%s:%d', 269 | ma.address, ma.port); 270 | log.info('nfsd: listening on: tcp://%s:%d', 271 | na.address, na.port); 272 | 273 | if (uid !== 0) { 274 | // On non-windows machines we run as 'nobody'. 275 | // On sunos we have to wait until after we're listening on the nfs 276 | // port since the user 'nobody' will not have the sys_nfs priv. 277 | // On darwin 'nobody' is -2 and we error setting the uid/gid to a 278 | // negative number, so use the symbolic name. 279 | if (os_platform === 'darwin') { 280 | gid = uid = 'nobody'; 281 | } 282 | step_down(); 283 | } 284 | }); 285 | 286 | mountd.on('error', function (e) { 287 | if (e.code == 'EADDRINUSE') { 288 | log.fatal('mountd already running, exiting.'); 289 | } else { 290 | log.fatal(e, 'unable to run the mountd'); 291 | } 292 | process.exit(1); 293 | }); 294 | 295 | nfsd.on('error', function (e) { 296 | if (e.code == 'EADDRINUSE') { 297 | log.fatal('nfsd already running, exiting.'); 298 | } else { 299 | log.fatal(e, 'unable to run the nfsd'); 300 | } 301 | process.exit(1); 302 | }); 303 | 304 | barrier.start('mount'); 305 | mountd.listen(cfg_mount.port || 1892, 306 | cfg_mount.address || '127.0.0.1', 307 | barrier.done.bind(barrier, 'mount')); 308 | 309 | // nfsd needs to listen on the same IP as configured for the mountd 310 | barrier.start('nfs'); 311 | nfsd.listen(cfg_nfs.port || 2049, 312 | cfg_mount.address || '127.0.0.1', 313 | barrier.done.bind(barrier, 'nfs')); 314 | } 315 | 316 | // Darwin uses negative numbers for 'nobody' but these get pulled out as a 317 | // large non-negative number. Convert to twos-complement. 318 | function convert_neg_id(id) 319 | { 320 | if (id > 0x7fffffff) 321 | return (-(~id + 1)); 322 | else 323 | return (id); 324 | } 325 | 326 | ///--- Mainline 327 | 328 | (function main() { 329 | var cfg = configure(); 330 | var log = cfg.log; 331 | 332 | os_platform = os.platform(); 333 | if (os_platform !== 'win32' && os_platform !== 'darwin') { 334 | uid = convert_neg_id(userid.uid('nobody')); 335 | try { 336 | gid = convert_neg_id(userid.gid('nobody')); 337 | } catch (e1) { 338 | // Linux uses 'nogroup' instead of 'nobody' 339 | try { 340 | gid = convert_neg_id(userid.gid('nogroup')); 341 | } catch (e2) { 342 | gid = uid; 343 | } 344 | } 345 | } 346 | 347 | var mfs = mantafs.createClient({ 348 | log: log.child({component: 'MantaFs'}, true), 349 | manta: cfg.manta, 350 | path: cfg.database.location, 351 | sizeMB: cfg.database.sizeMB, 352 | ttl: cfg.database.ttl, 353 | wbtime: cfg.database.wbtime, 354 | num_par: cfg.database.num_par, 355 | uid: cfg.nfs.uid || uid, 356 | gid: cfg.nfs.gid || gid 357 | }); 358 | 359 | // must always use the system's portmapper on sunos 360 | if (os_platform === 'sunos') 361 | cfg.portmap.usehost = true; 362 | 363 | cfg.mount.fs = mfs; 364 | cfg.nfs.fs = mfs; 365 | cfg.nfs.fd_cache = LRU({ 366 | dispose: function cache_close_fd(k, v) { 367 | mfs.close(v.fd, function on_close(err) { 368 | if (err) 369 | log.debug(err, 'failed to close(fd=%d) for %s', v.fd, k); 370 | }); 371 | }, 372 | max: cfg.nfs.fd_cache.max, 373 | maxAge: cfg.nfs.fd_cache.ttl * 1000 // 1m TTL 374 | }); 375 | 376 | cfg.nfs.cachepath = cfg.database.location; // used by fsstat 377 | 378 | log.info('configuration: %s', util.inspect(cfg)); 379 | 380 | var mntmapping = { 381 | prog: 100005, 382 | vers: 3, 383 | prot: 6, 384 | port: 1892 385 | }; 386 | 387 | var nfsmapping = { 388 | prog: 100003, 389 | vers: 3, 390 | prot: 6, 391 | port: 2049 392 | }; 393 | 394 | function cleanup() { 395 | mfs.shutdown(function (err) { 396 | if (err) { 397 | log.warn(err, 'mantafs shutdown error'); 398 | } 399 | 400 | if (cfg.portmap.usehost) { 401 | var pmapclient = app.createPortmapClient(cfg.portmap); 402 | 403 | pmapclient.once('connect', function () { 404 | pmapclient.unset(mntmapping, function (err1) { 405 | if (err1) { 406 | log.warn(err1, 407 | 'unregistering mountd from the portmapper'); 408 | } 409 | 410 | pmapclient.unset(nfsmapping, function (err2) { 411 | if (err2) { 412 | log.warn(err2, 413 | 'unregistering nfsd from the portmapper'); 414 | } 415 | log.info('Shutdown complete, exiting.'); 416 | process.exit(0); 417 | }); 418 | }); 419 | }); 420 | } else { 421 | log.info('Shutdown complete, exiting.'); 422 | process.exit(0); 423 | } 424 | }); 425 | } 426 | 427 | process.on('SIGTERM', function () { 428 | log.info('Got SIGTERM, shutting down.'); 429 | cleanup(); 430 | }); 431 | 432 | process.on('SIGINT', function () { 433 | log.info('Got SIGINT, shutting down.'); 434 | cleanup(); 435 | }); 436 | 437 | mfs.once('error', function (err) { 438 | log.fatal(err, 'unable to initialize mantafs cache'); 439 | process.exit(1); 440 | }); 441 | mfs.once('ready', function () { 442 | // Cache exists now, ensure cache dir modes are more secure 443 | fs.chmodSync(mfs.cache.location, 0700); 444 | fs.chmodSync(path.join(mfs.cache.location, 'fscache'), 0700); 445 | fs.chmodSync(path.join(mfs.cache.location, 'mantafs.db'), 0600); 446 | if (uid !== 0) { 447 | // On non-windows machines we run as 'nobody'. Tighten up now. 448 | fs.chownSync(mfs.cache.location, uid, gid); 449 | fs.chownSync(path.join(mfs.cache.location, 'fscache'), uid, gid); 450 | fs.chownSync(path.join(mfs.cache.location, 'mantafs.db'), uid, gid); 451 | } 452 | 453 | // the portmapper needs to listen on all addresses, unlike our mountd 454 | // and nfsd which only listen on localhost by default for some basic 455 | // security 456 | cfg.portmap.address = cfg.portmap.address || '0.0.0.0'; 457 | cfg.portmap.port = cfg.portmap.port || 111; 458 | 459 | // Use the system's portmapper 460 | function register_with_pmap() { 461 | // The Linux portmapper normally rejects requests that are not 462 | // made to the loopback address. 463 | cfg.portmap.url = util.format('udp://127.0.0.1:%d', 464 | cfg.portmap.port); 465 | var pmapclient = app.createPortmapClient(cfg.portmap); 466 | 467 | pmapclient.on('error', function (e) { 468 | log.fatal(e, 'unable to connect to the system`s portmapper'); 469 | process.exit(1); 470 | }); 471 | 472 | pmapclient.once('connect', function () { 473 | pmapclient.set(mntmapping, function (err1) { 474 | if (err1) { 475 | log.fatal(err1, 476 | 'unable to register mountd with the portmapper'); 477 | process.exit(1); 478 | } 479 | 480 | pmapclient.set(nfsmapping, function (err2) { 481 | if (err2) { 482 | log.fatal(err2, 483 | 'unable to register nfsd with the portmapper'); 484 | process.exit(1); 485 | } 486 | 487 | pmapclient.close(); 488 | run_servers(cfg.log, cfg.mount, cfg.nfs); 489 | }); 490 | }); 491 | }); 492 | } 493 | 494 | if (cfg.portmap.usehost) { 495 | register_with_pmap(); 496 | } else { 497 | // Here we run our own portmapper 498 | var pmapd = app.createPortmapServer(cfg.portmap); 499 | 500 | pmapd.on('error', function (e) { 501 | if (e.code == 'EADDRINUSE') { 502 | log.info('Portmapper running, registering there...'); 503 | cfg.portmap.usehost = 1; 504 | register_with_pmap(); 505 | } else { 506 | log.fatal(e, 'unable to run the portmapper'); 507 | process.exit(1); 508 | } 509 | }); 510 | 511 | pmapd.listen(cfg.portmap.port, cfg.portmap.address, function () { 512 | run_servers(cfg.log, cfg.mount, cfg.nfs); 513 | }); 514 | } 515 | }); 516 | })(); 517 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # manta-nfs 2 | 3 | `manta-nfs` implements a [NFS vers. 3](http://tools.ietf.org/html/rfc1813) 4 | server that uses 5 | [Joyent Manta](http://www.joyent.com/products/manta) as the backing store. 6 | The server implements all NFS functionality, although some OS-level commands, 7 | such as `chmod`, will have no effect since Manta does not support that concept. 8 | The server is implemented in [node.js](http://nodejs.org/) and **requires** 9 | v0.10.x. 10 | 11 | - [Overview](#overview) 12 | - [Getting Started](#getting-started) 13 | - [Configuration](#configuration) 14 | - [Usage](#usage) 15 | - [Limitations](#limitations) 16 | - [OS Specific Considerations](#os-specific-considerations) 17 | - [Darwin (OS X)](#darwin-os-x) 18 | - [Linux](#linux) 19 | - [SmartOS](#smartos) 20 | - [Windows](#windows) 21 | 22 | 23 | ## Overview 24 | 25 | The server is a process that runs locally on your laptop, in your 26 | zone, or on a standalone system, and services NFS requests. The server then 27 | acts as a gateway for those requests back into Manta. Manta objects are 28 | cached locally in the machine's file system so that all NFS operations can 29 | be supported. Unlike Manta, the server provides the typical POSIX write 30 | semantics via its use of the local object cache. 31 | 32 | The server cannot run on a system that is already acting as a NFS server 33 | since there would be a conflict on the required ports. 34 | In this case, the server will detect the existing server and exit. 35 | 36 | The server includes a built-in portmapper, but it will also interoperate 37 | transparently with the system's portmapper (usually `rpcbind`) if one is running. 38 | The server also includes a built-in `mountd` and `nfsd`. There is no `lockd` provided 39 | by the server. 40 | 41 | By default, the server only listens on the localhost address and only 42 | serves files locally. However, it can be configured to serve files to 43 | external hosts. 44 | 45 | Because the server caches Manta objects locally, care must be taken when 46 | accessing Manta in different ways or from different locations. There is no 47 | attempt to be coherent across multiple systems. Given this, you should 48 | not run more than one instance of the server for the same Manta user. Likewise, 49 | you should not write to the same object using both NFS and the CLI. Reading 50 | objects using both NFS and the CLI is obviously fine. 51 | 52 | If you write an object 53 | using one mechanism (e.g. NFS) and immediately read it using another (e.g. the 54 | CLI), you may not see the same data. The server holds an object locally 55 | for a period of time before writing it back to Manta. Likewise, if you update 56 | an existing object using the CLI, the server might have a stale copy in its 57 | cache. In this case you can wait for the server to notice the object has 58 | changed, or you can force the server to refresh its cached copy by 59 | simply `touch`ing the file. 60 | 61 | ## Getting Started 62 | 63 | Clone the repo then run `npm install` within the clone to build all of the 64 | dependencies. The 'Configuration' section of this readme describes how to 65 | configure the server before you can run it. The 'Usage' section of this 66 | readme describes how to start the server and how to perform an NFS mount. 67 | 68 | ## Configuration 69 | 70 | At a minimum the server needs the configuration information necessary to 71 | connect to a Manta account. If the Manta 72 | [environment variables](http://apidocs.joyent.com/manta/#setting-up-your-environment) 73 | are already set up, the server will use those and no other configuration is 74 | needed. 75 | 76 | In addition to the Manta account information, there are a variety of other 77 | configuration options. An example configuration file, showing all possible 78 | configuration options, is provided in `etc/example.json`, but you should not 79 | use that file to create your personal configuration. A simpler 80 | `etc/template.json` file is provided as a better starting point for your 81 | personal configuration. Each section of the configuration file is optional. 82 | The configuration file is specified to the server via the `-f` option: 83 | 84 | node server.js -f etc/myconfig.json 85 | 86 | Although most of the sections in `etc/example.json` should be 87 | self-explanatory, here is some additional information. 88 | 89 | * The `manta` section must be used to specify the required access information 90 | for Manta if the environment variables are not set. The configuration 91 | information takes precedence over the environment variables if both are 92 | set. 93 | 94 | * The `database` section can be used to configure where and how the server 95 | caches local copies of the Manta objects. The location, size of the 96 | cache, the time-to-live, writeback delay time, and number of parallel 97 | writebacks for the cache can be set in this section. 98 | 99 | The default cache is under `/var/tmp/mfsd` with a size limit of 5GB of 100 | local disk space. The time-to-live is the number of seconds a file will be 101 | cached before checking to see if it is stale. The default is twelve hours 102 | (43200 seconds). The writeback time is the number of seconds a dirty file 103 | will be cached before being written back to Manta. The default is one 104 | minute (60 seconds). If files are updated regularly (e.g. log files) then 105 | it might make sense to increase the timeout to reduce writeback traffic, 106 | but this also increases the window in which data only exists in the local 107 | cache. The number of parallel writebacks defaults to 2. That is, if there 108 | are multiple dirty files to writeback, two at a time will be written back. 109 | 110 | The cache size is not a hard limit. It is possible for more space to 111 | be used than is configured. For example, if an object is larger than the 112 | cache size, then if that object is pulled into the cache, the whole object 113 | must be downloaded and the space used by the object will be consumed. This 114 | would also force all of the other objects out of the cache, since the space 115 | used exceeds the size limit. Another example is with dirty files. These 116 | cannot be evicted from the cache until they have been uploaded back to 117 | Manta, so the cache space used can exceed the size limit until the objects 118 | have been completely uploaded. 119 | 120 | * The `mount` section's `address` field can be used to specify an address 121 | other than localhost for the server to listen on. Using '0.0.0.0' tells the 122 | server to listen on all addresses. Both the `mountd` and `nfsd` within the 123 | server will listen on the given address. Since the server has full access 124 | to all of the user's Manta data, it is a good idea to limit foreign host 125 | access when listening on the external network. The `hosts_allow` or 126 | `hosts_deny` sections can be used to restrict access to the given IP 127 | addresses. The `exports` section can also be used to restrict access to 128 | the specified portions of the Manta filesystem. 129 | 130 | * The `nfs` section can be used to set the `uid` and `gid` values for 131 | 'nobody'. This is useful if NFS clients are running a different OS, which 132 | uses different values for 'nobody', as compared to the server (e.g. Darwin 133 | vs. Linux). Over NFS all files will appear to be owned by 'nobody' since 134 | there is no mechanism to map a Manta username to a local uid on the various 135 | clients, but within Manta all files continue to be owned by the user 136 | account. The `fd-cache` section can be used to configure the server's file 137 | descriptor cache, although this is normally not necessary. 138 | 139 | ## Usage 140 | 141 | When running the server for the first time, you probably want to run it by 142 | hand to confirm that the configuration is correct and things are working as 143 | expected. Once you know things are working correctly, you may want to set up 144 | a service so that the server runs automatically. 145 | 146 | The server must be started as root since it needs access to the portmapper's 147 | privileged port. Once the server is running, it lowers its uid to 'nobody' 148 | to improve security. The `sudo` or `pfexec` commands are typically used to run 149 | a command as root, depending on which OS you're using. 150 | 151 | If you're using the Manta environment variables as the source of your Manta 152 | account information and you're using `sudo`, use the `-E` option to pass 153 | those forward. On some Linux distributions sudo will reset 'HOME' to root's 154 | home directory. On those distributions you must also set HOME back to your home 155 | directory. 156 | 157 | On Darwin or Linux, the server can be run with no config file like: 158 | 159 | sudo -E HOME=/home/foo node server.js 160 | 161 | On SmartOS, the server can be run like: 162 | 163 | pfexec node server.js 164 | 165 | To pass in a config file, use the -f option: 166 | 167 | sudo node server.js -f etc/myconfig.json 168 | 169 | All output logging is done via [bunyan](https://github.com/trentm/node-bunyan). 170 | Once started, the server will output an 171 | occasional log message, but the `-d` or `-v` options can be used to change the 172 | bunyan logging level to either 'debug' or 'trace'. Logging at either of these 173 | levels is not recommended, except during debugging, since there will be many 174 | log entries for each NFS operation. You may want to redirect the output from 175 | the server into a file: 176 | 177 | sudo node server.js -d -f etc/myconfig.json >log 2>&1 178 | 179 | To mount a Manta directory, use the standard NFS client `mount` command with 180 | a Manta path. The user name used here must be the same user as is configured 181 | for Manta access. For example, if Manta user 'foo' is configured, then to 182 | mount their 'public' directory: 183 | 184 | sudo mount 127.0.0.1:/foo/public /mnt 185 | 186 | Once you have confirmed that the server works as expected, you can set up a 187 | service on your system so that the server runs automatically when the system 188 | boots. Setting up a service like this is OS-specific and is discussed in that 189 | section for each operating system. 190 | 191 | ## Limitations 192 | 193 | There are certain NFS operations that cannot be supported because Manta 194 | itself does not support the underlying concept. These are: 195 | 196 | * Changing the owner uid or gid of a file 197 | * Changing the mtime or atime of a file 198 | * Changing or setting the mode of a file 199 | * Creating a file exclusively (O_EXCL - will happen only in the cache) 200 | * Making devices, sockets or FIFOs 201 | * Renaming or moving directories 202 | * Symlinks and hardlinks 203 | 204 | ## OS Specific Considerations 205 | 206 | This section discusses any issues that are specific to running the server on 207 | a given operating system. 208 | 209 | 210 | ### Darwin (OS X) 211 | 212 | There is normally no portmapper running on Darwin so the server runs with its 213 | built-in portmapper. 214 | 215 | The uid/gid for 'nobody' is -2. 216 | 217 | Because you cannot rename a directory, creating new folders using `Finder` is 218 | problematic. The new folder will initially be created by Finder with the name 219 | `untitled folder`, but you will not be able to rename it. Instead, you must use 220 | a terminal window and the command line to create directories with the correct 221 | name. 222 | 223 | The `svc/launchd/com.joyent.mantanfs.plist` file provides an example 224 | configuration for launchd(8). If necessary, edit the file and provide the 225 | correct paths to 'node', 'server.js' and your configuration file. 226 | 227 | Note that this configuration will bring the service up only if an interface 228 | other than `lo` has an IPV4/IPV6 address. However the reverse is not true, and 229 | launchd will not bring down the service if the network goes away. 230 | 231 | Run the following to load and start the service: 232 | 233 | sudo cp svc/launchd/com.joyent.mantanfs.plist /System/Library/LaunchDaemons/ 234 | sudo launchctl load /System/Library/LaunchDaemons/com.joyent.mantanfs.plist 235 | 236 | 237 | ### Linux (including lx-branded zones) 238 | 239 | Some distributions (e.g. Ubuntu or Centos) may not come pre-installed with 240 | the `/sbin/mount.nfs` command which is needed to perform a mount, while others 241 | (e.g. Fedora) may be ready to go. On Ubuntu, install the `nfs-common` package. 242 | 243 | apt-get install nfs-common 244 | 245 | On Centos, install the `nfs-utils` package. Depending on the choices described 246 | below, you may also need to install the `rpcbind` package. 247 | 248 | yum install nfs-utils 249 | yum install rpcbind 250 | 251 | Based on the distribution's package manager and package dependencies, the 252 | 'rpcbind' command may have been installed and started. However, due to a 253 | mis-design in the Linux rpcbind code, the manta-nfs server will not be able to 254 | register with the system's rpcbind. There are two options to work around this: 255 | 256 | * Disable the system's rpcbind and let the server use its built-in 257 | portmapper. The method for disabling the system's rpcbind varies depending 258 | on the service manager that the system uses. If 'rpcbind' is in a seperate 259 | package from '/sbin/mount.nfs', then you could simply uninstall that 260 | package. To disable 'rpcbind' on Ubuntu you can run: `stop portmap`. 261 | 262 | * Run the system's rpcbind in 'insecure' mode using the -i option. The 263 | mechanism for specifying customized options for a service varies by 264 | distribution and release. The rpcbind configuration file may be named 265 | `/etc/init/portmap.conf` or `/etc/sysconfig/rpcbind`. For a distribution 266 | which uses traditional sysvinit rc files you may need to edit 267 | `/etc/init.d/rpcbind` and explicitly add the option to the invocation of 268 | rpcbind in the script. 269 | 270 | Here is an example entry for the `/etc/sysconfig/rpcbind` file. 271 | RPCBIND_ARGS="-i" 272 | 273 | If running the manta-nfs server inside an lx-branded zone, the built-in 274 | portmapper may not work properly. In this case, the second option above (using 275 | the system's rpcbind) must be used. 276 | 277 | On Linux the uid/gid for 'nobody' is 65534. 278 | 279 | There is no lock manager included in the server, so you must disable locking 280 | when you mount. e.g. 281 | 282 | mount -o nolock 127.0.0.1:/foo.bar/public /home/foo/mnt 283 | 284 | When mounting from inside an lx-branded zone you may need to explicitly 285 | specify that you want to use the NFSv3 protocol. e.g. 286 | 287 | mount -o nolock,vers=3 127.0.0.1:/foo.bar/public /home/foo/mnt 288 | 289 | To setup the server as a service, so that it runs automatically when the 290 | system boots, you need to hook into the system's service manager. Linux offers 291 | a variety of dfferent service managers, depending upon the distribution and 292 | release. 293 | 294 | * rc files 295 | 296 | The traditional Unix rc file mechanism is not really a service manager but 297 | it does provide a way to start or stop services when the system is booting 298 | or shutting down. 299 | 300 | The `svc/rc/mantanfs` file is a shell script that will start up the server. 301 | Make a copy of this file into `/etc/init.d`. If necessary, edit the file and 302 | provide the correct paths to 'node', 'server.js' and your configuration 303 | file. 304 | 305 | Symlink the following names to the 'mantanfs' file: 306 | 307 | ln -s /etc/rc3.d/S90mantanfs -> ../init.d/mantanfs 308 | ln -s /etc/rc4.d/S90mantanfs -> ../init.d/mantanfs 309 | ln -s /etc/rc5.d/S90mantanfs -> ../init.d/mantanfs 310 | ln -s /etc/rc0.d/K90mantanfs -> ../init.d/mantanfs 311 | ln -s /etc/rc1.d/K90mantanfs -> ../init.d/mantanfs 312 | ln -s /etc/rc2.d/K90mantanfs -> ../init.d/mantanfs 313 | ln -s /etc/rc6.d/K90mantanfs -> ../init.d/mantanfs 314 | 315 | The script directs the server log to '/var/log/mantanfs.log'. 316 | 317 | * Systemd 318 | 319 | See this [wiki](https://fedoraproject.org/wiki/Systemd) for more details 320 | on configuring and using systemd. Also see the `systemd.unit(5)` and 321 | `systemd.service(5)` man pages. 322 | 323 | The `svc/systemd/mantanfs.service` file provides an example configuration 324 | for systemd. Make a copy of this file into /lib/systemd/system. If 325 | necessary, edit the file and provide the correct paths to 'node', 326 | 'server.js' and your configuration file. 327 | 328 | Run the following to start the service: 329 | 330 | systemctl start mantanfs.service 331 | 332 | Since systemd has its own logging, you must use the 'journalctl' command to 333 | look at the logs. 334 | 335 | journalctl _SYSTEMD_UNIT=mantanfs.service 336 | 337 | * Upstart 338 | 339 | See this [cookbook](http://upstart.ubuntu.com/cookbook/) for more details 340 | on configuring and using upstart. 341 | 342 | The `svc/upstart/mantanfs.conf` file provides an example configuration for 343 | upstart. Make a copy of this file into /etc/init. If necessary, edit the 344 | file and provide the correct paths to 'node', 'server.js' and your 345 | configuration file. 346 | 347 | Run the following to start the service: 348 | 349 | initctl start mantanfs 350 | 351 | The server log should be available as '/var/log/upstart/mantanfs.log'. 352 | 353 | ### SmartOS 354 | 355 | In order to mount from the host, the system's 'rpcbind' must be running. The 356 | server's built-in portmapper cannot be used. If the svc is not already enabled, 357 | enable it. 358 | 359 | svcadm enable network/rpc/bind 360 | 361 | If you intend to serve external hosts, you must also ensure that the bind 362 | service is configured to allow access. To check this: 363 | 364 | svccfg -s bind listprop config/local_only 365 | 366 | If this is set to true, you need to change it to false. 367 | 368 | svccfg -s bind setprop config/local_only=false 369 | svcadm refresh bind 370 | 371 | Due to a mis-design in the SmartOS mount code, mounting will fail on older 372 | platforms. If you see the following, you know your mount code is incorrect. 373 | 374 | nfs mount: 127.0.0.1: : RPC: Program not registered 375 | nfs mount: retrying: /home/foo.bar/mnt 376 | 377 | You will either need to run on a newer platform or you can use this 378 | [fixed NFS mount command](http://us-east.manta.joyent.com/jjelinek/public/mount) 379 | explicitly. e.g. 380 | 381 | pfexec ./mount 127.0.0.1:/foo.bar/public /home/foo/mnt 382 | 383 | For unmounting, you can use this 384 | [fixed umount command](http://us-east.manta.joyent.com/jjelinek/public/umount) 385 | explicitly. 386 | 387 | On SmartOS the uid/gid for 'nobody' is 60001. 388 | 389 | The `svc/smf/manta-nfs.xml` file provides an example configuration for 390 | smf(5). If necessary, edit the file and provide the correct paths to 'node', 391 | 'server.js' and your configuration file. 392 | 393 | Run the following to load and start the service: 394 | 395 | svccfg -v import svc/smf/manta-nfs.xml 396 | 397 | ### Windows 398 | 399 | Because of the POSIX dependencies in the server, the code does not currently 400 | build on Windows. However, the Windows NFS client can be used with a server 401 | running on a Unix-based host. Before you can use NFS you may need to set it up 402 | on your Windows system. The procedure varies by which version of Windows 403 | is in use. See the documentation for your release for the correct procedure 404 | to install NFS. 405 | 406 | Once NFS is installed, you simply mount from the server as usual. Substitute 407 | the server's IP address and the correct user name in the following example: 408 | 409 | C:\>mount \\192.168.0.1\foo\public * 410 | Z: is now successfully connected to \\192.168.0.1\foo\public 411 | 412 | The command completed successfully. 413 | 414 | Windows will assign an unused drive letter for the mount. In this example the 415 | drive letter was Z:. 416 | 417 | Windows Explorer has the same limitation as Darwin's Finder when creating a 418 | new folder. The new folder will initially be created by Explorer with the name 419 | `New folder`, but you will not be able to rename it. Instead, you must use 420 | a terminal window and the command line to create directories with the correct 421 | name. 422 | -------------------------------------------------------------------------------- /tools/service_bundle.dtd.1: -------------------------------------------------------------------------------- 1 | 2 | 24 | 25 | 32 | 33 | 39 | 40 | 45 | 46 | 47 | 48 | 49 | 52 | 58 | 59 | 62 | 65 | 66 | 77 | 78 | 79 | 80 | 83 | 84 | 85 | 86 | 96 | 97 | 98 | 99 | 101 | 102 | 124 | 125 | 127 | 128 | 129 | 130 | 132 | 133 | 134 | 135 | 137 | 138 | 139 | 140 | 142 | 143 | 144 | 145 | 147 | 148 | 149 | 150 | 152 | 153 | 154 | 155 | 157 | 158 | 159 | 160 | 162 | 163 | 164 | 165 | 167 | 168 | 169 | 170 | 172 | 173 | 174 | 175 | 177 | 178 | 179 | 180 | 182 | 183 | 184 | 185 | 187 | 188 | 189 | 190 | 192 | 193 | 194 | 195 | 196 | 197 | 213 | 214 | 220 | 221 | 227 | ]]> 228 | 229 | 235 | 236 | 242 | ]]> 243 | 244 | 262 | 263 | 265 | 266 | 273 | ]]> 274 | 275 | 277 | 278 | 285 | ]]> 286 | 287 | 308 | 309 | 312 | 313 | 317 | ]]> 318 | 319 | 322 | 323 | 327 | ]]> 328 | 329 | 339 | 340 | 341 | 342 | 344 | 345 | 346 | 347 | 382 | 383 | 385 | 386 | 393 | 394 | 395 | 396 | 421 | 422 | 424 | 425 | 432 | 433 | 434 | 435 | 443 | 444 | 445 | 446 | 449 | 450 | 456 | 457 | 458 | 459 | 460 | 461 | 471 | 472 | 473 | 474 | 476 | 477 | 500 | 501 | 502 | 503 | 509 | 510 | 535 | 537 | 538 | 542 | 543 | 544 | 545 | 574 | 575 | 577 | 578 | 584 | 585 | 594 | 595 | 597 | 598 | 599 | 600 | 603 | 604 | 618 | 619 | 620 | 621 | 624 | 625 | 640 | 641 | 642 | 643 | 647 | 648 | 656 | 657 | 659 | 660 | 661 | 662 | 672 | 674 | 675 | 677 | 678 | 686 | 688 | 689 | 690 | 691 | 699 | 701 | 702 | 703 | 704 | 709 | 710 | 712 | 713 | 714 | 715 | 728 | 729 | 730 | 731 | 733 | 734 | 745 | 746 | 748 | 749 | 751 | 752 | 757 | 758 | 760 | 761 | 762 | 763 | 777 | 778 | 779 | 780 | 783 | 784 | 792 | 793 | 795 | 796 | 797 | 798 | 808 | 809 | 810 | 811 | 814 | 815 | 820 | 821 | 823 | 824 | 825 | 837 | 838 | 839 | 840 | 842 | 843 | 851 | 852 | 854 | 855 | 856 | 857 | 873 | 874 | 877 | 878 | 884 | 885 | 913 | 914 | 916 | 917 | 922 | 923 | 937 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 949 | 950 | 952 | 953 | 955 | 956 | 957 | 958 | 960 | 961 | 963 | 964 | 967 | 968 | 974 | 975 | 977 | 978 | 979 | 980 | 981 | 982 | 994 | 995 | 996 | 997 | 999 | 1000 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1029 | 1030 | 1034 | 1035 | 1038 | 1039 | 1059 | 1060 | 1065 | 1066 | 1070 | 1071 | 1085 | 1086 | 1088 | 1089 | 1092 | --------------------------------------------------------------------------------