├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── examples ├── combo.js ├── mountd.js ├── murmur3.js └── nfsd.js ├── lib ├── index.js ├── mount │ ├── client.js │ ├── dump_reply.js │ ├── errors.js │ ├── index.js │ ├── mnt_call.js │ ├── mnt_reply.js │ ├── mount_reply.js │ ├── server.js │ ├── umnt_call.js │ └── umnt_reply.js └── nfs │ ├── access_call.js │ ├── access_reply.js │ ├── client.js │ ├── commit_call.js │ ├── commit_reply.js │ ├── create_call.js │ ├── create_reply.js │ ├── errors.js │ ├── fattr3.js │ ├── fs_info_call.js │ ├── fs_info_reply.js │ ├── fs_stat_call.js │ ├── fs_stat_reply.js │ ├── get_attr_call.js │ ├── get_attr_reply.js │ ├── index.js │ ├── link_call.js │ ├── link_reply.js │ ├── lookup_call.js │ ├── lookup_reply.js │ ├── mkdir_call.js │ ├── mkdir_reply.js │ ├── mknod_call.js │ ├── mknod_reply.js │ ├── nfs_call.js │ ├── nfs_reply.js │ ├── path_conf_call.js │ ├── path_conf_reply.js │ ├── read_call.js │ ├── read_reply.js │ ├── readdir_call.js │ ├── readdir_reply.js │ ├── readdirplus_call.js │ ├── readdirplus_reply.js │ ├── readlink_call.js │ ├── readlink_reply.js │ ├── remove_call.js │ ├── remove_reply.js │ ├── rename_call.js │ ├── rename_reply.js │ ├── rmdir_call.js │ ├── rmdir_reply.js │ ├── sattr3.js │ ├── server.js │ ├── set_attr_call.js │ ├── set_attr_reply.js │ ├── symlink_call.js │ ├── symlink_reply.js │ ├── wcc_data.js │ ├── write_call.js │ └── write_reply.js ├── package.json ├── smf └── manifests │ └── portmap.xml.in ├── test ├── helper.js ├── mount.test.js └── nfs.test.js └── tools ├── bashstyle ├── jsl.node.conf ├── jsl.web.conf ├── jsstyle.conf ├── mk ├── Makefile.defs ├── Makefile.deps ├── Makefile.node_deps.defs ├── Makefile.node_deps.targ ├── Makefile.smf.defs ├── Makefile.smf.targ └── Makefile.targ ├── mkrepo ├── runtests.in └── service_bundle.dtd.1 /.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 = https://github.com/joyent/jsstyle.git 4 | [submodule "deps/restdown"] 5 | path = deps/restdown 6 | url = https://github.com/trentm/restdown.git 7 | [submodule "deps/javascriptlint"] 8 | path = deps/javascriptlint 9 | url = https://github.com/davepacheco/javascriptlint.git 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2013, Joyent, Inc. All rights reserved. 3 | # 4 | 5 | # 6 | # Tools 7 | # 8 | NODEUNIT := ./node_modules/.bin/nodeunit 9 | NPM := npm 10 | 11 | # 12 | # Files 13 | # 14 | DOC_FILES = index.restdown 15 | JS_FILES := $(shell find lib test -name '*.js') 16 | JSON_FILES = package.json 17 | JSL_CONF_NODE = tools/jsl.node.conf 18 | JSL_FILES_NODE = $(JS_FILES) 19 | JSSTYLE_FILES = $(JS_FILES) 20 | JSSTYLE_FLAGS = -f tools/jsstyle.conf 21 | SMF_MANIFESTS_IN = smf/manifests/portmap.xml.in 22 | 23 | include ./tools/mk/Makefile.defs 24 | include ./tools/mk/Makefile.smf.defs 25 | 26 | # 27 | # Repo-specific targets 28 | # 29 | .PHONY: all 30 | all: $(SMF_MANIFESTS) | $(NODEUNIT) $(REPO_DEPS) 31 | $(NPM) rebuild 32 | 33 | $(NODEUNIT): | $(NPM_EXEC) 34 | $(NPM) install 35 | 36 | CLEAN_FILES += $(NODEUNIT) ./node_modules/nodeunit node_modules 37 | 38 | .PHONY: test 39 | test: $(NODEUNIT) 40 | $(NPM) test 41 | 42 | include ./tools/mk/Makefile.deps 43 | include ./tools/mk/Makefile.smf.targ 44 | include ./tools/mk/Makefile.targ 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tl;dr 2 | 3 | nfs bindings for node.js 4 | -------------------------------------------------------------------------------- /examples/mountd.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 | 10 | var assert = require('assert-plus'); 11 | var bunyan = require('bunyan'); 12 | var libuuid = require('node-uuid'); 13 | var nfs = require('../lib'); 14 | var rpc = require('oncrpc'); 15 | 16 | 17 | 18 | ///--- Globals 19 | 20 | var MOUNTS = {}; 21 | 22 | 23 | 24 | ////--- Private Functions 25 | 26 | function authorize(req, res, next) { 27 | if (!req.user_allowed([0])) { 28 | next(new nfs.MountAccessError('uid 0 required')); 29 | } else { 30 | next(); 31 | } 32 | } 33 | 34 | 35 | function check_dirpath(req, res, next) { 36 | assert.string(req.dirpath, 'req.dirpath'); 37 | 38 | var p = path.normalize(req.dirpath); 39 | if (p.length > 64) { 40 | next(new nfs.MountNameTooLongError(p + ' > 64 bytes')); 41 | } 42 | 43 | fs.stat(p, function (err, stats) { 44 | if (err) { 45 | if (err.code === 'ENOENT') { 46 | next(new nfs.MountNoEntError(err)); 47 | } else if (err.code === 'EACCES') { 48 | req.log.warn({ 49 | err: err, 50 | path: p 51 | }, 'unable to read directory'); 52 | next(new nfs.MountAccessError(err)); 53 | } else { 54 | next(new nfs.MountIOError(err, 'internal error')); 55 | } 56 | } else if (!stats.isDirectory()) { 57 | next(new nfs.MountNotDirError()); 58 | } else { 59 | res.setFileHandle(libuuid.v4()); 60 | res.writeHead(); 61 | res.end(); 62 | next(); 63 | } 64 | }); 65 | } 66 | 67 | 68 | 69 | ///--- Mainline 70 | 71 | (function main() { 72 | var log = bunyan.createLogger({ 73 | name: 'mountd', 74 | level: 'info', 75 | src: true, 76 | stream: process.stdout, 77 | serializers: rpc.serializers 78 | }); 79 | 80 | var server = nfs.createMountServer({ 81 | log: log 82 | }); 83 | 84 | server.mnt(authorize, check_dirpath); 85 | 86 | server.on('after', function (name, req, res, err) { 87 | log.info({ 88 | rpc_call: req, 89 | rpc_reply: res, 90 | err: err 91 | }, '%s: handled', name); 92 | }); 93 | 94 | server.on('uncaughtException', function (req, res, err) { 95 | console.error('ERROR: %s', err.stack); 96 | process.exit(1); 97 | }); 98 | 99 | server.start(function () { 100 | console.log('ready'); 101 | }); 102 | })(); 103 | -------------------------------------------------------------------------------- /examples/murmur3.js: -------------------------------------------------------------------------------- 1 | // This source file copied from 2 | // https://github.com/garycourt/murmurhash-js/blob/master/murmurhash3_gc.js 3 | // 4 | // Copyright (c) 2011 Gary Court 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining 7 | // a copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | /** 26 | * JS Implementation of MurmurHash3 (r136) (as of May 20, 2011) 27 | * 28 | * @author Gary Court 29 | * @see http://github.com/garycourt/murmurhash-js 30 | * @author Austin Appleby 31 | * @see http://sites.google.com/site/murmurhash/ 32 | * 33 | * @param {string} key ASCII only 34 | * @param {number} seed Positive integer only 35 | * @return {number} 32-bit positive integer hash 36 | */ 37 | /*jsl:ignore*/ 38 | /* BEGIN JSSTYLED */ 39 | function murmurhash3_32_gc(key, seed) { 40 | var remainder, bytes, h1, h1b, c1, c1b, c2, c2b, k1, i; 41 | 42 | remainder = key.length & 3; // key.length % 4 43 | bytes = key.length - remainder; 44 | h1 = seed; 45 | c1 = 0xcc9e2d51; 46 | c2 = 0x1b873593; 47 | i = 0; 48 | 49 | while (i < bytes) { 50 | k1 = 51 | ((key.charCodeAt(i) & 0xff)) | 52 | ((key.charCodeAt(++i) & 0xff) << 8) | 53 | ((key.charCodeAt(++i) & 0xff) << 16) | 54 | ((key.charCodeAt(++i) & 0xff) << 24); 55 | ++i; 56 | 57 | k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff; 58 | k1 = (k1 << 15) | (k1 >>> 17); 59 | k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff; 60 | 61 | h1 ^= k1; 62 | h1 = (h1 << 13) | (h1 >>> 19); 63 | h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff; 64 | h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16)); 65 | } 66 | 67 | k1 = 0; 68 | 69 | switch (remainder) { 70 | case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16; 71 | case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8; 72 | case 1: k1 ^= (key.charCodeAt(i) & 0xff); 73 | 74 | k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff; 75 | k1 = (k1 << 15) | (k1 >>> 17); 76 | k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff; 77 | h1 ^= k1; 78 | } 79 | 80 | h1 ^= key.length; 81 | 82 | h1 ^= h1 >>> 16; 83 | h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff; 84 | h1 ^= h1 >>> 13; 85 | h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff; 86 | h1 ^= h1 >>> 16; 87 | 88 | return h1 >>> 0; 89 | } 90 | module.exports = murmurhash3_32_gc; 91 | /* END JSSTYLED */ 92 | /*jsl:end*/ 93 | -------------------------------------------------------------------------------- /examples/nfsd.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 bunyan = require('bunyan'); 8 | var nfs = require('../lib'); 9 | var rpc = require('oncrpc'); 10 | 11 | 12 | 13 | (function main() { 14 | var bunyan = require('bunyan'); 15 | 16 | var log = bunyan.createLogger({ 17 | name: 'nfsd', 18 | level: 'info', 19 | src: true, 20 | stream: process.stdout, 21 | serializers: rpc.serializers 22 | }); 23 | 24 | var server = nfs.createNfsServer({ 25 | log: log 26 | }); 27 | 28 | // server.dump(function dump(req, res, next) { 29 | // res.addMapping({ 30 | // name: '/var/tmp', 31 | // dirname: '/var/tmp' 32 | // }); 33 | // // res.addMapping({ 34 | // // name: 'mount', 35 | // // prog: 100005, 36 | // // vers: 3, 37 | // // prot: 6, 38 | // // port: 1892 39 | // // }); 40 | // res.writeHead(); 41 | // res.end(); 42 | // next(); 43 | // }); 44 | 45 | server.on('after', function (name, req, res, err) { 46 | log.info({ 47 | rpc_call: req, 48 | rpc_reply: res, 49 | err: err 50 | }, '%s: handled', name); 51 | }); 52 | 53 | server.start(function () { 54 | log.info('nfsd listening at %j', server.address()); 55 | }); 56 | })(); 57 | -------------------------------------------------------------------------------- /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 | 8 | 9 | ///--- Helpers 10 | 11 | function _export(obj) { 12 | Object.keys(obj).forEach(function (k) { 13 | module.exports[k] = obj[k]; 14 | }); 15 | } 16 | 17 | 18 | 19 | ///--- Exports 20 | 21 | module.exports = {}; 22 | 23 | _export(require('./mount')); 24 | _export(require('./nfs')); 25 | -------------------------------------------------------------------------------- /lib/mount/client.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 util = require('util'); 8 | 9 | var assert = require('assert-plus'); 10 | var clone = require('clone'); 11 | var once = require('once'); 12 | var rpc = require('oncrpc'); 13 | 14 | var MountDumpReply = require('./dump_reply').MountDumpReply; 15 | var MountMntCall = require('./mnt_call').MountMntCall; 16 | var MountMntReply = require('./mnt_reply').MountMntReply; 17 | var MountUmntCall = require('./umnt_call').MountUmntCall; 18 | var MountUmntReply = require('./umnt_reply').MountUmntReply; 19 | 20 | 21 | 22 | ///--- Helpers 23 | 24 | function createCallback(cb) { 25 | cb = once(cb); 26 | 27 | function _callback(err, reply) { 28 | if (err) { 29 | cb(err, reply); 30 | return; 31 | } 32 | 33 | reply.once('error', cb); 34 | reply.once('end', cb.bind(null, null, reply)); 35 | reply.resume(); 36 | } 37 | 38 | return (_callback); 39 | } 40 | 41 | 42 | 43 | ///--- API 44 | 45 | function MountClient(opts) { 46 | assert.object(opts, 'options'); 47 | if (opts.log) { 48 | var l = opts.log; 49 | delete opts.log; 50 | } 51 | 52 | var _opts = clone(opts); 53 | _opts.log = opts.log = l; 54 | _opts.name = 'mount'; 55 | _opts.program = 100005; 56 | _opts.version = 3; 57 | 58 | rpc.RpcClient.call(this, _opts); 59 | } 60 | util.inherits(MountClient, rpc.RpcClient); 61 | 62 | 63 | MountClient.prototype.dump = function dump(cb) { 64 | assert.func(cb, 'callback'); 65 | 66 | var call = new rpc.RpcCall({ 67 | incoming: false, 68 | proc: 2 69 | }); 70 | 71 | this._rpc(call, MountDumpReply, createCallback(cb)); 72 | 73 | call.end(); 74 | 75 | return (this); 76 | }; 77 | 78 | 79 | MountClient.prototype.mnt = function mnt(dirpath, cb) { 80 | assert.string(dirpath, 'dirpath'); 81 | assert.func(cb, 'callback'); 82 | 83 | var call = new MountMntCall({ 84 | dirpath: dirpath, 85 | incoming: false, 86 | proc: 1 87 | }); 88 | 89 | this._rpc(call, MountMntReply, createCallback(cb)); 90 | 91 | call.end(); 92 | 93 | return (this); 94 | }; 95 | 96 | 97 | MountClient.prototype.umnt = function umnt(dirpath, cb) { 98 | assert.string(dirpath, 'dirpath'); 99 | assert.func(cb, 'callback'); 100 | 101 | cb = once(cb); 102 | 103 | var call = new MountUmntCall({ 104 | dirpath: dirpath, 105 | incoming: false, 106 | proc: 3 107 | }); 108 | 109 | this._rpc(call, MountUmntReply, createCallback(cb)); 110 | 111 | call.end(); 112 | 113 | 114 | return (this); 115 | }; 116 | 117 | 118 | 119 | ///--- Exports 120 | 121 | module.exports = { 122 | MountClient: MountClient, 123 | createMountClient: function createMountClient(opts) { 124 | return (new MountClient(opts)); 125 | } 126 | }; 127 | -------------------------------------------------------------------------------- /lib/mount/dump_reply.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 path = require('path'); 8 | var util = require('util'); 9 | 10 | var assert = require('assert-plus'); 11 | var clone = require('clone'); 12 | var rpc = require('oncrpc'); 13 | 14 | 15 | 16 | ///--- Globals 17 | var RpcReply = rpc.RpcReply; 18 | var XDR = rpc.XDR; 19 | 20 | 21 | 22 | 23 | ///--- Helpers 24 | 25 | function calculate_buffer_length(mappings) { 26 | assert.arrayOfObject(mappings, 'mappings'); 27 | var sz = 0; 28 | mappings.forEach(function (m) { 29 | sz += 4; // boolean marker 30 | sz += XDR.byteLength(m.name); 31 | sz += XDR.byteLength(m.dirpath); 32 | }); 33 | sz += 4; // final boolean marker 34 | 35 | return (sz); 36 | } 37 | 38 | 39 | 40 | ///--- API 41 | 42 | function MountDumpReply(opts) { 43 | RpcReply.call(this, opts); 44 | 45 | this.mappings = []; 46 | 47 | this._nfs_mount_dump_reply = true; // MDB 48 | } 49 | util.inherits(MountDumpReply, RpcReply); 50 | 51 | 52 | MountDumpReply.prototype.addMapping = function addMapping(opts, noClone) { 53 | assert.object(opts); 54 | assert.string(opts.name, 'options.name'); 55 | assert.string(opts.dirpath, 'options.dirpath'); 56 | assert.optionalBool(noClone, 'noClone'); 57 | 58 | this.mappings.push(noClone ? opts : clone(opts)); 59 | }; 60 | 61 | 62 | MountDumpReply.prototype._transform = function _transform(chunk, enc, cb) { 63 | if (this.incoming) { 64 | var xdr = new XDR(chunk); 65 | 66 | while (xdr.readBool()) { 67 | this.addMapping({ 68 | name: xdr.readString(), 69 | dirpath: xdr.readString() 70 | }, true); 71 | } 72 | } else { 73 | this.push(chunk); 74 | } 75 | 76 | cb(); 77 | }; 78 | 79 | 80 | MountDumpReply.prototype.writeHead = function writeHead() { 81 | var len = calculate_buffer_length(this.mappings); 82 | var xdr = this._serialize(len); 83 | 84 | this.mappings.forEach(function (p) { 85 | xdr.writeBool(true); 86 | xdr.writeString(p.name); 87 | xdr.writeString(p.dirpath); 88 | }); 89 | xdr.writeBool(false); 90 | 91 | this.write(xdr.buffer()); 92 | }; 93 | 94 | 95 | MountDumpReply.prototype.toString = function toString() { 96 | var fmt = '[object MountDumpReply ]'; 97 | return (util.format(fmt, this.xid, this.mappings)); 98 | }; 99 | 100 | 101 | 102 | ///--- Exports 103 | 104 | module.exports = { 105 | MountDumpReply: MountDumpReply 106 | }; 107 | -------------------------------------------------------------------------------- /lib/mount/errors.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 util = require('util'); 8 | 9 | var assert = require('assert-plus'); 10 | var rpc = require('oncrpc'); 11 | 12 | 13 | 14 | ///--- Globals 15 | 16 | // var _MOUNTSTAT3 = { 17 | // MNT3_OK: 0, // no error 18 | // MNT3ERR_Perm: 1, // Not owner 19 | // MNT3ERR_NoEnt: 2, // No such file or directory 20 | // MNT3ERR_IO: 5, // I/O error 21 | // MNT3ERR_Access: 13, // Permission denied 22 | // MNT3ERR_NotDir: 20, // Not a directory 23 | // MNT3ERR_Inval: 22, // Invalid argument 24 | // MNT3ERR_NameTooLong: 63, // Filename too long 25 | // MNT3ERR_NotSupp: 10004, // Operation not supported 26 | // MNT3ERR_ServerFault: 10006 // A failure on the server 27 | // }; 28 | 29 | 30 | var MOUNTSTAT3 = { 31 | MNT3_OK: 0, // no error 32 | MNT3ERR_PERM: 1, // Not owner 33 | MNT3ERR_NOENT: 2, // No such file or directory 34 | MNT3ERR_IO: 5, // I/O error 35 | MNT3ERR_ACCES: 13, // Permission denied 36 | MNT3ERR_NOTDIR: 20, // Not a directory 37 | MNT3ERR_INVAL: 22, // Invalid argument 38 | MNT3ERR_NAMETOOLONG: 63, // Filename too long 39 | MNT3ERR_NOTSUPP: 10004, // Operation not supported 40 | MNT3ERR_SERVERFAULT: 10006 // A failure on the server 41 | }; 42 | 43 | 44 | 45 | ///--- Base API 46 | 47 | function MountError(cause, msg) { 48 | rpc.RpcError.apply(this, arguments); 49 | } 50 | MountError.prototype.name = 'MountError'; 51 | util.inherits(MountError, rpc.RpcError); 52 | 53 | 54 | MountError.prototype.toBuffer = function toBuffer() { 55 | var xdr = this._serialize(4); 56 | xdr.writeInt(this.mountstat3); 57 | 58 | return (xdr.buffer()); 59 | }; 60 | 61 | 62 | 63 | ///--- Exports 64 | 65 | module.exports = { 66 | MountError: MountError 67 | }; 68 | 69 | // Object.keys(MOUNTSTAT3).forEach(function (k) { 70 | // if (k === 'MNT3_OK') 71 | // return; 72 | 73 | // var name = 'Mount' + k.split('_')[1] + 'Error'; 74 | // var err = function () { 75 | // MountError.apply(this, arguments); 76 | // this.mountstat3 = MOUNTSTAT3[k]; 77 | // }; 78 | // util.inherits(err, MountError); 79 | // err.prototype.name = name; 80 | 81 | // module.exports[name] = err; 82 | // }); 83 | 84 | Object.keys(MOUNTSTAT3).forEach(function (k) { 85 | module.exports[k] = MOUNTSTAT3[k]; 86 | }); 87 | -------------------------------------------------------------------------------- /lib/mount/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 | 8 | 9 | ///--- Helpers 10 | 11 | function _export(obj) { 12 | Object.keys(obj).forEach(function (k) { 13 | module.exports[k] = obj[k]; 14 | }); 15 | } 16 | 17 | 18 | 19 | ///--- Exports 20 | 21 | module.exports = {}; 22 | 23 | _export(require('./client')); 24 | _export(require('./dump_reply')); 25 | _export(require('./errors')); 26 | _export(require('./mnt_call')); 27 | _export(require('./mnt_reply')); 28 | _export(require('./server')); 29 | _export(require('./umnt_call')); 30 | _export(require('./umnt_reply')); 31 | -------------------------------------------------------------------------------- /lib/mount/mnt_call.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 util = require('util'); 8 | 9 | var assert = require('assert-plus'); 10 | var rpc = require('oncrpc'); 11 | 12 | 13 | 14 | ///--- Globals 15 | 16 | var RpcCall = rpc.RpcCall; 17 | var XDR = rpc.XDR; 18 | 19 | 20 | 21 | ///--- API 22 | 23 | function MountMntCall(opts) { 24 | RpcCall.call(this, opts, true); 25 | 26 | this.dirpath = opts.dirpath || ''; 27 | 28 | this._nfs_mount_mnt_call = true; // MDB 29 | } 30 | util.inherits(MountMntCall, RpcCall); 31 | 32 | 33 | MountMntCall.prototype._transform = function _transform(chunk, enc, cb) { 34 | if (this.incoming) { 35 | var xdr = new XDR(chunk); 36 | this.dirpath = xdr.readString(); 37 | } else { 38 | this.push(chunk); 39 | } 40 | 41 | cb(); 42 | }; 43 | 44 | 45 | MountMntCall.prototype.writeHead = function writeHead() { 46 | var xdr = this._serialize(XDR.byteLength(this.dirpath)); 47 | xdr.writeString(this.dirpath); 48 | 49 | this.write(xdr.buffer()); 50 | }; 51 | 52 | 53 | MountMntCall.prototype.toString = function toString() { 54 | var fmt = '[object MountMntCall ]'; 55 | return (util.format(fmt, this.xid, this.dirpath)); 56 | }; 57 | 58 | 59 | 60 | ///--- Exports 61 | 62 | module.exports = { 63 | MountMntCall: MountMntCall 64 | }; 65 | -------------------------------------------------------------------------------- /lib/mount/mnt_reply.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 path = require('path'); 8 | var util = require('util'); 9 | 10 | var assert = require('assert-plus'); 11 | var clone = require('clone'); 12 | var rpc = require('oncrpc'); 13 | 14 | var mnt_err = require('./errors'); 15 | var MountReply = require('./mount_reply').MountReply; 16 | 17 | 18 | ///--- Globals 19 | 20 | var XDR = rpc.XDR; 21 | 22 | 23 | 24 | 25 | ///--- Helpers 26 | 27 | function calculate_buffer_length(mappings) { 28 | assert.arrayOfObject(mappings, 'mappings'); 29 | var sz = 0; 30 | mappings.forEach(function (m) { 31 | sz += 4; // boolean marker 32 | sz += XDR.byteLength(m.name); 33 | sz += XDR.byteLength(m.dirpath); 34 | }); 35 | sz += 4; // final boolean marker 36 | 37 | return (sz); 38 | } 39 | 40 | 41 | 42 | ///--- API 43 | 44 | function MountMntReply(opts) { 45 | MountReply.call(this, opts); 46 | 47 | this.fhs_status = 0; 48 | this.mountinfo = { 49 | fhandle: '', 50 | auth_flavors: [1] 51 | }; 52 | 53 | this._nfs_mount_mnt_reply = true; // MDB 54 | } 55 | util.inherits(MountMntReply, MountReply); 56 | MountMntReply.prototype.__defineSetter__('status', function (s) { 57 | assert.number(s, 'status'); 58 | this.fhs_status = s; 59 | }); 60 | MountMntReply.prototype.__defineGetter__('status', function () { 61 | return (this.fhs_status); 62 | }); 63 | MountMntReply.prototype._allowed_error_codes = [ 64 | mnt_err.MNT3ERR_NOENT, 65 | mnt_err.MNT3ERR_IO, 66 | mnt_err.MNT3ERR_ACCES, 67 | mnt_err.MNT3ERR_NOTDIR, 68 | mnt_err.MNT3ERR_NAMETOOLONG 69 | ]; 70 | 71 | 72 | MountMntReply.prototype.setFileHandle = function setFileHandle(fh) { 73 | assert.string(fh, 'file_handle'); 74 | 75 | this.mountinfo.fhandle = fh; 76 | 77 | if (this.mountinfo.fhandle.length > 64) 78 | this.mountinfo.fhandle.length = 64; 79 | 80 | return (this.mountinfo.fhandle); 81 | }; 82 | 83 | 84 | MountMntReply.prototype._transform = function _transform(chunk, enc, cb) { 85 | if (this.incoming) { 86 | var xdr = new XDR(chunk); 87 | 88 | this.fhs_status = xdr.readInt(); 89 | if (this.fhs_status === 0) { 90 | this.mountinfo.fhandle = xdr.readString(); 91 | this.mountinfo.auth_flavors = xdr.readIntArray(); 92 | } 93 | } else { 94 | this.push(chunk); 95 | } 96 | 97 | cb(); 98 | }; 99 | 100 | 101 | MountMntReply.prototype.writeHead = function writeHead() { 102 | var len = 4; 103 | if (this.fhs_status === 0) { 104 | len += XDR.byteLength(this.mountinfo.fhandle); 105 | len += XDR.byteLength(this.mountinfo.auth_flavors); 106 | } 107 | var xdr = this._serialize(len); 108 | 109 | xdr.writeInt(this.fhs_status); 110 | if (this.fhs_status === 0) { 111 | xdr.writeString(this.mountinfo.fhandle); 112 | xdr.writeIntArray(this.mountinfo.auth_flavors); 113 | } 114 | 115 | this.write(xdr.buffer()); 116 | }; 117 | 118 | 119 | MountMntReply.prototype.toString = function toString() { 120 | var fmt = '[object MountMntReply ]'; 121 | return (util.format(fmt, this.xid, this.fhs_status, this.mountinfo)); 122 | }; 123 | 124 | 125 | 126 | ///--- Exports 127 | 128 | module.exports = { 129 | MountMntReply: MountMntReply 130 | }; 131 | -------------------------------------------------------------------------------- /lib/mount/mount_reply.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 util = require('util'); 8 | 9 | var assert = require('assert-plus'); 10 | var RpcReply = require('oncrpc').RpcReply; 11 | 12 | var mnt_err = require('./errors'); 13 | 14 | 15 | 16 | ///--- Errors 17 | 18 | var ALLOWED_ERRORS = [ 19 | mnt_err.MNT3ERR_NOENT, 20 | mnt_err.MNT3ERR_IO, 21 | mnt_err.MNT3ERR_ACCES, 22 | mnt_err.MNT3ERR_NOTDIR, 23 | mnt_err.MNT3ERR_NAMETOOLONG, 24 | mnt_err.MNT3ERR_SERVERFAULT 25 | ]; 26 | 27 | ///--- API 28 | 29 | function MountReply(opts) { 30 | assert.object(opts, 'options'); 31 | 32 | RpcReply.call(this, opts); 33 | 34 | this._mount_reply = true; // MDB 35 | } 36 | util.inherits(MountReply, RpcReply); 37 | 38 | 39 | MountReply.prototype.error = function error(status) { 40 | assert.number(status, 'status'); 41 | 42 | if (!ALLOWED_ERRORS.some(function (c) { 43 | return (c === status); 44 | })) { 45 | throw new Error(status + ' is not an allowed status code'); 46 | } 47 | 48 | this.status = status; 49 | this.send(); 50 | }; 51 | 52 | 53 | 54 | ///--- Exports 55 | 56 | module.exports = { 57 | MountReply: MountReply 58 | }; 59 | -------------------------------------------------------------------------------- /lib/mount/server.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 util = require('util'); 8 | 9 | var assert = require('assert-plus'); 10 | var clone = require('clone'); 11 | var rpc = require('oncrpc'); 12 | 13 | var errors = require('./errors'); 14 | var MountDumpReply = require('./dump_reply').MountDumpReply; 15 | var MountMntCall = require('./mnt_call').MountMntCall; 16 | var MountMntReply = require('./mnt_reply').MountMntReply; 17 | var MountUmntCall = require('./umnt_call').MountUmntCall; 18 | var MountUmntReply = require('./umnt_reply').MountUmntReply; 19 | 20 | 21 | 22 | ///--- Globals 23 | 24 | var slice = Function.prototype.call.bind(Array.prototype.slice); 25 | var RpcServer = rpc.RpcServer; 26 | 27 | 28 | 29 | ///--- API 30 | 31 | function MountServer(opts) { 32 | assert.object(opts, 'options'); 33 | if (opts.log) { 34 | var l = opts.log; 35 | delete opts.log; 36 | } 37 | 38 | var _opts = clone(opts); 39 | _opts.log = opts.log = l; 40 | _opts.name = 'mount'; 41 | _opts.program = 100005; 42 | _opts.version = [1, 3]; 43 | 44 | RpcServer.call(this, _opts); 45 | } 46 | util.inherits(MountServer, RpcServer); 47 | 48 | 49 | MountServer.prototype.dump = function dump() { 50 | var cfg = { 51 | name: 'dump', 52 | procedure: 2, 53 | reply: MountDumpReply 54 | }; 55 | this._mount(cfg, slice(arguments)); 56 | 57 | return (this); 58 | }; 59 | 60 | 61 | 62 | MountServer.prototype.mnt = function mnt() { 63 | var cfg = { 64 | name: 'mnt', 65 | procedure: 1, 66 | call: MountMntCall, 67 | reply: MountMntReply 68 | }; 69 | this._mount(cfg, slice(arguments)); 70 | 71 | return (this); 72 | }; 73 | 74 | 75 | MountServer.prototype.umnt = function umnt() { 76 | var cfg = { 77 | name: 'umnt', 78 | procedure: 3, 79 | call: MountUmntCall, 80 | reply: MountUmntReply 81 | }; 82 | this._mount(cfg, slice(arguments)); 83 | 84 | return (this); 85 | }; 86 | 87 | 88 | MountServer.prototype.start = function start(host, cb) { 89 | var args = slice(arguments); 90 | args.unshift(1892); 91 | this.listen.apply(this, args); 92 | }; 93 | 94 | 95 | 96 | ///--- Exports 97 | 98 | module.exports = { 99 | MountServer: MountServer, 100 | createMountServer: function createMountServer(opts) { 101 | return (new MountServer(opts)); 102 | } 103 | }; 104 | -------------------------------------------------------------------------------- /lib/mount/umnt_call.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 util = require('util'); 8 | 9 | var assert = require('assert-plus'); 10 | var rpc = require('oncrpc'); 11 | 12 | 13 | 14 | ///--- Globals 15 | 16 | var RpcCall = rpc.RpcCall; 17 | var XDR = rpc.XDR; 18 | 19 | 20 | 21 | ///--- API 22 | 23 | function MountUmntCall(opts) { 24 | RpcCall.call(this, opts, true); 25 | 26 | this.dirpath = opts.dirpath || ''; 27 | 28 | this._nfs_mount_umnt_call = true; // MDB 29 | } 30 | util.inherits(MountUmntCall, RpcCall); 31 | 32 | 33 | MountUmntCall.prototype._transform = function _transform(chunk, enc, cb) { 34 | if (this.incoming) { 35 | var xdr = new XDR(chunk); 36 | this.dirpath = xdr.readString(); 37 | } else { 38 | this.push(chunk); 39 | } 40 | 41 | cb(); 42 | }; 43 | 44 | 45 | MountUmntCall.prototype.writeHead = function writeHead() { 46 | var xdr = this._serialize(XDR.byteLength(this.dirpath)); 47 | xdr.writeString(this.dirpath); 48 | 49 | this.write(xdr.buffer()); 50 | }; 51 | 52 | 53 | MountUmntCall.prototype.toString = function toString() { 54 | var fmt = '[object MountUmntCall ]'; 55 | return (util.format(fmt, this.xid, this.dirpath)); 56 | }; 57 | 58 | 59 | 60 | ///--- Exports 61 | 62 | module.exports = { 63 | MountUmntCall: MountUmntCall 64 | }; 65 | -------------------------------------------------------------------------------- /lib/mount/umnt_reply.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 path = require('path'); 8 | var util = require('util'); 9 | 10 | var assert = require('assert-plus'); 11 | var clone = require('clone'); 12 | var rpc = require('oncrpc'); 13 | 14 | var mnt_err = require('./errors'); 15 | var MountReply = require('./mount_reply').MountReply; 16 | 17 | 18 | ///--- API 19 | 20 | function MountUmntReply(opts) { 21 | MountReply.call(this, opts); 22 | 23 | this._nfs_mount_umnt_reply = true; // MDB 24 | } 25 | util.inherits(MountUmntReply, MountReply); 26 | MountUmntReply.prototype.__defineSetter__('status', function (s) {}); 27 | MountUmntReply.prototype.__defineGetter__('status', function () { 28 | return (0); 29 | }); 30 | MountUmntReply.prototype._allowed_error_codes = []; 31 | 32 | 33 | MountUmntReply.prototype.toString = function toString() { 34 | var fmt = '[object MountUmntReply ]'; 35 | return (util.format(fmt, this.xid)); 36 | }; 37 | 38 | 39 | 40 | ///--- Exports 41 | 42 | module.exports = { 43 | MountUmntReply: MountUmntReply 44 | }; 45 | -------------------------------------------------------------------------------- /lib/nfs/errors.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 util = require('util'); 8 | 9 | var assert = require('assert-plus'); 10 | var rpc = require('oncrpc'); 11 | 12 | 13 | 14 | ///--- Globals 15 | 16 | var NFSSTAT3 = { 17 | NFS3_OK: 0, 18 | NFS3ERR_PERM: 1, 19 | NFS3ERR_NOENT: 2, 20 | NFS3ERR_IO: 5, 21 | NFS3ERR_NXIO: 6, 22 | NFS3ERR_ACCES: 13, 23 | NFS3ERR_EXIST: 17, 24 | NFS3ERR_XDEV: 18, 25 | NFS3ERR_NODEV: 19, 26 | NFS3ERR_NOTDIR: 20, 27 | NFS3ERR_ISDIR: 21, 28 | NFS3ERR_INVAL: 22, 29 | NFS3ERR_FBIG: 27, 30 | NFS3ERR_NOSPACE: 28, 31 | NFS3ERR_ROFS: 30, 32 | NFS3ERR_MLINK: 31, 33 | NFS3ERR_NAMETOOLONG: 63, 34 | NFS3ERR_NOTEMPTY: 66, 35 | NFS3ERR_DQUOT: 69, 36 | NFS3ERR_STALE: 70, 37 | NFS3ERR_REMOTE: 71, 38 | NFS3ERR_BADHANDLE: 10001, 39 | NFS3ERR_NOT_SYNC: 10002, 40 | NFS3ERR_BAD_COOKIE: 10003, 41 | NFS3ERR_NOTSUPP: 10004, 42 | NFS3ERR_TOOSMALL: 10005, 43 | NFS3ERR_SERVERFAULT: 10006, 44 | NFS3ERR_BADTYPE: 10007, 45 | NFS3ERR_JUKEBOX: 10008 46 | }; 47 | 48 | 49 | 50 | ///--- Base API 51 | 52 | function NfsError(cause, msg) { 53 | rpc.RpcError.apply(this, arguments); 54 | } 55 | NfsError.prototype.name = 'NfsError'; 56 | util.inherits(NfsError, rpc.RpcError); 57 | 58 | 59 | NfsError.prototype.toBuffer = function toBuffer() { 60 | var xdr = this._serialize(4); 61 | xdr.writeInt(this.nfsstat3); 62 | 63 | return (xdr.buffer()); 64 | }; 65 | 66 | 67 | 68 | ///--- Exports 69 | 70 | module.exports = { 71 | NfsError: NfsError 72 | }; 73 | 74 | // Object.keys(_NFSSTAT3).forEach(function (k) { 75 | // if (k === 'NFS3_OK') 76 | // return; 77 | 78 | // var name = 'Nfs' + k.split('_')[1] + 'Error'; 79 | // var err = function () { 80 | // NfsError.apply(this, arguments); 81 | // this.nfsstat3 = NFSSTAT3[k]; 82 | // }; 83 | // util.inherits(err, NfsError); 84 | // err.prototype.name = name; 85 | 86 | // module.exports[name] = err; 87 | // }); 88 | 89 | 90 | Object.keys(NFSSTAT3).forEach(function (k) { 91 | module.exports[k] = NFSSTAT3[k]; 92 | }); 93 | -------------------------------------------------------------------------------- /lib/nfs/fattr3.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 path = require('path'); 8 | var util = require('util'); 9 | 10 | var assert = require('assert-plus'); 11 | var clone = require('clone'); 12 | var rpc = require('oncrpc'); 13 | 14 | 15 | 16 | ///--- Globals 17 | 18 | var XDR = rpc.XDR; 19 | 20 | 21 | 22 | ///--- Globals 23 | 24 | var ftype3 = { 25 | NF3REG: 1, 26 | NF3DIR: 2, 27 | NF3BLK: 3, 28 | NF3CHR: 4, 29 | NF3LNK: 5, 30 | NF3SOCK: 6, 31 | NF3FIFO: 7 32 | }; 33 | 34 | 35 | // struct nfstime3 { 36 | // uint32 seconds; 37 | // uint32 nseconds; 38 | // }; 39 | 40 | // struct specdata3 { 41 | // uint32 specdata1; 42 | // uint32 specdata2; 43 | // }; 44 | 45 | // struct fattr3 { 46 | // ftype3 type; // unit32 47 | // mode3 mode; // uint32 48 | // uint32 nlink; // uint32 49 | // uid3 uid; // uint32 50 | // gid3 gid; // uint32 51 | // size3 size; // uint64 52 | // size3 used; // uint64 53 | // specdata3 rdev; 54 | // uint64 fsid; 55 | // fileid3 fileid; // uint64 56 | // nfstime3 atime; 57 | // nfstime3 mtime; 58 | // nfstime3 ctime; 59 | // }; 60 | 61 | 62 | function get_type(t) { 63 | var type; 64 | 65 | if (t.isFile()) { 66 | type = ftype3.NF3REG; 67 | } else if (t.isDirectory()) { 68 | type = ftype3.NF3DIR; 69 | } else if (t.isBlockDevice()) { 70 | type = ftype3.NF3CHR; 71 | } else if (t.isSymbolicLink()) { 72 | type = ftype3.NF3LNK; 73 | } else if (t.isSocket()) { 74 | type = ftype3.NF3SOCK; 75 | } else if (t.isFIFO()) { 76 | type = ftype3.NF3FIFO; 77 | } else { 78 | type = -1; 79 | } 80 | 81 | return (type); 82 | } 83 | 84 | 85 | function create_fattr3(stats) { 86 | var fattr3 = { 87 | type: get_type(stats), 88 | mode: stats.mode, 89 | nlink: stats.nlink, 90 | uid: stats.uid, 91 | gid: stats.gid, 92 | size: stats.size, 93 | used: stats.size, 94 | rdev: { 95 | specdata1: 0, 96 | specdata2: 0 97 | }, 98 | fsid: stats.dev, 99 | fileid: stats.ino, 100 | atime: { 101 | seconds: Math.floor(new Date(stats.atime).getTime() / 1000), 102 | nseconds: 0 103 | }, 104 | mtime: { 105 | seconds: Math.floor(new Date(stats.mtime).getTime() / 1000), 106 | nseconds: 0 107 | }, 108 | ctime: { 109 | seconds: Math.floor(new Date(stats.ctime).getTime() / 1000), 110 | nseconds: 0 111 | } 112 | }; 113 | 114 | return (fattr3); 115 | } 116 | 117 | 118 | function parse_fattr3(xdr) { 119 | assert.object(xdr, 'xdr'); 120 | 121 | var fattr3 = { 122 | type: xdr.readInt(), 123 | mode: xdr.readInt(), 124 | nlink: xdr.readInt(), 125 | uid: xdr.readInt(), 126 | gid: xdr.readInt(), 127 | size: xdr.readHyper(), 128 | used: xdr.readHyper(), 129 | rdev: { 130 | specdata1: xdr.readInt(), 131 | specdata2: xdr.readInt() 132 | }, 133 | fsid: xdr.readHyper(), 134 | fileid: xdr.readHyper(), 135 | atime: { 136 | seconds: xdr.readInt(), 137 | nseconds: xdr.readInt() 138 | }, 139 | mtime: { 140 | seconds: xdr.readInt(), 141 | nseconds: xdr.readInt() 142 | }, 143 | ctime: { 144 | seconds: xdr.readInt(), 145 | nseconds: xdr.readInt() 146 | } 147 | }; 148 | 149 | return (fattr3); 150 | } 151 | 152 | 153 | function serialize_fattr3(xdr, fattr3) { 154 | assert.object(xdr, 'xdr'); 155 | assert.object(fattr3, 'fattr3'); 156 | 157 | xdr.writeInt(fattr3.type); 158 | xdr.writeInt(fattr3.mode); 159 | xdr.writeInt(fattr3.nlink); 160 | xdr.writeInt(fattr3.uid); 161 | xdr.writeInt(fattr3.gid); 162 | xdr.writeHyper(fattr3.size); 163 | xdr.writeHyper(fattr3.used); 164 | xdr.writeInt(fattr3.rdev.specdata1); 165 | xdr.writeInt(fattr3.rdev.specdata2); 166 | xdr.writeHyper(fattr3.fsid); 167 | xdr.writeHyper(fattr3.fileid); 168 | xdr.writeInt(fattr3.atime.seconds); 169 | xdr.writeInt(fattr3.atime.nseconds); 170 | xdr.writeInt(fattr3.mtime.seconds); 171 | xdr.writeInt(fattr3.mtime.nseconds); 172 | xdr.writeInt(fattr3.ctime.seconds); 173 | xdr.writeInt(fattr3.ctime.nseconds); 174 | 175 | return (xdr); 176 | } 177 | 178 | 179 | 180 | ///--- Exports 181 | 182 | module.exports = { 183 | create: create_fattr3, 184 | create_fattr3: create_fattr3, 185 | parse: parse_fattr3, 186 | serialize: serialize_fattr3, 187 | XDR_SIZE: 84 188 | }; 189 | -------------------------------------------------------------------------------- /lib/nfs/fs_stat_call.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 | // 3.3.18 Procedure 18: FSSTAT - Get dynamic file system information 8 | // 9 | // SYNOPSIS 10 | // 11 | // FSSTAT3res NFSPROC3_FSSTAT(FSSTAT3args) = 18; 12 | // 13 | // struct FSSTAT3args { 14 | // nfs_fh3 fsroot; 15 | // }; 16 | // 17 | // struct FSSTAT3resok { 18 | // post_op_attr obj_attributes; 19 | // size3 tbytes; 20 | // size3 fbytes; 21 | // size3 abytes; 22 | // size3 tfiles; 23 | // size3 ffiles; 24 | // size3 afiles; 25 | // uint32 invarsec; 26 | // }; 27 | // 28 | // struct FSSTAT3resfail { 29 | // post_op_attr obj_attributes; 30 | // }; 31 | // 32 | // union FSSTAT3res switch (nfsstat3 status) { 33 | // case NFS3_OK: 34 | // FSSTAT3resok resok; 35 | // default: 36 | // FSSTAT3resfail resfail; 37 | // }; 38 | // 39 | // DESCRIPTION 40 | // 41 | // Procedure FSSTAT retrieves volatile file system state 42 | // information. On entry, the arguments in FSSTAT3args are: 43 | // 44 | // fsroot 45 | // A file handle identifying a object in the file system. 46 | // This is normally a file handle for a mount point for a 47 | // file system, as originally obtained from the MOUNT 48 | // service on the server. 49 | // 50 | // On successful return, FSSTAT3res.status is NFS3_OK and 51 | // FSSTAT3res.resok contains: 52 | // 53 | // obj_attributes 54 | // The attributes of the file system object specified in 55 | // fsroot. 56 | // 57 | // tbytes 58 | // The total size, in bytes, of the file system. 59 | // 60 | // fbytes 61 | // The amount of free space, in bytes, in the file 62 | // system. 63 | // 64 | // abytes 65 | // The amount of free space, in bytes, available to the 66 | // user identified by the authentication information in 67 | // the RPC. (This reflects space that is reserved by the 68 | // file system; it does not reflect any quota system 69 | // implemented by the server.) 70 | // 71 | // tfiles 72 | // The total number of file slots in the file system. (On 73 | // a UNIX server, this often corresponds to the number of 74 | // inodes configured.) 75 | // 76 | // ffiles 77 | // The number of free file slots in the file system. 78 | // 79 | // afiles 80 | // The number of free file slots that are available to the 81 | // user corresponding to the authentication information in 82 | // the RPC. (This reflects slots that are reserved by the 83 | // file system; it does not reflect any quota system 84 | // implemented by the server.) 85 | // 86 | // invarsec 87 | // A measure of file system volatility: this is the number 88 | // of seconds for which the file system is not expected to 89 | // change. For a volatile, frequently updated file system, 90 | // this will be 0. For an immutable file system, such as a 91 | // CD-ROM, this would be the largest unsigned integer. For 92 | // file systems that are infrequently modified, for 93 | // example, one containing local executable programs and 94 | // on-line documentation, a value corresponding to a few 95 | // hours or days might be used. The client may use this as 96 | // a hint in tuning its cache management. Note however, 97 | // this measure is assumed to be dynamic and may change at 98 | // any time. 99 | // 100 | // Otherwise, FSSTAT3res.status contains the error on failure 101 | // and FSSTAT3res.resfail contains the following: 102 | // 103 | // obj_attributes 104 | // The attributes of the file system object specified in 105 | // fsroot. 106 | // 107 | // IMPLEMENTATION 108 | // 109 | // Not all implementations can support the entire list of 110 | // attributes. It is expected that servers will make a best 111 | // effort at supporting all the attributes. 112 | // 113 | // ERRORS 114 | // 115 | // NFS3ERR_IO 116 | // NFS3ERR_STALE 117 | // NFS3ERR_BADHANDLE 118 | // NFS3ERR_SERVERFAULT 119 | // 120 | // SEE ALSO 121 | // 122 | // FSINFO. 123 | 124 | var util = require('util'); 125 | 126 | var assert = require('assert-plus'); 127 | var rpc = require('oncrpc'); 128 | 129 | var NfsCall = require('./nfs_call').NfsCall; 130 | 131 | 132 | 133 | ///--- Globals 134 | 135 | var XDR = rpc.XDR; 136 | 137 | 138 | 139 | ///--- API 140 | 141 | function FsStatCall(opts) { 142 | NfsCall.call(this, opts, true); 143 | 144 | this.fsroot = opts.fsroot || ''; 145 | 146 | this._nfs_fs_stat_call = true; // MDB 147 | } 148 | util.inherits(FsStatCall, NfsCall); 149 | Object.defineProperty(FsStatCall.prototype, 'object', { 150 | get: function object() { 151 | return (this.fsroot); 152 | } 153 | }); 154 | 155 | 156 | FsStatCall.prototype._transform = function _transform(chunk, enc, cb) { 157 | if (this.incoming) { 158 | var xdr = new XDR(chunk); 159 | this.fsroot = xdr.readString(); 160 | } else { 161 | this.push(chunk); 162 | } 163 | 164 | cb(); 165 | }; 166 | 167 | 168 | FsStatCall.prototype.writeHead = function writeHead() { 169 | var xdr = this._serialize(XDR.byteLength(this.fsroot)); 170 | xdr.writeString(this.fsroot); 171 | 172 | this.write(xdr.buffer()); 173 | }; 174 | 175 | 176 | FsStatCall.prototype.toString = function toString() { 177 | var fmt = '[object FsStatCall ]'; 178 | return (util.format(fmt, this.xid, this.fsroot)); 179 | }; 180 | 181 | 182 | 183 | ///--- Exports 184 | 185 | module.exports = { 186 | FsStatCall: FsStatCall 187 | }; 188 | -------------------------------------------------------------------------------- /lib/nfs/get_attr_call.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 | // 3.3.1 Procedure 1: GETATTR - Get file attributes 8 | // 9 | // SYNOPSIS 10 | // 11 | // GETATTR3res NFSPROC3_GETATTR(GETATTR3args) = 1; 12 | // 13 | // struct GETATTR3args { 14 | // nfs_fh3 object; 15 | // }; 16 | // 17 | // struct GETATTR3resok { 18 | // fattr3 obj_attributes; 19 | // }; 20 | // 21 | // union GETATTR3res switch (nfsstat3 status) { 22 | // case NFS3_OK: 23 | // GETATTR3resok resok; 24 | // default: 25 | // void; 26 | // }; 27 | // 28 | // DESCRIPTION 29 | // 30 | // Procedure GETATTR retrieves the attributes for a specified 31 | // file system object. The object is identified by the file 32 | // handle that the server returned as part of the response 33 | // from a LOOKUP, CREATE, MKDIR, SYMLINK, MKNOD, or 34 | // READDIRPLUS procedure (or from the MOUNT service, 35 | // described elsewhere). On entry, the arguments in 36 | // GETATTR3args are: 37 | // 38 | // object 39 | // The file handle of an object whose attributes are to be 40 | // retrieved. 41 | // 42 | // On successful return, GETATTR3res.status is NFS3_OK and 43 | // GETATTR3res.resok contains: 44 | // 45 | // obj_attributes 46 | // The attributes for the object. 47 | // 48 | // Otherwise, GETATTR3res.status contains the error on failure and 49 | // no other results are returned. 50 | // 51 | // IMPLEMENTATION 52 | // 53 | // The attributes of file system objects is a point of major 54 | // disagreement between different operating systems. Servers 55 | // should make a best attempt to support all of the 56 | // attributes in the fattr3 structure so that clients can 57 | // count on this as a common ground. Some mapping may be 58 | // required to map local attributes to those in the fattr3 59 | // structure. 60 | // 61 | // Today, most client NFS version 3 protocol implementations 62 | // implement a time-bounded attribute caching scheme to 63 | // reduce over-the-wire attribute checks. 64 | // 65 | // ERRORS 66 | // 67 | // NFS3ERR_IO 68 | // NFS3ERR_STALE 69 | // NFS3ERR_BADHANDLE 70 | // NFS3ERR_SERVERFAULT 71 | // 72 | // SEE ALSO 73 | // 74 | // ACCESS. 75 | 76 | 77 | var util = require('util'); 78 | 79 | var assert = require('assert-plus'); 80 | var rpc = require('oncrpc'); 81 | 82 | var NfsCall = require('./nfs_call').NfsCall; 83 | 84 | 85 | 86 | ///--- Globals 87 | 88 | var XDR = rpc.XDR; 89 | 90 | 91 | 92 | ///--- API 93 | 94 | function GetAttrCall(opts) { 95 | NfsCall.call(this, opts, true); 96 | 97 | this._object = opts.object || ''; 98 | 99 | this._nfs_get_attr_call = true; // MDB 100 | } 101 | util.inherits(GetAttrCall, NfsCall); 102 | Object.defineProperty(GetAttrCall.prototype, 'object', { 103 | get: function object() { 104 | return (this._object); 105 | } 106 | }); 107 | 108 | 109 | GetAttrCall.prototype._transform = function _transform(chunk, enc, cb) { 110 | if (this.incoming) { 111 | var xdr = new XDR(chunk); 112 | this._object = xdr.readString(); 113 | } else { 114 | this.push(chunk); 115 | } 116 | 117 | cb(); 118 | }; 119 | 120 | 121 | GetAttrCall.prototype.writeHead = function writeHead() { 122 | var xdr = this._serialize(XDR.byteLength(this._object)); 123 | xdr.writeString(this._object); 124 | 125 | this.write(xdr.buffer()); 126 | }; 127 | 128 | 129 | GetAttrCall.prototype.toString = function toString() { 130 | var fmt = '[object GetAttrCall ]'; 131 | return (util.format(fmt, this.xid, this._object)); 132 | }; 133 | 134 | 135 | 136 | ///--- Exports 137 | 138 | module.exports = { 139 | GetAttrCall: GetAttrCall 140 | }; 141 | -------------------------------------------------------------------------------- /lib/nfs/get_attr_reply.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 | // 3.3.1 Procedure 1: GETATTR - Get file attributes 8 | // 9 | // SYNOPSIS 10 | // 11 | // GETATTR3res NFSPROC3_GETATTR(GETATTR3args) = 1; 12 | // 13 | // struct GETATTR3args { 14 | // nfs_fh3 object; 15 | // }; 16 | // 17 | // struct GETATTR3resok { 18 | // fattr3 obj_attributes; 19 | // }; 20 | // 21 | // union GETATTR3res switch (nfsstat3 status) { 22 | // case NFS3_OK: 23 | // GETATTR3resok resok; 24 | // default: 25 | // void; 26 | // }; 27 | // 28 | // DESCRIPTION 29 | // 30 | // Procedure GETATTR retrieves the attributes for a specified 31 | // file system object. The object is identified by the file 32 | // handle that the server returned as part of the response 33 | // from a LOOKUP, CREATE, MKDIR, SYMLINK, MKNOD, or 34 | // READDIRPLUS procedure (or from the MOUNT service, 35 | // described elsewhere). On entry, the arguments in 36 | // GETATTR3args are: 37 | // 38 | // object 39 | // The file handle of an object whose attributes are to be 40 | // retrieved. 41 | // 42 | // On successful return, GETATTR3res.status is NFS3_OK and 43 | // GETATTR3res.resok contains: 44 | // 45 | // obj_attributes 46 | // The attributes for the object. 47 | // 48 | // Otherwise, GETATTR3res.status contains the error on failure and 49 | // no other results are returned. 50 | // 51 | // IMPLEMENTATION 52 | // 53 | // The attributes of file system objects is a point of major 54 | // disagreement between different operating systems. Servers 55 | // should make a best attempt to support all of the 56 | // attributes in the fattr3 structure so that clients can 57 | // count on this as a common ground. Some mapping may be 58 | // required to map local attributes to those in the fattr3 59 | // structure. 60 | // 61 | // Today, most client NFS version 3 protocol implementations 62 | // implement a time-bounded attribute caching scheme to 63 | // reduce over-the-wire attribute checks. 64 | // 65 | // ERRORS 66 | // 67 | // NFS3ERR_IO 68 | // NFS3ERR_STALE 69 | // NFS3ERR_BADHANDLE 70 | // NFS3ERR_SERVERFAULT 71 | // 72 | // SEE ALSO 73 | // 74 | // ACCESS. 75 | 76 | var fs = require('fs'); 77 | var path = require('path'); 78 | var util = require('util'); 79 | 80 | var assert = require('assert-plus'); 81 | var clone = require('clone'); 82 | var rpc = require('oncrpc'); 83 | 84 | var nfs_err = require('./errors'); 85 | var fattr3 = require('./fattr3'); 86 | var NfsReply = require('./nfs_reply').NfsReply; 87 | 88 | 89 | 90 | ///--- Globals 91 | 92 | var XDR = rpc.XDR; 93 | 94 | 95 | 96 | ///--- API 97 | 98 | function GetAttrReply(opts) { 99 | NfsReply.call(this, opts); 100 | 101 | this.status = 0; 102 | this.obj_attributes = opts.obj_attributes | {}; 103 | 104 | this._nfs_get_attr_reply = true; // MDB 105 | } 106 | util.inherits(GetAttrReply, NfsReply); 107 | GetAttrReply.prototype._allowed_error_codes = [ 108 | nfs_err.NFS3ERR_IO, 109 | nfs_err.NFS3ERR_STALE, 110 | nfs_err.NFS3ERR_BADHANDLE, 111 | nfs_err.NFS3ERR_SERVERFAULT 112 | ]; 113 | 114 | 115 | GetAttrReply.prototype.setAttributes = function setAttributes(stats) { 116 | assert.ok(stats instanceof fs.Stats, 'fs.Stats'); 117 | 118 | this.obj_attributes = fattr3.create(stats); 119 | 120 | return (this.obj_attributes); 121 | }; 122 | 123 | 124 | GetAttrReply.prototype._transform = function _transform(chunk, enc, cb) { 125 | if (this.incoming) { 126 | var xdr = new XDR(chunk); 127 | 128 | this.status = xdr.readInt(); 129 | if (this.status === 0) 130 | this.obj_attributes = fattr3.parse(xdr); 131 | } else { 132 | this.push(chunk); 133 | } 134 | 135 | cb(); 136 | }; 137 | 138 | 139 | GetAttrReply.prototype.writeHead = function writeHead() { 140 | var len = 4; 141 | if (this.status === 0) 142 | len += fattr3.XDR_SIZE; 143 | 144 | var xdr = this._serialize(len); 145 | 146 | xdr.writeInt(this.status); 147 | if (this.status === 0) 148 | fattr3.serialize(xdr, this.obj_attributes); 149 | 150 | this.write(xdr.buffer()); 151 | }; 152 | 153 | 154 | GetAttrReply.prototype.toString = function toString() { 155 | var fmt = '[object GetAttrReply ]'; 156 | return (util.format(fmt, this.xid, this.status, this.obj_attributes)); 157 | }; 158 | 159 | 160 | 161 | ///--- Exports 162 | 163 | module.exports = { 164 | GetAttrReply: GetAttrReply 165 | }; 166 | -------------------------------------------------------------------------------- /lib/nfs/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 errors = require('./errors'); 8 | 9 | ///--- Helpers 10 | 11 | function _export(obj) { 12 | Object.keys(obj).forEach(function (k) { 13 | module.exports[k] = obj[k]; 14 | }); 15 | } 16 | 17 | 18 | 19 | ///--- Exports 20 | 21 | module.exports = { 22 | handle_error: function handle_error(err, req, res, next) { 23 | req.log.warn(err, 'stat failed'); 24 | switch (err.code) { 25 | case 'EACCESS': 26 | res.error(errors.NFS3ERR_ACCES); 27 | break; 28 | 29 | case 'ENOENT': 30 | res.error(errors.NFS3ERR_NOENT); 31 | break; 32 | 33 | case 'ENOTDIR': 34 | res.error(errors.NFS3ERR_NOTDIR); 35 | break; 36 | 37 | case 'ENOTEMPTY': 38 | res.error(errors.NFS3ERR_NOTEMPTY); 39 | break; 40 | 41 | default: 42 | res.error(errors.NFS3ERR_SERVERFAULT); 43 | break; 44 | } 45 | next(false); 46 | } 47 | }; 48 | 49 | _export(errors); 50 | _export(require('./client')); 51 | _export(require('./server')); 52 | _export(require('./access_call')); 53 | _export(require('./access_reply')); 54 | _export(require('./create_call')); 55 | _export(require('./create_reply')); 56 | _export(require('./commit_call')); 57 | _export(require('./commit_reply')); 58 | _export(require('./fattr3')); 59 | _export(require('./fs_info_call')); 60 | _export(require('./fs_info_reply')); 61 | _export(require('./fs_stat_call')); 62 | _export(require('./fs_stat_reply')); 63 | _export(require('./get_attr_call')); 64 | _export(require('./get_attr_reply')); 65 | _export(require('./link_call')); 66 | _export(require('./link_reply')); 67 | _export(require('./lookup_call')); 68 | _export(require('./lookup_reply')); 69 | _export(require('./mkdir_call')); 70 | _export(require('./mkdir_reply')); 71 | _export(require('./mknod_call')); 72 | _export(require('./mknod_reply')); 73 | _export(require('./nfs_call')); 74 | _export(require('./nfs_reply')); 75 | _export(require('./path_conf_call')); 76 | _export(require('./path_conf_reply')); 77 | _export(require('./readdir_call')); 78 | _export(require('./readdir_reply')); 79 | _export(require('./readdirplus_call')); 80 | _export(require('./readdirplus_reply')); 81 | _export(require('./read_call')); 82 | _export(require('./read_reply')); 83 | _export(require('./readlink_call')); 84 | _export(require('./readlink_reply')); 85 | _export(require('./remove_call')); 86 | _export(require('./remove_reply')); 87 | _export(require('./rmdir_call')); 88 | _export(require('./rmdir_reply')); 89 | _export(require('./sattr3')); 90 | _export(require('./set_attr_call')); 91 | _export(require('./set_attr_reply')); 92 | _export(require('./symlink_call')); 93 | _export(require('./symlink_reply')); 94 | _export(require('./write_call')); 95 | _export(require('./write_reply')); 96 | -------------------------------------------------------------------------------- /lib/nfs/link_call.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 | // 3.3.15 Procedure 15: LINK - Create Link to an object 8 | // 9 | // SYNOPSIS 10 | // 11 | // LINK3res NFSPROC3_LINK(LINK3args) = 15; 12 | // 13 | // struct LINK3args { 14 | // nfs_fh3 file; 15 | // diropargs3 link; 16 | // }; 17 | // 18 | // struct LINK3resok { 19 | // post_op_attr file_attributes; 20 | // wcc_data linkdir_wcc; 21 | // }; 22 | // 23 | // struct LINK3resfail { 24 | // post_op_attr file_attributes; 25 | // wcc_data linkdir_wcc; 26 | // }; 27 | // 28 | // union LINK3res switch (nfsstat3 status) { 29 | // case NFS3_OK: 30 | // LINK3resok resok; 31 | // default: 32 | // LINK3resfail resfail; 33 | // }; 34 | // 35 | // DESCRIPTION 36 | // 37 | // Procedure LINK creates a hard link from file to link.name, 38 | // in the directory, link.dir. file and link.dir must reside 39 | // on the same file system and server. On entry, the 40 | // arguments in LINK3args are: 41 | // 42 | // file 43 | // The file handle for the existing file system object. 44 | // 45 | // link 46 | // The location of the link to be created: 47 | // 48 | // link.dir 49 | // The file handle for the directory in which the link 50 | // is to be created. 51 | // 52 | // link.name 53 | // The name that is to be associated with the created 54 | // link. Refer to General comments on filenames on page 55 | // 17. 56 | // 57 | // On successful return, LINK3res.status is NFS3_OK and 58 | // LINK3res.resok contains: 59 | // 60 | // file_attributes 61 | // The post-operation attributes of the file system object 62 | // identified by file. 63 | // 64 | // linkdir_wcc 65 | // Weak cache consistency data for the directory, 66 | // link.dir. 67 | // 68 | // Otherwise, LINK3res.status contains the error on failure 69 | // and LINK3res.resfail contains the following: 70 | // 71 | // file_attributes 72 | // The post-operation attributes of the file system object 73 | // identified by file. 74 | // 75 | // linkdir_wcc 76 | // Weak cache consistency data for the directory, 77 | // link.dir. 78 | // 79 | // IMPLEMENTATION 80 | // 81 | // Changes to any property of the hard-linked files are 82 | // reflected in all of the linked files. When a hard link is 83 | // made to a file, the attributes for the file should have a 84 | // value for nlink that is one greater than the value before 85 | // the LINK. 86 | // 87 | // The comments under RENAME regarding object and target 88 | // residing on the same file system apply here as well. The 89 | // comments regarding the target name applies as well. Refer 90 | // to General comments on filenames on page 30. 91 | // 92 | // ERRORS 93 | // 94 | // NFS3ERR_IO 95 | // NFS3ERR_ACCES 96 | // NFS3ERR_EXIST 97 | // NFS3ERR_XDEV 98 | // NFS3ERR_NOTDIR 99 | // NFS3ERR_INVAL 100 | // NFS3ERR_NOSPC 101 | // NFS3ERR_ROFS 102 | // NFS3ERR_MLINK 103 | // NFS3ERR_NAMETOOLONG 104 | // NFS3ERR_DQUOT 105 | // NFS3ERR_STALE 106 | // NFS3ERR_BADHANDLE 107 | // NFS3ERR_NOTSUPP 108 | // NFS3ERR_SERVERFAULT 109 | // 110 | // SEE ALSO 111 | // 112 | // SYMLINK, RENAME and FSINFO. 113 | 114 | 115 | var util = require('util'); 116 | 117 | var assert = require('assert-plus'); 118 | var rpc = require('oncrpc'); 119 | 120 | var NfsCall = require('./nfs_call').NfsCall; 121 | 122 | 123 | 124 | ///--- Globals 125 | 126 | var XDR = rpc.XDR; 127 | 128 | 129 | 130 | ///--- API 131 | 132 | function LinkCall(opts) { 133 | assert.object(opts, 'opts'); 134 | assert.optionalString(opts.file, 'options.file'); 135 | assert.optionalObject(opts.link, 'opts.link'); 136 | 137 | NfsCall.call(this, opts, true); 138 | 139 | this.file = opts.file || ''; 140 | this.link = opts.link || { 141 | dir: '', 142 | name: '' 143 | }; 144 | 145 | this._nfs_link_call = true; // MDB 146 | } 147 | util.inherits(LinkCall, NfsCall); 148 | Object.defineProperty(LinkCall.prototype, 'object', { 149 | get: function object() { 150 | return (this.file); 151 | } 152 | }); 153 | 154 | 155 | LinkCall.prototype._transform = function _transform(chunk, enc, cb) { 156 | if (this.incoming) { 157 | var xdr = new XDR(chunk); 158 | 159 | this.file = xdr.readString(); 160 | this.link.dir = xdr.readString(); 161 | this.link.name = xdr.readString(); 162 | } else { 163 | this.push(chunk); 164 | } 165 | 166 | cb(); 167 | }; 168 | 169 | 170 | LinkCall.prototype.writeHead = function writeHead() { 171 | var len = XDR.byteLength(this.file) + XDR.byteLength(this.link.dir) + 172 | XDR.byteLength(this.link.name); 173 | 174 | var xdr = this._serialize(len); 175 | 176 | xdr.writeString(this.file); 177 | xdr.writeString(this.link.dir); 178 | xdr.writeString(this.link.name); 179 | 180 | this.write(xdr.buffer()); 181 | }; 182 | 183 | 184 | LinkCall.prototype.toString = function toString() { 185 | var fmt = '[object LinkCall ]'; 186 | return (util.format(fmt, this.xid, this.file, this.link)); 187 | }; 188 | 189 | 190 | 191 | ///--- Exports 192 | 193 | module.exports = { 194 | LinkCall: LinkCall 195 | }; 196 | -------------------------------------------------------------------------------- /lib/nfs/link_reply.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 | // 3.3.15 Procedure 15: LINK - Create Link to an object 8 | // 9 | // SYNOPSIS 10 | // 11 | // LINK3res NFSPROC3_LINK(LINK3args) = 15; 12 | // 13 | // struct LINK3args { 14 | // nfs_fh3 file; 15 | // diropargs3 link; 16 | // }; 17 | // 18 | // struct LINK3resok { 19 | // post_op_attr file_attributes; 20 | // wcc_data linkdir_wcc; 21 | // }; 22 | // 23 | // struct LINK3resfail { 24 | // post_op_attr file_attributes; 25 | // wcc_data linkdir_wcc; 26 | // }; 27 | // 28 | // union LINK3res switch (nfsstat3 status) { 29 | // case NFS3_OK: 30 | // LINK3resok resok; 31 | // default: 32 | // LINK3resfail resfail; 33 | // }; 34 | // 35 | // DESCRIPTION 36 | // 37 | // Procedure LINK creates a hard link from file to link.name, 38 | // in the directory, link.dir. file and link.dir must reside 39 | // on the same file system and server. On entry, the 40 | // arguments in LINK3args are: 41 | // 42 | // file 43 | // The file handle for the existing file system object. 44 | // 45 | // link 46 | // The location of the link to be created: 47 | // 48 | // link.dir 49 | // The file handle for the directory in which the link 50 | // is to be created. 51 | // 52 | // link.name 53 | // The name that is to be associated with the created 54 | // link. Refer to General comments on filenames on page 55 | // 17. 56 | // 57 | // On successful return, LINK3res.status is NFS3_OK and 58 | // LINK3res.resok contains: 59 | // 60 | // file_attributes 61 | // The post-operation attributes of the file system object 62 | // identified by file. 63 | // 64 | // linkdir_wcc 65 | // Weak cache consistency data for the directory, 66 | // link.dir. 67 | // 68 | // Otherwise, LINK3res.status contains the error on failure 69 | // and LINK3res.resfail contains the following: 70 | // 71 | // file_attributes 72 | // The post-operation attributes of the file system object 73 | // identified by file. 74 | // 75 | // linkdir_wcc 76 | // Weak cache consistency data for the directory, 77 | // link.dir. 78 | // 79 | // IMPLEMENTATION 80 | // 81 | // Changes to any property of the hard-linked files are 82 | // reflected in all of the linked files. When a hard link is 83 | // made to a file, the attributes for the file should have a 84 | // value for nlink that is one greater than the value before 85 | // the LINK. 86 | // 87 | // The comments under RENAME regarding object and target 88 | // residing on the same file system apply here as well. The 89 | // comments regarding the target name applies as well. Refer 90 | // to General comments on filenames on page 30. 91 | // 92 | // ERRORS 93 | // 94 | // NFS3ERR_IO 95 | // NFS3ERR_ACCES 96 | // NFS3ERR_EXIST 97 | // NFS3ERR_XDEV 98 | // NFS3ERR_NOTDIR 99 | // NFS3ERR_INVAL 100 | // NFS3ERR_NOSPC 101 | // NFS3ERR_ROFS 102 | // NFS3ERR_MLINK 103 | // NFS3ERR_NAMETOOLONG 104 | // NFS3ERR_DQUOT 105 | // NFS3ERR_STALE 106 | // NFS3ERR_BADHANDLE 107 | // NFS3ERR_NOTSUPP 108 | // NFS3ERR_SERVERFAULT 109 | // 110 | // SEE ALSO 111 | // 112 | // SYMLINK, RENAME and FSINFO. 113 | 114 | var fs = require('fs'); 115 | var path = require('path'); 116 | var util = require('util'); 117 | 118 | var assert = require('assert-plus'); 119 | var clone = require('clone'); 120 | var rpc = require('oncrpc'); 121 | 122 | var nfs_err = require('./errors'); 123 | var fattr3 = require('./fattr3'); 124 | var wcc_data = require('./wcc_data'); 125 | var NfsReply = require('./nfs_reply').NfsReply; 126 | 127 | 128 | 129 | ///--- Globals 130 | 131 | var XDR = rpc.XDR; 132 | 133 | 134 | 135 | ///--- API 136 | 137 | function LinkReply(opts) { 138 | NfsReply.call(this, opts); 139 | 140 | this.status = 0; 141 | this.dir_wcc = opts.dir_wcc | null; 142 | 143 | this._nfs_link_reply = true; // MDB 144 | } 145 | util.inherits(LinkReply, NfsReply); 146 | LinkReply.prototype._allowed_error_codes = [ 147 | nfs_err.NFS3ERR_IO, 148 | nfs_err.NFS3ERR_ACCES, 149 | nfs_err.NFS3ERR_EXIST, 150 | nfs_err.NFS3ERR_XDEV, 151 | nfs_err.NFS3ERR_NOTDIR, 152 | nfs_err.NFS3ERR_INVAL, 153 | nfs_err.NFS3ERR_NOSPC, 154 | nfs_err.NFS3ERR_ROFS, 155 | nfs_err.NFS3ERR_MLINK, 156 | nfs_err.NFS3ERR_NAMETOOLONG, 157 | nfs_err.NFS3ERR_DQUOT, 158 | nfs_err.NFS3ERR_STALE, 159 | nfs_err.NFS3ERR_BADHANDLE, 160 | nfs_err.NFS3ERR_NOTSUPP, 161 | nfs_err.NFS3ERR_SERVERFAULT 162 | ]; 163 | 164 | 165 | LinkReply.prototype.setFileAttributes = function setFileAttributes(stats) { 166 | assert.ok(stats instanceof fs.Stats, 'fs.Stats'); 167 | 168 | this.file_attributes = fattr3.create(stats); 169 | 170 | return (this.file_attributes); 171 | }; 172 | 173 | 174 | LinkReply.prototype.set_linkdir_wcc = function set_linkdir_wcc() { 175 | this.linkdir_wcc = wcc_data.create(); 176 | return (this.linkdir_wcc); 177 | }; 178 | 179 | 180 | LinkReply.prototype._transform = function _transform(chunk, enc, cb) { 181 | if (this.incoming) { 182 | var xdr = new XDR(chunk); 183 | 184 | this.status = xdr.readInt(); 185 | this.fromdir_wcc = wcc_data.parse(xdr); 186 | this.todir_wcc = wcc_data.parse(xdr); 187 | } else { 188 | this.push(chunk); 189 | } 190 | 191 | cb(); 192 | }; 193 | 194 | 195 | LinkReply.prototype.writeHead = function writeHead() { 196 | var len = 4; 197 | 198 | if (this.file_attributes) { 199 | len += 4 + fattr3.XDR_SIZE; 200 | } else { 201 | len += 4; 202 | } 203 | len += wcc_data.length(this.linkdir_wcc); 204 | 205 | var xdr = this._serialize(len); 206 | 207 | xdr.writeInt(this.status); 208 | 209 | if (this.file_attributes) { 210 | xdr.writeBool(true); 211 | fattr3.serialize(xdr, this.file_attributes); 212 | } else { 213 | xdr.writeBool(false); 214 | } 215 | 216 | wcc_data.serialize(xdr, this.linkdir_wcc); 217 | 218 | this.write(xdr.buffer()); 219 | }; 220 | 221 | 222 | LinkReply.prototype.toString = function toString() { 223 | var fmt = '[object LinkReply ]'; 225 | return (util.format(fmt, this.xid, this.status, this.file_attributes, 226 | this.linkdir_wcc)); 227 | }; 228 | 229 | 230 | 231 | ///--- Exports 232 | 233 | module.exports = { 234 | LinkReply: LinkReply 235 | }; 236 | -------------------------------------------------------------------------------- /lib/nfs/lookup_call.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 | // 3.3.3 Procedure 3: LOOKUP - Lookup filename 8 | // 9 | // SYNOPSIS 10 | // 11 | // LOOKUP3res NFSPROC3_LOOKUP(LOOKUP3args) = 3; 12 | // 13 | // struct LOOKUP3args { 14 | // diropargs3 what; 15 | // }; 16 | // 17 | // struct LOOKUP3resok { 18 | // nfs_fh3 object; 19 | // post_op_attr obj_attributes; 20 | // post_op_attr dir_attributes; 21 | // }; 22 | // 23 | // struct LOOKUP3resfail { 24 | // post_op_attr dir_attributes; 25 | // }; 26 | // 27 | // union LOOKUP3res switch (nfsstat3 status) { 28 | // case NFS3_OK: 29 | // LOOKUP3resok resok; 30 | // default: 31 | // LOOKUP3resfail resfail; 32 | // }; 33 | // 34 | // DESCRIPTION 35 | // 36 | // Procedure LOOKUP searches a directory for a specific name 37 | // and returns the file handle for the corresponding file 38 | // system object. On entry, the arguments in LOOKUP3args 39 | // are: 40 | // 41 | // what 42 | // Object to look up: 43 | // 44 | // dir 45 | // The file handle for the directory to search. 46 | // 47 | // name 48 | // The filename to be searched for. Refer to General 49 | // comments on filenames on page 30. 50 | // 51 | // On successful return, LOOKUP3res.status is NFS3_OK and 52 | // LOOKUP3res.resok contains: 53 | // 54 | // object 55 | // The file handle of the object corresponding to 56 | // what.name. 57 | // 58 | // obj_attributes 59 | // The attributes of the object corresponding to 60 | // what.name. 61 | // 62 | // dir_attributes 63 | // The post-operation attributes of the directory, 64 | // what.dir. 65 | // 66 | // Otherwise, LOOKUP3res.status contains the error on failure and 67 | // LOOKUP3res.resfail contains the following: 68 | // 69 | // dir_attributes 70 | // The post-operation attributes for the directory, 71 | // what.dir. 72 | // 73 | // IMPLEMENTATION 74 | // 75 | // At first glance, in the case where what.name refers to a 76 | // mount point on the server, two different replies seem 77 | // possible. The server can return either the file handle for 78 | // the underlying directory that is mounted on or the file 79 | // handle of the root of the mounted directory. This 80 | // ambiguity is simply resolved. A server will not allow a 81 | // LOOKUP operation to cross a mountpoint to the root of a 82 | // different filesystem, even if the filesystem is exported. 83 | // This does not prevent a client from accessing a hierarchy 84 | // of filesystems exported by a server, but the client must 85 | // mount each of the filesystems individually so that the 86 | // mountpoint crossing takes place on the client. A given 87 | // server implementation may refine these rules given 88 | // capabilities or limitations particular to that 89 | // implementation. Refer to [X/OpenNFS] for a discussion on 90 | // exporting file systems. 91 | // 92 | // Two filenames are distinguished, as in the NFS version 2 93 | // protocol. The name, ".", is an alias for the current 94 | // directory and the name, "..", is an alias for the parent 95 | // directory; that is, the directory that includes the 96 | // specified directory as a member. There is no facility for 97 | // dealing with a multiparented directory and the NFS 98 | // protocol assumes a hierarchical organization, organized as 99 | // a single-rooted tree. 100 | // Note that this procedure does not follow symbolic links. 101 | // The client is responsible for all parsing of filenames 102 | // including filenames that are modified by symbolic links 103 | // encountered during the lookup process. 104 | // 105 | // ERRORS 106 | // 107 | // NFS3ERR_IO 108 | // NFS3ERR_NOENT 109 | // NFS3ERR_ACCES 110 | // NFS3ERR_NOTDIR 111 | // NFS3ERR_NAMETOOLONG 112 | // NFS3ERR_STALE 113 | // NFS3ERR_BADHANDLE 114 | // NFS3ERR_SERVERFAULT 115 | // 116 | // SEE ALSO 117 | // 118 | // CREATE, MKDIR, SYMLINK, MKNOD, READDIRPLUS, and PATHCONF. 119 | 120 | var path = require('path'); 121 | var util = require('util'); 122 | 123 | var assert = require('assert-plus'); 124 | var rpc = require('oncrpc'); 125 | 126 | var NfsCall = require('./nfs_call').NfsCall; 127 | 128 | 129 | 130 | ///--- Globals 131 | 132 | var XDR = rpc.XDR; 133 | 134 | 135 | 136 | ///--- API 137 | 138 | function LookupCall(opts) { 139 | assert.object(opts, 'opts'); 140 | assert.optionalObject(opts.what, 'opts.what'); 141 | 142 | NfsCall.call(this, opts, true); 143 | 144 | this.what = opts.what || { 145 | dir: '', 146 | name: '' 147 | }; 148 | 149 | this._nfs_lookup_call = true; // MDB 150 | } 151 | util.inherits(LookupCall, NfsCall); 152 | Object.defineProperty(LookupCall.prototype, 'object', { 153 | get: function object() { 154 | return (this.what.dir); 155 | } 156 | }); 157 | 158 | 159 | LookupCall.prototype._transform = function _transform(chunk, enc, cb) { 160 | if (this.incoming) { 161 | var xdr = new XDR(chunk); 162 | this.what.dir = xdr.readString(); 163 | this.what.name = xdr.readString(); 164 | } else { 165 | this.push(chunk); 166 | } 167 | 168 | cb(); 169 | }; 170 | 171 | 172 | LookupCall.prototype.writeHead = function writeHead() { 173 | var xdr = this._serialize(XDR.byteLength(this.what.dir) + 174 | XDR.byteLength(this.what.name)); 175 | xdr.writeString(this.what.dir); 176 | xdr.writeString(this.what.name); 177 | 178 | this.write(xdr.buffer()); 179 | }; 180 | 181 | 182 | LookupCall.prototype.toString = function toString() { 183 | var fmt = '[object LookupCall ]'; 184 | return (util.format(fmt, this.xid, this.what)); 185 | }; 186 | 187 | 188 | 189 | ///--- Exports 190 | 191 | module.exports = { 192 | LookupCall: LookupCall 193 | }; 194 | -------------------------------------------------------------------------------- /lib/nfs/mkdir_call.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 | // 3.3.9 Procedure 9: MKDIR - Create a directory 8 | // 9 | // SYNOPSIS 10 | // 11 | // MKDIR3res NFSPROC3_MKDIR(MKDIR3args) = 9; 12 | // 13 | // struct MKDIR3args { 14 | // diropargs3 where; 15 | // sattr3 attributes; 16 | // }; 17 | // 18 | // struct MKDIR3resok { 19 | // post_op_fh3 obj; 20 | // post_op_attr obj_attributes; 21 | // wcc_data dir_wcc; 22 | // }; 23 | // 24 | // struct MKDIR3resfail { 25 | // wcc_data dir_wcc; 26 | // }; 27 | // 28 | // union MKDIR3res switch (nfsstat3 status) { 29 | // case NFS3_OK: 30 | // MKDIR3resok resok; 31 | // default: 32 | // MKDIR3resfail resfail; 33 | // }; 34 | // 35 | // DESCRIPTION 36 | // 37 | // Procedure MKDIR creates a new subdirectory. On entry, the 38 | // arguments in MKDIR3args are: 39 | // 40 | // where 41 | // The location of the subdirectory to be created: 42 | // 43 | // dir 44 | // The file handle for the directory in which the 45 | // subdirectory is to be created. 46 | // 47 | // name 48 | // The name that is to be associated with the created 49 | // subdirectory. Refer to General comments on filenames 50 | // on page 30. 51 | // 52 | // attributes 53 | // The initial attributes for the subdirectory. 54 | // 55 | // On successful return, MKDIR3res.status is NFS3_OK and the 56 | // results in MKDIR3res.resok are: 57 | // 58 | // obj 59 | // The file handle for the newly created directory. 60 | // 61 | // obj_attributes 62 | // The attributes for the newly created subdirectory. 63 | // 64 | // dir_wcc 65 | // Weak cache consistency data for the directory, 66 | // where.dir. For a client that requires only the 67 | // post-MKDIR directory attributes, these can be found in 68 | // dir_wcc.after. 69 | // 70 | // Otherwise, MKDIR3res.status contains the error on failure 71 | // and MKDIR3res.resfail contains the following: 72 | // 73 | // dir_wcc 74 | // Weak cache consistency data for the directory, 75 | // where.dir. For a client that requires only the 76 | // post-MKDIR directory attributes, these can be found in 77 | // dir_wcc.after. Even though the MKDIR failed, full 78 | // wcc_data is returned to allow the client to determine 79 | // whether the failing MKDIR resulted in any change to the 80 | // directory. 81 | // 82 | // IMPLEMENTATION 83 | // 84 | // Many server implementations will not allow the filenames, 85 | // "." or "..", to be used as targets in a MKDIR operation. 86 | // In this case, the server should return NFS3ERR_EXIST. 87 | // Refer to General comments on filenames on page 30. 88 | // 89 | // ERRORS 90 | // 91 | // NFS3ERR_IO 92 | // NFS3ERR_ACCES 93 | // NFS3ERR_EXIST 94 | // NFS3ERR_NOTDIR 95 | // NFS3ERR_NOSPC 96 | // NFS3ERR_ROFS 97 | // NFS3ERR_NAMETOOLONG 98 | // NFS3ERR_DQUOT 99 | // NFS3ERR_STALE 100 | // NFS3ERR_BADHANDLE 101 | // NFS3ERR_NOTSUPP 102 | // NFS3ERR_SERVERFAULT 103 | // 104 | // SEE ALSO 105 | // 106 | // CREATE, SYMLINK, MKNOD, and PATHCONF. 107 | 108 | 109 | var util = require('util'); 110 | 111 | var assert = require('assert-plus'); 112 | var rpc = require('oncrpc'); 113 | 114 | var sattr3 = require('./sattr3'); 115 | 116 | var NfsCall = require('./nfs_call').NfsCall; 117 | 118 | 119 | 120 | ///--- Globals 121 | 122 | var XDR = rpc.XDR; 123 | 124 | 125 | 126 | ///--- API 127 | 128 | function MkdirCall(opts) { 129 | assert.object(opts, 'opts'); 130 | assert.optionalObject(opts.where, 'opts.where'); 131 | assert.optionalObject(opts.attributes, 'opts.attributes'); 132 | 133 | NfsCall.call(this, opts, true); 134 | 135 | this.where = opts.where || { 136 | dir: '', 137 | name: '' 138 | }; 139 | 140 | this.attributes = opts.attributes || { 141 | mode: null, 142 | uid: null, 143 | gid: null, 144 | size: null, 145 | how_a_time: 0, 146 | atime: null, 147 | how_m_time: 0, 148 | mtime: null 149 | }; 150 | 151 | this._nfs_mkdir_call = true; // MDB 152 | } 153 | util.inherits(MkdirCall, NfsCall); 154 | Object.defineProperty(MkdirCall.prototype, 'object', { 155 | get: function object() { 156 | return (this.where.dir); 157 | } 158 | }); 159 | 160 | 161 | MkdirCall.prototype._transform = function _transform(chunk, enc, cb) { 162 | if (this.incoming) { 163 | var xdr = new XDR(chunk); 164 | this.where.dir = xdr.readString(); 165 | this.where.name = xdr.readString(); 166 | this.attributes = sattr3.parse(xdr); 167 | } else { 168 | this.push(chunk); 169 | } 170 | 171 | cb(); 172 | }; 173 | 174 | 175 | MkdirCall.prototype.writeHead = function writeHead() { 176 | var len = XDR.byteLength(this.where.dir) + XDR.byteLength(this.where.name); 177 | 178 | len += sattr3.length(this.attributes); 179 | 180 | var xdr = this._serialize(len); 181 | 182 | xdr.writeString(this.where.dir); 183 | xdr.writeString(this.where.name); 184 | 185 | sattr3.serialize(xdr, this.attributes); 186 | 187 | this.write(xdr.buffer()); 188 | }; 189 | 190 | 191 | MkdirCall.prototype.toString = function toString() { 192 | var fmt = '[object MkdirCall ]'; 193 | return (util.format(fmt, this.xid, this.where, this.attributes)); 194 | }; 195 | 196 | 197 | 198 | ///--- Exports 199 | 200 | module.exports = { 201 | MkdirCall: MkdirCall 202 | }; 203 | -------------------------------------------------------------------------------- /lib/nfs/mkdir_reply.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 | // 3.3.9 Procedure 9: MKDIR - Create a directory 8 | // 9 | // SYNOPSIS 10 | // 11 | // MKDIR3res NFSPROC3_MKDIR(MKDIR3args) = 9; 12 | // 13 | // struct MKDIR3args { 14 | // diropargs3 where; 15 | // sattr3 attributes; 16 | // }; 17 | // 18 | // struct MKDIR3resok { 19 | // post_op_fh3 obj; 20 | // post_op_attr obj_attributes; 21 | // wcc_data dir_wcc; 22 | // }; 23 | // 24 | // struct MKDIR3resfail { 25 | // wcc_data dir_wcc; 26 | // }; 27 | // 28 | // union MKDIR3res switch (nfsstat3 status) { 29 | // case NFS3_OK: 30 | // MKDIR3resok resok; 31 | // default: 32 | // MKDIR3resfail resfail; 33 | // }; 34 | // 35 | // DESCRIPTION 36 | // 37 | // Procedure MKDIR creates a new subdirectory. On entry, the 38 | // arguments in MKDIR3args are: 39 | // 40 | // where 41 | // The location of the subdirectory to be created: 42 | // 43 | // dir 44 | // The file handle for the directory in which the 45 | // subdirectory is to be created. 46 | // 47 | // name 48 | // The name that is to be associated with the created 49 | // subdirectory. Refer to General comments on filenames 50 | // on page 30. 51 | // 52 | // attributes 53 | // The initial attributes for the subdirectory. 54 | // 55 | // On successful return, MKDIR3res.status is NFS3_OK and the 56 | // results in MKDIR3res.resok are: 57 | // 58 | // obj 59 | // The file handle for the newly created directory. 60 | // 61 | // obj_attributes 62 | // The attributes for the newly created subdirectory. 63 | // 64 | // dir_wcc 65 | // Weak cache consistency data for the directory, 66 | // where.dir. For a client that requires only the 67 | // post-MKDIR directory attributes, these can be found in 68 | // dir_wcc.after. 69 | // 70 | // Otherwise, MKDIR3res.status contains the error on failure 71 | // and MKDIR3res.resfail contains the following: 72 | // 73 | // dir_wcc 74 | // Weak cache consistency data for the directory, 75 | // where.dir. For a client that requires only the 76 | // post-MKDIR directory attributes, these can be found in 77 | // dir_wcc.after. Even though the MKDIR failed, full 78 | // wcc_data is returned to allow the client to determine 79 | // whether the failing MKDIR resulted in any change to the 80 | // directory. 81 | // 82 | // IMPLEMENTATION 83 | // 84 | // Many server implementations will not allow the filenames, 85 | // "." or "..", to be used as targets in a MKDIR operation. 86 | // In this case, the server should return NFS3ERR_EXIST. 87 | // Refer to General comments on filenames on page 30. 88 | // 89 | // ERRORS 90 | // 91 | // NFS3ERR_IO 92 | // NFS3ERR_ACCES 93 | // NFS3ERR_EXIST 94 | // NFS3ERR_NOTDIR 95 | // NFS3ERR_NOSPC 96 | // NFS3ERR_ROFS 97 | // NFS3ERR_NAMETOOLONG 98 | // NFS3ERR_DQUOT 99 | // NFS3ERR_STALE 100 | // NFS3ERR_BADHANDLE 101 | // NFS3ERR_NOTSUPP 102 | // NFS3ERR_SERVERFAULT 103 | // 104 | // SEE ALSO 105 | // 106 | // CREATE, SYMLINK, MKNOD, and PATHCONF. 107 | 108 | var fs = require('fs'); 109 | var path = require('path'); 110 | var util = require('util'); 111 | 112 | var assert = require('assert-plus'); 113 | var clone = require('clone'); 114 | var rpc = require('oncrpc'); 115 | 116 | var nfs_err = require('./errors'); 117 | var fattr3 = require('./fattr3'); 118 | var wcc_data = require('./wcc_data'); 119 | var NfsReply = require('./nfs_reply').NfsReply; 120 | 121 | 122 | 123 | ///--- Globals 124 | 125 | var XDR = rpc.XDR; 126 | 127 | 128 | 129 | ///--- API 130 | 131 | function MkdirReply(opts) { 132 | NfsReply.call(this, opts); 133 | 134 | this.status = 0; 135 | this.obj = ''; 136 | this.obj_attributes = opts.obj_attributes | null; 137 | this.dir_wcc = opts.dir_wcc | null; 138 | 139 | this._nfs_mkdir_reply = true; // MDB 140 | } 141 | util.inherits(MkdirReply, NfsReply); 142 | MkdirReply.prototype._allowed_error_codes = [ 143 | nfs_err.NFS3ERR_IO, 144 | nfs_err.NFS3ERR_ACCES, 145 | nfs_err.NFS3ERR_EXIST, 146 | nfs_err.NFS3ERR_NOTDIR, 147 | nfs_err.NFS3ERR_NOSPC, 148 | nfs_err.NFS3ERR_ROFS, 149 | nfs_err.NFS3ERR_NAMETOOLONG, 150 | nfs_err.NFS3ERR_DQUOT, 151 | nfs_err.NFS3ERR_STALE, 152 | nfs_err.NFS3ERR_BADHANDLE, 153 | nfs_err.NFS3ERR_NOTSUPP, 154 | nfs_err.NFS3ERR_SERVERFAULT 155 | ]; 156 | 157 | 158 | MkdirReply.prototype.setObjAttributes = function setObjAttributes(stats) { 159 | assert.ok(stats instanceof fs.Stats, 'fs.Stats'); 160 | 161 | this.obj_attributes = fattr3.create(stats); 162 | 163 | return (this.obj_attributes); 164 | }; 165 | 166 | 167 | MkdirReply.prototype.set_dir_wcc = function set_dir_wcc() { 168 | this.dir_wcc = wcc_data.create(); 169 | return (this.dir_wcc); 170 | }; 171 | 172 | 173 | MkdirReply.prototype._transform = function _transform(chunk, enc, cb) { 174 | if (this.incoming) { 175 | var xdr = new XDR(chunk); 176 | 177 | this.status = xdr.readInt(); 178 | if (this.status === 0) { 179 | if (xdr.readBool()) 180 | this.obj = xdr.readString(); 181 | if (xdr.readBool()) 182 | this.obj_attributes = fattr3.parse(xdr); 183 | } 184 | 185 | this.dir_wcc = wcc_data.parse(xdr); 186 | } else { 187 | this.push(chunk); 188 | } 189 | 190 | cb(); 191 | }; 192 | 193 | 194 | MkdirReply.prototype.writeHead = function writeHead() { 195 | var len = 4; 196 | 197 | if (this.status === 0) { 198 | len += 4 + XDR.byteLength(this.obj); 199 | 200 | len += 4; 201 | if (this.obj_attributes) 202 | len += fattr3.XDR_SIZE; 203 | } 204 | 205 | len += wcc_data.length(this.dir_wcc); 206 | 207 | var xdr = this._serialize(len); 208 | 209 | xdr.writeInt(this.status); 210 | 211 | if (this.status === 0) { 212 | xdr.writeBool(true); 213 | xdr.writeString(this.obj); 214 | if (this.obj_attributes) { 215 | xdr.writeBool(true); 216 | fattr3.serialize(xdr, this.obj_attributes); 217 | } else { 218 | xdr.writeBool(false); 219 | } 220 | } 221 | 222 | wcc_data.serialize(xdr, this.dir_wcc); 223 | 224 | this.write(xdr.buffer()); 225 | }; 226 | 227 | 228 | MkdirReply.prototype.toString = function toString() { 229 | var fmt = '[object MkdirReply ]'; 231 | return (util.format(fmt, this.xid, this.status, this.obj, 232 | this.obj_attributes, this.dir_wcc)); 233 | }; 234 | 235 | 236 | 237 | ///--- Exports 238 | 239 | module.exports = { 240 | MkdirReply: MkdirReply 241 | }; 242 | -------------------------------------------------------------------------------- /lib/nfs/nfs_call.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 util = require('util'); 8 | 9 | var assert = require('assert-plus'); 10 | var RpcCall = require('oncrpc').RpcCall; 11 | 12 | 13 | 14 | ///--- API 15 | 16 | function NfsCall(opts) { 17 | assert.object(opts, 'options'); 18 | 19 | RpcCall.call(this, opts); 20 | 21 | this._nfs_call = true; // MDB 22 | } 23 | util.inherits(NfsCall, RpcCall); 24 | Object.defineProperty(NfsCall.prototype, 'object', { 25 | get: function object() { 26 | throw new Error('get: object must be defined'); 27 | } 28 | }); 29 | 30 | 31 | 32 | ///--- Exports 33 | 34 | module.exports = { 35 | NfsCall: NfsCall 36 | }; 37 | -------------------------------------------------------------------------------- /lib/nfs/nfs_reply.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 util = require('util'); 8 | 9 | var assert = require('assert-plus'); 10 | var RpcReply = require('oncrpc').RpcReply; 11 | 12 | 13 | 14 | ///--- API 15 | 16 | function NfsReply(opts) { 17 | assert.object(opts, 'options'); 18 | 19 | RpcReply.call(this, opts); 20 | 21 | this._nfs_reply = true; // MDB 22 | } 23 | util.inherits(NfsReply, RpcReply); 24 | 25 | 26 | NfsReply.prototype.error = function error(status) { 27 | assert.number(status, 'status'); 28 | 29 | if (!Array.isArray(this._allowed_error_codes)) 30 | throw new Error('this._allowed_error_codes must be defined'); 31 | 32 | if (!this._allowed_error_codes.some(function (c) { 33 | return (c === status); 34 | })) { 35 | throw new Error(status + ' is not an allowed status code'); 36 | } 37 | 38 | this.status = status; 39 | this.send(); 40 | }; 41 | 42 | 43 | 44 | ///--- Exports 45 | 46 | module.exports = { 47 | NfsReply: NfsReply 48 | }; 49 | -------------------------------------------------------------------------------- /lib/nfs/path_conf_call.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 | // 3.3.20 Procedure 20: PATHCONF - Retrieve POSIX information 8 | // 9 | // SYNOPSIS 10 | // 11 | // PATHCONF3res NFSPROC3_PATHCONF(PATHCONF3args) = 20; 12 | // 13 | // struct PATHCONF3args { 14 | // nfs_fh3 object; 15 | // }; 16 | // 17 | // struct PATHCONF3resok { 18 | // post_op_attr obj_attributes; 19 | // uint32 linkmax; 20 | // uint32 name_max; 21 | // bool no_trunc; 22 | // bool chown_restricted; 23 | // bool case_insensitive; 24 | // bool case_preserving; 25 | // }; 26 | // 27 | // struct PATHCONF3resfail { 28 | // post_op_attr obj_attributes; 29 | // }; 30 | // 31 | // union PATHCONF3res switch (nfsstat3 status) { 32 | // case NFS3_OK: 33 | // PATHCONF3resok resok; 34 | // default: 35 | // PATHCONF3resfail resfail; 36 | // }; 37 | // 38 | // DESCRIPTION 39 | // 40 | // Procedure PATHCONF retrieves the pathconf information for 41 | // a file or directory. If the FSF_HOMOGENEOUS bit is set in 42 | // FSFINFO3resok.properties, the pathconf information will be 43 | // the same for all files and directories in the exported 44 | // file system in which this file or directory resides. On 45 | // entry, the arguments in PATHCONF3args are: 46 | // 47 | // object 48 | // The file handle for the file system object. 49 | // 50 | // On successful return, PATHCONF3res.status is NFS3_OK and 51 | // PATHCONF3res.resok contains: 52 | // 53 | // obj_attributes 54 | // The attributes of the object specified by object. 55 | // 56 | // linkmax 57 | // The maximum number of hard links to an object. 58 | // 59 | // name_max 60 | // The maximum length of a component of a filename. 61 | // 62 | // no_trunc 63 | // If TRUE, the server will reject any request that 64 | // includes a name longer than name_max with the error, 65 | // NFS3ERR_NAMETOOLONG. If FALSE, any length name over 66 | // name_max bytes will be silently truncated to name_max 67 | // bytes. 68 | // 69 | // chown_restricted 70 | // If TRUE, the server will reject any request to change 71 | // either the owner or the group associated with a file if 72 | // the caller is not the privileged user. (Uid 0.) 73 | // 74 | // case_insensitive 75 | // If TRUE, the server file system does not distinguish 76 | // case when interpreting filenames. 77 | // 78 | // case_preserving 79 | // If TRUE, the server file system will preserve the case 80 | // of a name during a CREATE, MKDIR, MKNOD, SYMLINK, 81 | // RENAME, or LINK operation. 82 | // 83 | // Otherwise, PATHCONF3res.status contains the error on 84 | // failure and PATHCONF3res.resfail contains the following: 85 | // obj_attributes 86 | // The attributes of the object specified by object. 87 | // 88 | // IMPLEMENTATION 89 | // 90 | // In some implementations of the NFS version 2 protocol, 91 | // pathconf information was obtained at mount time through 92 | // the MOUNT protocol. The proper place to obtain it, is as 93 | // here, in the NFS version 3 protocol itself. 94 | // 95 | // ERRORS 96 | // 97 | // NFS3ERR_STALE 98 | // NFS3ERR_BADHANDLE 99 | // NFS3ERR_SERVERFAULT 100 | // 101 | // SEE ALSO 102 | // 103 | // LOOKUP, CREATE, MKDIR, SYMLINK, MKNOD, RENAME, LINK and FSINFO. 104 | 105 | var util = require('util'); 106 | 107 | var assert = require('assert-plus'); 108 | var rpc = require('oncrpc'); 109 | 110 | var NfsCall = require('./nfs_call').NfsCall; 111 | 112 | 113 | 114 | ///--- Globals 115 | 116 | var XDR = rpc.XDR; 117 | 118 | 119 | 120 | ///--- API 121 | 122 | function PathConfCall(opts) { 123 | NfsCall.call(this, opts, true); 124 | 125 | this._object = ''; 126 | 127 | this._nfs_path_conf_call = true; // MDB 128 | } 129 | util.inherits(PathConfCall, NfsCall); 130 | Object.defineProperty(PathConfCall.prototype, 'object', { 131 | get: function object() { 132 | return (this._object); 133 | } 134 | }); 135 | 136 | 137 | PathConfCall.prototype._transform = function _transform(chunk, enc, cb) { 138 | if (this.incoming) { 139 | var xdr = new XDR(chunk); 140 | this._object = xdr.readString(); 141 | } else { 142 | this.push(chunk); 143 | } 144 | 145 | cb(); 146 | }; 147 | 148 | 149 | PathConfCall.prototype.writeHead = function writeHead() { 150 | var xdr = this._serialize(XDR.byteLength(this._object)); 151 | xdr.writeString(this._object); 152 | 153 | this.write(xdr.buffer()); 154 | }; 155 | 156 | 157 | PathConfCall.prototype.toString = function toString() { 158 | var fmt = '[object PathConfCall ]'; 159 | return (util.format(fmt, this.xid, this._object)); 160 | }; 161 | 162 | 163 | 164 | ///--- Exports 165 | 166 | module.exports = { 167 | PathConfCall: PathConfCall 168 | }; 169 | -------------------------------------------------------------------------------- /lib/nfs/path_conf_reply.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 | // 3.3.20 Procedure 20: PATHCONF - Retrieve POSIX information 8 | // 9 | // SYNOPSIS 10 | // 11 | // PATHCONF3res NFSPROC3_PATHCONF(PATHCONF3args) = 20; 12 | // 13 | // struct PATHCONF3args { 14 | // nfs_fh3 object; 15 | // }; 16 | // 17 | // struct PATHCONF3resok { 18 | // post_op_attr obj_attributes; 19 | // uint32 linkmax; 20 | // uint32 name_max; 21 | // bool no_trunc; 22 | // bool chown_restricted; 23 | // bool case_insensitive; 24 | // bool case_preserving; 25 | // }; 26 | // 27 | // struct PATHCONF3resfail { 28 | // post_op_attr obj_attributes; 29 | // }; 30 | // 31 | // union PATHCONF3res switch (nfsstat3 status) { 32 | // case NFS3_OK: 33 | // PATHCONF3resok resok; 34 | // default: 35 | // PATHCONF3resfail resfail; 36 | // }; 37 | // 38 | // DESCRIPTION 39 | // 40 | // Procedure PATHCONF retrieves the pathconf information for 41 | // a file or directory. If the FSF_HOMOGENEOUS bit is set in 42 | // FSFINFO3resok.properties, the pathconf information will be 43 | // the same for all files and directories in the exported 44 | // file system in which this file or directory resides. On 45 | // entry, the arguments in PATHCONF3args are: 46 | // 47 | // object 48 | // The file handle for the file system object. 49 | // 50 | // On successful return, PATHCONF3res.status is NFS3_OK and 51 | // PATHCONF3res.resok contains: 52 | // 53 | // obj_attributes 54 | // The attributes of the object specified by object. 55 | // 56 | // linkmax 57 | // The maximum number of hard links to an object. 58 | // 59 | // name_max 60 | // The maximum length of a component of a filename. 61 | // 62 | // no_trunc 63 | // If TRUE, the server will reject any request that 64 | // includes a name longer than name_max with the error, 65 | // NFS3ERR_NAMETOOLONG. If FALSE, any length name over 66 | // name_max bytes will be silently truncated to name_max 67 | // bytes. 68 | // 69 | // chown_restricted 70 | // If TRUE, the server will reject any request to change 71 | // either the owner or the group associated with a file if 72 | // the caller is not the privileged user. (Uid 0.) 73 | // 74 | // case_insensitive 75 | // If TRUE, the server file system does not distinguish 76 | // case when interpreting filenames. 77 | // 78 | // case_preserving 79 | // If TRUE, the server file system will preserve the case 80 | // of a name during a CREATE, MKDIR, MKNOD, SYMLINK, 81 | // RENAME, or LINK operation. 82 | // 83 | // Otherwise, PATHCONF3res.status contains the error on 84 | // failure and PATHCONF3res.resfail contains the following: 85 | // obj_attributes 86 | // The attributes of the object specified by object. 87 | // 88 | // IMPLEMENTATION 89 | // 90 | // In some implementations of the NFS version 2 protocol, 91 | // pathconf information was obtained at mount time through 92 | // the MOUNT protocol. The proper place to obtain it, is as 93 | // here, in the NFS version 3 protocol itself. 94 | // 95 | // ERRORS 96 | // 97 | // NFS3ERR_STALE 98 | // NFS3ERR_BADHANDLE 99 | // NFS3ERR_SERVERFAULT 100 | // 101 | // SEE ALSO 102 | // 103 | // LOOKUP, CREATE, MKDIR, SYMLINK, MKNOD, RENAME, LINK and FSINFO. 104 | 105 | var fs = require('fs'); 106 | var util = require('util'); 107 | 108 | var assert = require('assert-plus'); 109 | var clone = require('clone'); 110 | var rpc = require('oncrpc'); 111 | 112 | var nfs_err = require('./errors'); 113 | var fattr3 = require('./fattr3'); 114 | var NfsReply = require('./nfs_reply').NfsReply; 115 | 116 | 117 | 118 | ///--- Globals 119 | 120 | var XDR = rpc.XDR; 121 | 122 | 123 | 124 | ///--- API 125 | 126 | function PathConfReply(opts) { 127 | NfsReply.call(this, opts); 128 | 129 | this.status = 0; 130 | this.obj_attributes = null; 131 | this.linkmax = 0; 132 | this.name_max = 0; 133 | this.no_trunc = true; 134 | this.chown_restricted = true; 135 | this.case_insensitive = false; 136 | this.case_preserving = true; 137 | 138 | this._nfs_path_conf_reply = true; // MDB 139 | } 140 | util.inherits(PathConfReply, NfsReply); 141 | PathConfReply.prototype._allowed_error_codes = [ 142 | nfs_err.NFS3ERR_STALE, 143 | nfs_err.NFS3ERR_BADHANDLE, 144 | nfs_err.NFS3ERR_SERVERFAULT 145 | ]; 146 | 147 | 148 | PathConfReply.prototype.setAttributes = function setAttributes(stats) { 149 | assert.ok(stats instanceof fs.Stats, 'fs.Stats'); 150 | 151 | this.obj_attributes = fattr3.create(stats); 152 | 153 | return (this.obj_attributes); 154 | }; 155 | 156 | 157 | PathConfReply.prototype._transform = function _transform(chunk, enc, cb) { 158 | if (this.incoming) { 159 | var xdr = new XDR(chunk); 160 | 161 | this.status = xdr.readInt(); 162 | if (this.status === 0) { 163 | if (xdr.readBool()) 164 | this.obj_attributes = fattr3.parse(xdr); 165 | 166 | this.linkmax = xdr.readInt(); 167 | this.name_max = xdr.readInt(); 168 | this.no_trunc = xdr.readBool(); 169 | this.chown_restricted = xdr.readBool(); 170 | this.case_insensitive = xdr.readBool(); 171 | this.case_preserving = xdr.readBool(); 172 | } 173 | } else { 174 | this.push(chunk); 175 | } 176 | 177 | cb(); 178 | }; 179 | 180 | 181 | PathConfReply.prototype.writeHead = function writeHead() { 182 | var len = 8; 183 | if (this.status === 0) 184 | len += fattr3.XDR_SIZE + 24; 185 | 186 | var xdr = this._serialize(len); 187 | 188 | xdr.writeInt(this.status); 189 | 190 | if (this.obj_attributes) { 191 | xdr.writeBool(true); 192 | fattr3.serialize(xdr, this.obj_attributes); 193 | } else { 194 | xdr.writeBool(false); 195 | } 196 | 197 | if (this.status === 0) { 198 | xdr.writeInt(this.linkmax); 199 | xdr.writeInt(this.name_max); 200 | xdr.writeBool(this.no_trunc); 201 | xdr.writeBool(this.chown_restricted); 202 | xdr.writeBool(this.case_insensitive); 203 | xdr.writeBool(this.case_preserving); 204 | } 205 | 206 | this.write(xdr.buffer()); 207 | }; 208 | 209 | 210 | PathConfReply.prototype.toString = function toString() { 211 | var fmt = '[object PathConfReply ]'; 214 | return (util.format(fmt, 215 | this.xid, 216 | this.status, 217 | this.obj_attributes, 218 | this.linkmax, 219 | this.name_max, 220 | this.no_trunc, 221 | this.chown_restricted, 222 | this.case_insensitive, 223 | this.case_preserving)); 224 | }; 225 | 226 | 227 | 228 | ///--- Exports 229 | 230 | module.exports = { 231 | PathConfReply: PathConfReply 232 | }; 233 | -------------------------------------------------------------------------------- /lib/nfs/read_call.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 | // 3.3.6 Procedure 6: READ - Read From file 8 | // 9 | // SYNOPSIS 10 | // 11 | // READ3res NFSPROC3_READ(READ3args) = 6; 12 | // 13 | // struct READ3args { 14 | // nfs_fh3 file; 15 | // offset3 offset; 16 | // count3 count; 17 | // }; 18 | // 19 | // struct READ3resok { 20 | // post_op_attr file_attributes; 21 | // count3 count; 22 | // bool eof; 23 | // opaque data<>; 24 | // }; 25 | // 26 | // struct READ3resfail { 27 | // post_op_attr file_attributes; 28 | // }; 29 | // 30 | // union READ3res switch (nfsstat3 status) { 31 | // case NFS3_OK: 32 | // READ3resok resok; 33 | // default: 34 | // READ3resfail resfail; 35 | // }; 36 | // 37 | // DESCRIPTION 38 | // 39 | // Procedure READ reads data from a file. On entry, the 40 | // arguments in READ3args are: 41 | // 42 | // file 43 | // The file handle of the file from which data is to be 44 | // read. This must identify a file system object of type, 45 | // NF3REG. 46 | // 47 | // offset 48 | // The position within the file at which the read is to 49 | // begin. An offset of 0 means to read data starting at 50 | // the beginning of the file. If offset is greater than or 51 | // equal to the size of the file, the status, NFS3_OK, is 52 | // returned with count set to 0 and eof set to TRUE, 53 | // subject to access permissions checking. 54 | // 55 | // count 56 | // The number of bytes of data that are to be read. If 57 | // count is 0, the READ will succeed and return 0 bytes of 58 | // data, subject to access permissions checking. count 59 | // must be less than or equal to the value of the rtmax 60 | // field in the FSINFO reply structure for the file system 61 | // that contains file. If greater, the server may return 62 | // only rtmax bytes, resulting in a short read. 63 | // 64 | // On successful return, READ3res.status is NFS3_OK and 65 | // READ3res.resok contains: 66 | // 67 | // file_attributes 68 | // The attributes of the file on completion of the read. 69 | // 70 | // count 71 | // The number of bytes of data returned by the read. 72 | // 73 | // eof 74 | // If the read ended at the end-of-file (formally, in a 75 | // correctly formed READ request, if READ3args.offset plus 76 | // READ3resok.count is equal to the size of the file), eof 77 | // is returned as TRUE; otherwise it is FALSE. A 78 | // successful READ of an empty file will always return eof 79 | // as TRUE. 80 | // 81 | // data 82 | // The counted data read from the file. 83 | // 84 | // Otherwise, READ3res.status contains the error on failure 85 | // and READ3res.resfail contains the following: 86 | // 87 | // file_attributes 88 | // The post-operation attributes of the file. 89 | // 90 | // IMPLEMENTATION 91 | // 92 | // The nfsdata type used for the READ and WRITE operations in 93 | // the NFS version 2 protocol defining the data portion of a 94 | // request or reply has been changed to a variable-length 95 | // opaque byte array. The maximum size allowed by the 96 | // protocol is now limited by what XDR and underlying 97 | // transports will allow. There are no artificial limits 98 | // imposed by the NFS version 3 protocol. Consult the FSINFO 99 | // procedure description for details. 100 | // 101 | // It is possible for the server to return fewer than count 102 | // bytes of data. If the server returns less than the count 103 | // requested and eof set to FALSE, the client should issue 104 | // another READ to get the remaining data. A server may 105 | // return less data than requested under several 106 | // circumstances. The file may have been truncated by another 107 | // client or perhaps on the server itself, changing the file 108 | // size from what the requesting client believes to be the 109 | // case. This would reduce the actual amount of data 110 | // available to the client. It is possible that the server 111 | // may back off the transfer size and reduce the read request 112 | // return. Server resource exhaustion may also occur 113 | // necessitating a smaller read return. 114 | // 115 | // Some NFS version 2 protocol client implementations chose 116 | // to interpret a short read response as indicating EOF. The 117 | // addition of the eof flag in the NFS version 3 protocol 118 | // provides a correct way of handling EOF. 119 | // 120 | // Some NFS version 2 protocol server implementations 121 | // incorrectly returned NFSERR_ISDIR if the file system 122 | // object type was not a regular file. The correct return 123 | // value for the NFS version 3 protocol is NFS3ERR_INVAL. 124 | // 125 | // ERRORS 126 | // 127 | // NFS3ERR_IO 128 | // NFS3ERR_NXIO 129 | // NFS3ERR_ACCES 130 | // NFS3ERR_INVAL 131 | // NFS3ERR_STALE 132 | // NFS3ERR_BADHANDLE 133 | // NFS3ERR_SERVERFAULT 134 | // 135 | // SEE ALSO 136 | // 137 | // READLINK. 138 | 139 | var util = require('util'); 140 | 141 | var assert = require('assert-plus'); 142 | var rpc = require('oncrpc'); 143 | 144 | var NfsCall = require('./nfs_call').NfsCall; 145 | 146 | 147 | 148 | ///--- Globals 149 | 150 | var XDR = rpc.XDR; 151 | 152 | 153 | 154 | ///--- API 155 | 156 | function ReadCall(opts) { 157 | assert.object(opts, 'options'); 158 | assert.optionalString(opts.file, 'options.file'); 159 | assert.optionalNumber(opts.offset, 'options.offset'); 160 | assert.optionalNumber(opts.count, 'options.count'); 161 | 162 | NfsCall.call(this, opts, true); 163 | 164 | this.file = opts.file || ''; 165 | this.offset = opts.offset || 0; 166 | this.count = opts.count || 0; 167 | 168 | this._nfs_read_call = true; // MDB 169 | } 170 | util.inherits(ReadCall, NfsCall); 171 | Object.defineProperty(ReadCall.prototype, 'object', { 172 | get: function object() { 173 | return (this.file); 174 | } 175 | }); 176 | 177 | 178 | ReadCall.prototype._transform = function _transform(chunk, enc, cb) { 179 | if (this.incoming) { 180 | var xdr = new XDR(chunk); 181 | this.file = xdr.readString(); 182 | this.offset = xdr.readHyper(); 183 | this.count = xdr.readInt(); 184 | } else { 185 | this.push(chunk); 186 | } 187 | 188 | cb(); 189 | }; 190 | 191 | 192 | ReadCall.prototype.writeHead = function writeHead() { 193 | var xdr = this._serialize(12 + XDR.byteLength(this.file)); 194 | xdr.writeString(this.file); 195 | xdr.writeHyper(this.offset); 196 | xdr.writeInt(this.count); 197 | 198 | this.write(xdr.buffer()); 199 | }; 200 | 201 | 202 | ReadCall.prototype.toString = function toString() { 203 | var fmt = '[object ReadCall ]'; 204 | return (util.format(fmt, this.xid, this.file, this.offset, this.count)); 205 | }; 206 | 207 | 208 | 209 | ///--- Exports 210 | 211 | module.exports = { 212 | ReadCall: ReadCall 213 | }; 214 | -------------------------------------------------------------------------------- /lib/nfs/readlink_call.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 | // 3.3.5 Procedure 5: READLINK - Read from symbolic link 8 | // 9 | // SYNOPSIS 10 | // 11 | // READLINK3res NFSPROC3_READLINK(READLINK3args) = 5; 12 | // 13 | // struct READLINK3args { 14 | // nfs_fh3 symlink; 15 | // }; 16 | // 17 | // struct READLINK3resok { 18 | // post_op_attr symlink_attributes; 19 | // nfspath3 data; 20 | // }; 21 | // 22 | // struct READLINK3resfail { 23 | // post_op_attr symlink_attributes; 24 | // }; 25 | // 26 | // union READLINK3res switch (nfsstat3 status) { 27 | // case NFS3_OK: 28 | // READLINK3resok resok; 29 | // default: 30 | // READLINK3resfail resfail; 31 | // }; 32 | // 33 | // DESCRIPTION 34 | // 35 | // Procedure READLINK reads the data associated with a 36 | // symbolic link. The data is an ASCII string that is opaque 37 | // to the server. That is, whether created by the NFS 38 | // version 3 protocol software from a client or created 39 | // locally on the server, the data in a symbolic link is not 40 | // interpreted when created, but is simply stored. On entry, 41 | // the arguments in READLINK3args are: 42 | // 43 | // symlink 44 | // The file handle for a symbolic link (file system object 45 | // of type NF3LNK). 46 | // 47 | // On successful return, READLINK3res.status is NFS3_OK and 48 | // READLINK3res.resok contains: 49 | // 50 | // data 51 | // The data associated with the symbolic link. 52 | // 53 | // symlink_attributes 54 | // The post-operation attributes for the symbolic link. 55 | // 56 | // Otherwise, READLINK3res.status contains the error on 57 | // failure and READLINK3res.resfail contains the following: 58 | // 59 | // symlink_attributes 60 | // The post-operation attributes for the symbolic link. 61 | // 62 | // IMPLEMENTATION 63 | // 64 | // A symbolic link is nominally a pointer to another file. 65 | // The data is not necessarily interpreted by the server, 66 | // just stored in the file. It is possible for a client 67 | // implementation to store a path name that is not meaningful 68 | // to the server operating system in a symbolic link. A 69 | // READLINK operation returns the data to the client for 70 | // interpretation. If different implementations want to share 71 | // access to symbolic links, then they must agree on the 72 | // interpretation of the data in the symbolic link. 73 | // 74 | // The READLINK operation is only allowed on objects of type, 75 | // NF3LNK. The server should return the error, 76 | // NFS3ERR_INVAL, if the object is not of type, NF3LNK. 77 | // (Note: The X/Open XNFS Specification for the NFS version 2 78 | // protocol defined the error status in this case as 79 | // NFSERR_NXIO. This is inconsistent with existing server 80 | // practice.) 81 | // 82 | // ERRORS 83 | // 84 | // NFS3ERR_IO 85 | // NFS3ERR_INVAL 86 | // NFS3ERR_ACCES 87 | // NFS3ERR_STALE 88 | // NFS3ERR_BADHANDLE 89 | // NFS3ERR_NOTSUPP 90 | // NFS3ERR_SERVERFAULT 91 | // 92 | // SEE ALSO 93 | // 94 | // READLINK, SYMLINK. 95 | 96 | var util = require('util'); 97 | 98 | var assert = require('assert-plus'); 99 | var rpc = require('oncrpc'); 100 | 101 | var NfsCall = require('./nfs_call').NfsCall; 102 | 103 | 104 | 105 | ///--- Globals 106 | 107 | var XDR = rpc.XDR; 108 | 109 | 110 | 111 | ///--- API 112 | 113 | function ReadlinkCall(opts) { 114 | NfsCall.call(this, opts, true); 115 | 116 | this.symlink = opts.symlink || ''; 117 | 118 | this._nfs_readlink_call = true; // MDB 119 | } 120 | util.inherits(ReadlinkCall, NfsCall); 121 | Object.defineProperty(ReadlinkCall.prototype, 'object', { 122 | get: function object() { 123 | return (this.symlink); 124 | } 125 | }); 126 | 127 | 128 | ReadlinkCall.prototype._transform = function _transform(chunk, enc, cb) { 129 | if (this.incoming) { 130 | var xdr = new XDR(chunk); 131 | this.symlink = xdr.readString(); 132 | } else { 133 | this.push(chunk); 134 | } 135 | 136 | cb(); 137 | }; 138 | 139 | 140 | ReadlinkCall.prototype.writeHead = function writeHead() { 141 | var xdr = this._serialize(XDR.byteLength(this.symlink)); 142 | xdr.writeString(this.symlink); 143 | 144 | this.write(xdr.buffer()); 145 | }; 146 | 147 | 148 | ReadlinkCall.prototype.toString = function toString() { 149 | var fmt = '[object ReadlinkCall ]'; 150 | return (util.format(fmt, this.xid, this.symlink)); 151 | }; 152 | 153 | 154 | 155 | ///--- Exports 156 | 157 | module.exports = { 158 | ReadlinkCall: ReadlinkCall 159 | }; 160 | -------------------------------------------------------------------------------- /lib/nfs/readlink_reply.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 | // 3.3.5 Procedure 5: READLINK - Read from symbolic link 8 | // 9 | // SYNOPSIS 10 | // 11 | // READLINK3res NFSPROC3_READLINK(READLINK3args) = 5; 12 | // 13 | // struct READLINK3args { 14 | // nfs_fh3 symlink; 15 | // }; 16 | // 17 | // struct READLINK3resok { 18 | // post_op_attr symlink_attributes; 19 | // nfspath3 data; 20 | // }; 21 | // 22 | // struct READLINK3resfail { 23 | // post_op_attr symlink_attributes; 24 | // }; 25 | // 26 | // union READLINK3res switch (nfsstat3 status) { 27 | // case NFS3_OK: 28 | // READLINK3resok resok; 29 | // default: 30 | // READLINK3resfail resfail; 31 | // }; 32 | // 33 | // DESCRIPTION 34 | // 35 | // Procedure READLINK reads the data associated with a 36 | // symbolic link. The data is an ASCII string that is opaque 37 | // to the server. That is, whether created by the NFS 38 | // version 3 protocol software from a client or created 39 | // locally on the server, the data in a symbolic link is not 40 | // interpreted when created, but is simply stored. On entry, 41 | // the arguments in READLINK3args are: 42 | // 43 | // symlink 44 | // The file handle for a symbolic link (file system object 45 | // of type NF3LNK). 46 | // 47 | // On successful return, READLINK3res.status is NFS3_OK and 48 | // READLINK3res.resok contains: 49 | // 50 | // data 51 | // The data associated with the symbolic link. 52 | // 53 | // symlink_attributes 54 | // The post-operation attributes for the symbolic link. 55 | // 56 | // Otherwise, READLINK3res.status contains the error on 57 | // failure and READLINK3res.resfail contains the following: 58 | // 59 | // symlink_attributes 60 | // The post-operation attributes for the symbolic link. 61 | // 62 | // IMPLEMENTATION 63 | // 64 | // A symbolic link is nominally a pointer to another file. 65 | // The data is not necessarily interpreted by the server, 66 | // just stored in the file. It is possible for a client 67 | // implementation to store a path name that is not meaningful 68 | // to the server operating system in a symbolic link. A 69 | // READLINK operation returns the data to the client for 70 | // interpretation. If different implementations want to share 71 | // access to symbolic links, then they must agree on the 72 | // interpretation of the data in the symbolic link. 73 | // 74 | // The READLINK operation is only allowed on objects of type, 75 | // NF3LNK. The server should return the error, 76 | // NFS3ERR_INVAL, if the object is not of type, NF3LNK. 77 | // (Note: The X/Open XNFS Specification for the NFS version 2 78 | // protocol defined the error status in this case as 79 | // NFSERR_NXIO. This is inconsistent with existing server 80 | // practice.) 81 | // 82 | // ERRORS 83 | // 84 | // NFS3ERR_IO 85 | // NFS3ERR_INVAL 86 | // NFS3ERR_ACCES 87 | // NFS3ERR_STALE 88 | // NFS3ERR_BADHANDLE 89 | // NFS3ERR_NOTSUPP 90 | // NFS3ERR_SERVERFAULT 91 | // 92 | // SEE ALSO 93 | // 94 | // READLINK, SYMLINK. 95 | 96 | var fs = require('fs'); 97 | var path = require('path'); 98 | var util = require('util'); 99 | 100 | var assert = require('assert-plus'); 101 | var clone = require('clone'); 102 | var rpc = require('oncrpc'); 103 | 104 | var nfs_err = require('./errors'); 105 | var fattr3 = require('./fattr3'); 106 | var NfsReply = require('./nfs_reply').NfsReply; 107 | 108 | 109 | 110 | ///--- Globals 111 | 112 | var XDR = rpc.XDR; 113 | 114 | 115 | 116 | ///--- API 117 | 118 | function ReadlinkReply(opts) { 119 | NfsReply.call(this, opts); 120 | 121 | this.status = 0; 122 | this.obj_attributes = opts.obj_attributes | {}; 123 | 124 | this._nfs_readlink_reply = true; // MDB 125 | } 126 | util.inherits(ReadlinkReply, NfsReply); 127 | ReadlinkReply.prototype._allowed_error_codes = [ 128 | nfs_err.NFS3ERR_IO, 129 | nfs_err.NFS3ERR_INVAL, 130 | nfs_err.NFS3ERR_ACCES, 131 | nfs_err.NFS3ERR_STALE, 132 | nfs_err.NFS3ERR_BADHANDLE, 133 | nfs_err.NFS3ERR_NOTSUPP, 134 | nfs_err.NFS3ERR_SERVERFAULT 135 | ]; 136 | 137 | 138 | ReadlinkReply.prototype.setAttributes = function setAttributes(stats) { 139 | assert.ok(stats instanceof fs.Stats, 'fs.Stats'); 140 | 141 | this.symlink_attributes = fattr3.create(stats); 142 | 143 | return (this.obj_attributes); 144 | }; 145 | 146 | 147 | ReadlinkReply.prototype._transform = function _transform(chunk, enc, cb) { 148 | if (this.incoming) { 149 | var xdr = new XDR(chunk); 150 | 151 | this.status = xdr.readInt(); 152 | if (xdr.readBool()) 153 | this.symlink_attributes = fattr3.parse(xdr); 154 | if (this.status === 0) 155 | this.data = xdr.readString(); 156 | } else { 157 | this.push(chunk); 158 | } 159 | 160 | cb(); 161 | }; 162 | 163 | 164 | ReadlinkReply.prototype.writeHead = function writeHead() { 165 | var len = 4; 166 | 167 | if (this.symlink_attributes) 168 | len += 4 + fattr3.XDR_SIZE; 169 | 170 | if (this.status === 0) 171 | len += XDR.byteLength(this.data); 172 | 173 | var xdr = this._serialize(len); 174 | 175 | xdr.writeInt(this.status); 176 | if (this.symlink_attributes) { 177 | xdr.writeBool(true); 178 | fattr3.serialize(xdr, this.symlink_attributes); 179 | } else { 180 | xdr.writeBool(false); 181 | } 182 | 183 | if (this.status === 0) 184 | xdr.writeString(this.data); 185 | 186 | this.write(xdr.buffer()); 187 | }; 188 | 189 | 190 | ReadlinkReply.prototype.toString = function toString() { 191 | var fmt = '[object ReadlinkReply ]'; 193 | return (util.format(fmt, this.xid, this.status, this.symlink_attributes, 194 | this.data)); 195 | }; 196 | 197 | 198 | 199 | ///--- Exports 200 | 201 | module.exports = { 202 | ReadlinkReply: ReadlinkReply 203 | }; 204 | -------------------------------------------------------------------------------- /lib/nfs/remove_call.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 | // 3.3.12 Procedure 12: REMOVE - Remove a File 8 | // 9 | // SYNOPSIS 10 | // 11 | // REMOVE3res NFSPROC3_REMOVE(REMOVE3args) = 12; 12 | // 13 | // struct REMOVE3args { 14 | // diropargs3 object; 15 | // }; 16 | // 17 | // struct REMOVE3resok { 18 | // wcc_data dir_wcc; 19 | // }; 20 | // 21 | // struct REMOVE3resfail { 22 | // wcc_data dir_wcc; 23 | // }; 24 | // 25 | // union REMOVE3res switch (nfsstat3 status) { 26 | // case NFS3_OK: 27 | // REMOVE3resok resok; 28 | // default: 29 | // REMOVE3resfail resfail; 30 | // }; 31 | // 32 | // DESCRIPTION 33 | // 34 | // Procedure REMOVE removes (deletes) an entry from a 35 | // directory. If the entry in the directory was the last 36 | // reference to the corresponding file system object, the 37 | // object may be destroyed. On entry, the arguments in 38 | // REMOVE3args are: 39 | // 40 | // object 41 | // A diropargs3 structure identifying the entry to be 42 | // removed: 43 | // 44 | // dir 45 | // The file handle for the directory from which the entry 46 | // is to be removed. 47 | // 48 | // name 49 | // The name of the entry to be removed. Refer to General 50 | // comments on filenames on page 30. 51 | // 52 | // On successful return, REMOVE3res.status is NFS3_OK and 53 | // REMOVE3res.resok contains: 54 | // 55 | // dir_wcc 56 | // Weak cache consistency data for the directory, 57 | // object.dir. For a client that requires only the 58 | // post-REMOVE directory attributes, these can be found in 59 | // dir_wcc.after. 60 | // 61 | // Otherwise, REMOVE3res.status contains the error on failure 62 | // and REMOVE3res.resfail contains the following: 63 | // 64 | // dir_wcc 65 | // Weak cache consistency data for the directory, 66 | // object.dir. For a client that requires only the 67 | // post-REMOVE directory attributes, these can be found in 68 | // dir_wcc.after. Even though the REMOVE failed, full 69 | // wcc_data is returned to allow the client to determine 70 | // whether the failing REMOVE changed the directory. 71 | // 72 | // IMPLEMENTATION 73 | // 74 | // In general, REMOVE is intended to remove non-directory 75 | // file objects and RMDIR is to be used to remove 76 | // directories. However, REMOVE can be used to remove 77 | // directories, subject to restrictions imposed by either the 78 | // client or server interfaces. This had been a source of 79 | // confusion in the NFS version 2 protocol. 80 | // 81 | // The concept of last reference is server specific. However, 82 | // if the nlink field in the previous attributes of the 83 | // object had the value 1, the client should not rely on 84 | // referring to the object via a file handle. Likewise, the 85 | // client should not rely on the resources (disk space, 86 | // directory entry, and so on.) formerly associated with the 87 | // object becoming immediately available. Thus, if a client 88 | // needs to be able to continue to access a file after using 89 | // REMOVE to remove it, the client should take steps to make 90 | // sure that the file will still be accessible. The usual 91 | // mechanism used is to use RENAME to rename the file from 92 | // its old name to a new hidden name. 93 | // 94 | // Refer to General comments on filenames on page 30. 95 | // 96 | // ERRORS 97 | // 98 | // NFS3ERR_NOENT 99 | // NFS3ERR_IO 100 | // NFS3ERR_ACCES 101 | // NFS3ERR_NOTDIR 102 | // NFS3ERR_NAMETOOLONG 103 | // NFS3ERR_ROFS 104 | // NFS3ERR_STALE 105 | // NFS3ERR_BADHANDLE 106 | // NFS3ERR_SERVERFAULT 107 | // 108 | // SEE ALSO 109 | // 110 | // RMDIR and RENAME. 111 | 112 | 113 | var util = require('util'); 114 | 115 | var assert = require('assert-plus'); 116 | var rpc = require('oncrpc'); 117 | 118 | var NfsCall = require('./nfs_call').NfsCall; 119 | 120 | 121 | 122 | ///--- Globals 123 | 124 | var XDR = rpc.XDR; 125 | 126 | 127 | 128 | ///--- API 129 | 130 | function RemoveCall(opts) { 131 | assert.object(opts, 'opts'); 132 | assert.optionalObject(opts.object, 'opts.object'); 133 | 134 | NfsCall.call(this, opts, true); 135 | 136 | this._object = opts.object || { 137 | dir: '', 138 | name: '' 139 | }; 140 | 141 | this._nfs_remove_call = true; // MDB 142 | } 143 | util.inherits(RemoveCall, NfsCall); 144 | Object.defineProperty(RemoveCall.prototype, 'object', { 145 | get: function object() { 146 | return (this._object.dir); 147 | } 148 | }); 149 | 150 | 151 | RemoveCall.prototype._transform = function _transform(chunk, enc, cb) { 152 | if (this.incoming) { 153 | var xdr = new XDR(chunk); 154 | this._object.dir = xdr.readString(); 155 | this._object.name = xdr.readString(); 156 | } else { 157 | this.push(chunk); 158 | } 159 | 160 | cb(); 161 | }; 162 | 163 | 164 | RemoveCall.prototype.writeHead = function writeHead() { 165 | var len = XDR.byteLength(this._object.dir) + 166 | XDR.byteLength(this._object.name); 167 | 168 | var xdr = this._serialize(len); 169 | 170 | xdr.writeString(this._object.dir); 171 | xdr.writeString(this._object.name); 172 | 173 | this.write(xdr.buffer()); 174 | }; 175 | 176 | 177 | RemoveCall.prototype.toString = function toString() { 178 | var fmt = '[object RemoveCall ]'; 179 | return (util.format(fmt, this.xid, this._object)); 180 | }; 181 | 182 | 183 | 184 | ///--- Exports 185 | 186 | module.exports = { 187 | RemoveCall: RemoveCall 188 | }; 189 | -------------------------------------------------------------------------------- /lib/nfs/remove_reply.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 | // 3.3.12 Procedure 12: REMOVE - Remove a File 8 | // 9 | // SYNOPSIS 10 | // 11 | // REMOVE3res NFSPROC3_REMOVE(REMOVE3args) = 12; 12 | // 13 | // struct REMOVE3args { 14 | // diropargs3 object; 15 | // }; 16 | // 17 | // struct REMOVE3resok { 18 | // wcc_data dir_wcc; 19 | // }; 20 | // 21 | // struct REMOVE3resfail { 22 | // wcc_data dir_wcc; 23 | // }; 24 | // 25 | // union REMOVE3res switch (nfsstat3 status) { 26 | // case NFS3_OK: 27 | // REMOVE3resok resok; 28 | // default: 29 | // REMOVE3resfail resfail; 30 | // }; 31 | // 32 | // DESCRIPTION 33 | // 34 | // Procedure REMOVE removes (deletes) an entry from a 35 | // directory. If the entry in the directory was the last 36 | // reference to the corresponding file system object, the 37 | // object may be destroyed. On entry, the arguments in 38 | // REMOVE3args are: 39 | // 40 | // object 41 | // A diropargs3 structure identifying the entry to be 42 | // removed: 43 | // 44 | // dir 45 | // The file handle for the directory from which the entry 46 | // is to be removed. 47 | // 48 | // name 49 | // The name of the entry to be removed. Refer to General 50 | // comments on filenames on page 30. 51 | // 52 | // On successful return, REMOVE3res.status is NFS3_OK and 53 | // REMOVE3res.resok contains: 54 | // 55 | // dir_wcc 56 | // Weak cache consistency data for the directory, 57 | // object.dir. For a client that requires only the 58 | // post-REMOVE directory attributes, these can be found in 59 | // dir_wcc.after. 60 | // 61 | // Otherwise, REMOVE3res.status contains the error on failure 62 | // and REMOVE3res.resfail contains the following: 63 | // 64 | // dir_wcc 65 | // Weak cache consistency data for the directory, 66 | // object.dir. For a client that requires only the 67 | // post-REMOVE directory attributes, these can be found in 68 | // dir_wcc.after. Even though the REMOVE failed, full 69 | // wcc_data is returned to allow the client to determine 70 | // whether the failing REMOVE changed the directory. 71 | // 72 | // IMPLEMENTATION 73 | // 74 | // In general, REMOVE is intended to remove non-directory 75 | // file objects and RMDIR is to be used to remove 76 | // directories. However, REMOVE can be used to remove 77 | // directories, subject to restrictions imposed by either the 78 | // client or server interfaces. This had been a source of 79 | // confusion in the NFS version 2 protocol. 80 | // 81 | // The concept of last reference is server specific. However, 82 | // if the nlink field in the previous attributes of the 83 | // object had the value 1, the client should not rely on 84 | // referring to the object via a file handle. Likewise, the 85 | // client should not rely on the resources (disk space, 86 | // directory entry, and so on.) formerly associated with the 87 | // object becoming immediately available. Thus, if a client 88 | // needs to be able to continue to access a file after using 89 | // REMOVE to remove it, the client should take steps to make 90 | // sure that the file will still be accessible. The usual 91 | // mechanism used is to use RENAME to rename the file from 92 | // its old name to a new hidden name. 93 | // 94 | // Refer to General comments on filenames on page 30. 95 | // 96 | // ERRORS 97 | // 98 | // NFS3ERR_NOENT 99 | // NFS3ERR_IO 100 | // NFS3ERR_ACCES 101 | // NFS3ERR_NOTDIR 102 | // NFS3ERR_NAMETOOLONG 103 | // NFS3ERR_ROFS 104 | // NFS3ERR_STALE 105 | // NFS3ERR_BADHANDLE 106 | // NFS3ERR_SERVERFAULT 107 | // 108 | // SEE ALSO 109 | // 110 | // RMDIR and RENAME. 111 | 112 | var fs = require('fs'); 113 | var path = require('path'); 114 | var util = require('util'); 115 | 116 | var assert = require('assert-plus'); 117 | var clone = require('clone'); 118 | var rpc = require('oncrpc'); 119 | 120 | var nfs_err = require('./errors'); 121 | var wcc_data = require('./wcc_data'); 122 | var NfsReply = require('./nfs_reply').NfsReply; 123 | 124 | 125 | 126 | ///--- Globals 127 | 128 | var XDR = rpc.XDR; 129 | 130 | 131 | 132 | ///--- API 133 | 134 | function RemoveReply(opts) { 135 | NfsReply.call(this, opts); 136 | 137 | this.status = 0; 138 | this.dir_wcc = opts.dir_wcc | null; 139 | 140 | this._nfs_remove_reply = true; // MDB 141 | } 142 | util.inherits(RemoveReply, NfsReply); 143 | RemoveReply.prototype._allowed_error_codes = [ 144 | nfs_err.NFS3ERR_NOENT, 145 | nfs_err.NFS3ERR_IO, 146 | nfs_err.NFS3ERR_ACCES, 147 | nfs_err.NFS3ERR_NOTDIR, 148 | nfs_err.NFS3ERR_NAMETOOLONG, 149 | nfs_err.NFS3ERR_ROFS, 150 | nfs_err.NFS3ERR_STALE, 151 | nfs_err.NFS3ERR_BADHANDLE, 152 | nfs_err.NFS3ERR_SERVERFAULT 153 | ]; 154 | 155 | 156 | RemoveReply.prototype.set_dir_wcc = function set_dir_wcc() { 157 | this.dir_wcc = wcc_data.create(); 158 | return (this.dir_wcc); 159 | }; 160 | 161 | 162 | RemoveReply.prototype._transform = function _transform(chunk, enc, cb) { 163 | if (this.incoming) { 164 | var xdr = new XDR(chunk); 165 | 166 | this.status = xdr.readInt(); 167 | this.dir_wcc = wcc_data.parse(xdr); 168 | } else { 169 | this.push(chunk); 170 | } 171 | 172 | cb(); 173 | }; 174 | 175 | 176 | RemoveReply.prototype.writeHead = function writeHead() { 177 | var len = 4; 178 | 179 | len += wcc_data.length(this.dir_wcc); 180 | 181 | var xdr = this._serialize(len); 182 | 183 | xdr.writeInt(this.status); 184 | 185 | wcc_data.serialize(xdr, this.dir_wcc); 186 | 187 | this.write(xdr.buffer()); 188 | }; 189 | 190 | 191 | RemoveReply.prototype.toString = function toString() { 192 | var fmt = '[object RemoveReply ]'; 193 | return (util.format(fmt, this.xid, this.status, this.dir_wcc)); 194 | }; 195 | 196 | 197 | 198 | ///--- Exports 199 | 200 | module.exports = { 201 | RemoveReply: RemoveReply 202 | }; 203 | -------------------------------------------------------------------------------- /lib/nfs/rename_call.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 | // 3.3.14 Procedure 14: RENAME - Rename a File or Directory 8 | // 9 | // SYNOPSIS 10 | // 11 | // RENAME3res NFSPROC3_RENAME(RENAME3args) = 14; 12 | // 13 | // struct RENAME3args { 14 | // diropargs3 from; 15 | // diropargs3 to; 16 | // }; 17 | // 18 | // struct RENAME3resok { 19 | // wcc_data fromdir_wcc; 20 | // wcc_data todir_wcc; 21 | // }; 22 | // 23 | // struct RENAME3resfail { 24 | // wcc_data fromdir_wcc; 25 | // wcc_data todir_wcc; 26 | // }; 27 | // 28 | // union RENAME3res switch (nfsstat3 status) { 29 | // case NFS3_OK: 30 | // RENAME3resok resok; 31 | // default: 32 | // RENAME3resfail resfail; 33 | // }; 34 | // 35 | // DESCRIPTION 36 | // 37 | // Procedure RENAME renames the file identified by from.name 38 | // in the directory, from.dir, to to.name in the di- rectory, 39 | // to.dir. The operation is required to be atomic to the 40 | // client. To.dir and from.dir must reside on the same file 41 | // system and server. On entry, the arguments in RENAME3args 42 | // are: 43 | // 44 | // from 45 | // A diropargs3 structure identifying the source (the file 46 | // system object to be re-named): 47 | // 48 | // from.dir 49 | // The file handle for the directory from which the 50 | // entry is to be renamed. 51 | // 52 | // from.name 53 | // The name of the entry that identifies the object to 54 | // be renamed. Refer to General comments on filenames 55 | // on page 30. 56 | // 57 | // to 58 | // A diropargs3 structure identifying the target (the new 59 | // name of the object): 60 | // 61 | // to.dir 62 | // The file handle for the directory to which the 63 | // object is to be renamed. 64 | // 65 | // to.name 66 | // The new name for the object. Refer to General 67 | // comments on filenames on page 30. 68 | // 69 | // If the directory, to.dir, already contains an entry with 70 | // the name, to.name, the source object must be compatible 71 | // with the target: either both are non-directories or both 72 | // are directories and the target must be empty. If 73 | // compatible, the existing target is removed before the 74 | // rename occurs. If they are not compatible or if the target 75 | // is a directory but not empty, the server should return the 76 | // error, NFS3ERR_EXIST. 77 | // 78 | // On successful return, RENAME3res.status is NFS3_OK and 79 | // RENAME3res.resok contains: 80 | // 81 | // fromdir_wcc 82 | // Weak cache consistency data for the directory, 83 | // from.dir. 84 | // 85 | // todir_wcc 86 | // Weak cache consistency data for the directory, to.dir. 87 | // 88 | // Otherwise, RENAME3res.status contains the error on failure 89 | // and RENAME3res.resfail contains the following: 90 | // 91 | // fromdir_wcc 92 | // Weak cache consistency data for the directory, 93 | // from.dir. 94 | // 95 | // todir_wcc 96 | // Weak cache consistency data for the directory, to.dir. 97 | // 98 | // IMPLEMENTATION 99 | // The RENAME operation must be atomic to the client. The 100 | // message "to.dir and from.dir must reside on the same file 101 | // system on the server, [or the operation will fail]" means 102 | // that the fsid fields in the attributes for the directories 103 | // are the same. If they reside on different file systems, 104 | // the error, NFS3ERR_XDEV, is returned. Even though the 105 | // operation is atomic, the status, NFS3ERR_MLINK, may be 106 | // returned if the server used a "unlink/link/unlink" 107 | // sequence internally. 108 | // 109 | // A file handle may or may not become stale on a rename. 110 | // However, server implementors are strongly encouraged to 111 | // attempt to keep file handles from becoming stale in this 112 | // fashion. 113 | // 114 | // On some servers, the filenames, "." and "..", are illegal 115 | // as either from.name or to.name. In addition, neither 116 | // from.name nor to.name can be an alias for from.dir. These 117 | // servers will return the error, NFS3ERR_INVAL, in these 118 | // cases. 119 | // 120 | // If from and to both refer to the same file (they might 121 | // be hard links of each other), then RENAME should perform 122 | // no action and return NFS3_OK. 123 | // 124 | // Refer to General comments on filenames on page 30. 125 | // 126 | // ERRORS 127 | // 128 | // NFS3ERR_NOENT 129 | // NFS3ERR_IO 130 | // NFS3ERR_ACCES 131 | // NFS3ERR_EXIST 132 | // NFS3ERR_XDEV 133 | // NFS3ERR_NOTDIR 134 | // NFS3ERR_ISDIR 135 | // NFS3ERR_INVAL 136 | // NFS3ERR_NOSPC 137 | // NFS3ERR_ROFS 138 | // NFS3ERR_MLINK 139 | // NFS3ERR_NAMETOOLONG 140 | // NFS3ERR_NOTEMPTY 141 | // NFS3ERR_DQUOT 142 | // NFS3ERR_STALE 143 | // NFS3ERR_BADHANDLE 144 | // NFS3ERR_NOTSUPP 145 | // NFS3ERR_SERVERFAULT 146 | // 147 | // SEE ALSO 148 | // 149 | // REMOVE and LINK. 150 | 151 | 152 | var util = require('util'); 153 | 154 | var assert = require('assert-plus'); 155 | var rpc = require('oncrpc'); 156 | 157 | var NfsCall = require('./nfs_call').NfsCall; 158 | 159 | 160 | 161 | ///--- Globals 162 | 163 | var XDR = rpc.XDR; 164 | 165 | 166 | 167 | ///--- API 168 | 169 | function RenameCall(opts) { 170 | assert.object(opts, 'opts'); 171 | assert.optionalObject(opts.from, 'opts.from'); 172 | assert.optionalObject(opts.to, 'opts.to'); 173 | 174 | NfsCall.call(this, opts, true); 175 | 176 | this.from = opts.from || { 177 | dir: '', 178 | name: '' 179 | }; 180 | 181 | this.to = opts.to || { 182 | dir: '', 183 | name: '' 184 | }; 185 | 186 | this._nfs_rename_call = true; // MDB 187 | } 188 | util.inherits(RenameCall, NfsCall); 189 | Object.defineProperty(RenameCall.prototype, 'object', { 190 | get: function object() { 191 | return (this.from.dir); 192 | } 193 | }); 194 | 195 | 196 | RenameCall.prototype._transform = function _transform(chunk, enc, cb) { 197 | if (this.incoming) { 198 | var xdr = new XDR(chunk); 199 | this.from.dir = xdr.readString(); 200 | this.from.name = xdr.readString(); 201 | this.to.dir = xdr.readString(); 202 | this.to.name = xdr.readString(); 203 | } else { 204 | this.push(chunk); 205 | } 206 | 207 | cb(); 208 | }; 209 | 210 | 211 | RenameCall.prototype.writeHead = function writeHead() { 212 | var len = XDR.byteLength(this.from.dir) + 213 | XDR.byteLength(this.from.name) + 214 | XDR.byteLength(this.to.dir) + 215 | XDR.byteLength(this.to.name); 216 | 217 | var xdr = this._serialize(len); 218 | 219 | xdr.writeString(this.from.dir); 220 | xdr.writeString(this.from.name); 221 | xdr.writeString(this.to.dir); 222 | xdr.writeString(this.to.name); 223 | 224 | this.write(xdr.buffer()); 225 | }; 226 | 227 | 228 | RenameCall.prototype.toString = function toString() { 229 | var fmt = '[object RenameCall ]'; 230 | return (util.format(fmt, this.xid, this.from, this.to)); 231 | }; 232 | 233 | 234 | 235 | ///--- Exports 236 | 237 | module.exports = { 238 | RenameCall: RenameCall 239 | }; 240 | -------------------------------------------------------------------------------- /lib/nfs/rmdir_call.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 | // 3.3.13 Procedure 13: RMDIR - Remove a Directory 8 | // 9 | // SYNOPSIS 10 | // 11 | // RMDIR3res NFSPROC3_RMDIR(RMDIR3args) = 13; 12 | // 13 | // struct RMDIR3args { 14 | // diropargs3 object; 15 | // }; 16 | // 17 | // struct RMDIR3resok { 18 | // wcc_data dir_wcc; 19 | // }; 20 | // 21 | // struct RMDIR3resfail { 22 | // wcc_data dir_wcc; 23 | // }; 24 | // 25 | // union RMDIR3res switch (nfsstat3 status) { 26 | // case NFS3_OK: 27 | // RMDIR3resok resok; 28 | // default: 29 | // RMDIR3resfail resfail; 30 | // }; 31 | // 32 | // DESCRIPTION 33 | // 34 | // Procedure RMDIR removes (deletes) a subdirectory from a 35 | // directory. If the directory entry of the subdirectory is 36 | // the last reference to the subdirectory, the subdirectory 37 | // may be destroyed. On entry, the arguments in RMDIR3args 38 | // are: 39 | // 40 | // object 41 | // A diropargs3 structure identifying the directory entry 42 | // to be removed: 43 | // 44 | // dir 45 | // The file handle for the directory from which the 46 | // subdirectory is to be removed. 47 | // 48 | // name 49 | // The name of the subdirectory to be removed. Refer to 50 | // General comments on filenames on page 30. 51 | // 52 | // On successful return, RMDIR3res.status is NFS3_OK and 53 | // RMDIR3res.resok contains: 54 | // 55 | // dir_wcc 56 | // Weak cache consistency data for the directory, 57 | // object.dir. For a client that requires only the 58 | // post-RMDIR directory attributes, these can be found in 59 | // dir_wcc.after. 60 | // 61 | // Otherwise, RMDIR3res.status contains the error on failure 62 | // and RMDIR3res.resfail contains the following: 63 | // 64 | // dir_wcc 65 | // Weak cache consistency data for the directory, 66 | // object.dir. For a client that requires only the 67 | // post-RMDIR directory attributes, these can be found in 68 | // dir_wcc.after. Note that even though the RMDIR failed, 69 | // full wcc_data is returned to allow the client to 70 | // determine whether the failing RMDIR changed the 71 | // directory. 72 | // 73 | // IMPLEMENTATION 74 | // 75 | // Note that on some servers, removal of a non-empty 76 | // directory is disallowed. 77 | // 78 | // On some servers, the filename, ".", is illegal. These 79 | // servers will return the error, NFS3ERR_INVAL. On some 80 | // servers, the filename, "..", is illegal. These servers 81 | // will return the error, NFS3ERR_EXIST. This would seem 82 | // inconsistent, but allows these servers to comply with 83 | // their own specific interface definitions. Clients should 84 | // be prepared to handle both cases. 85 | // 86 | // The client should not rely on the resources (disk space, 87 | // directory entry, and so on.) formerly associated with the 88 | // directory becoming immediately available. 89 | // 90 | // ERRORS 91 | // 92 | // NFS3ERR_NOENT 93 | // NFS3ERR_IO 94 | // NFS3ERR_ACCES 95 | // NFS3ERR_INVAL 96 | // NFS3ERR_EXIST 97 | // NFS3ERR_NOTDIR 98 | // NFS3ERR_NAMETOOLONG 99 | // NFS3ERR_ROFS 100 | // NFS3ERR_NOTEMPTY 101 | // NFS3ERR_STALE 102 | // NFS3ERR_BADHANDLE 103 | // NFS3ERR_NOTSUPP 104 | // NFS3ERR_SERVERFAULT 105 | // 106 | // SEE ALSO 107 | // 108 | // REMOVE. 109 | 110 | 111 | var util = require('util'); 112 | 113 | var assert = require('assert-plus'); 114 | var rpc = require('oncrpc'); 115 | 116 | var NfsCall = require('./nfs_call').NfsCall; 117 | 118 | 119 | 120 | ///--- Globals 121 | 122 | var XDR = rpc.XDR; 123 | 124 | 125 | 126 | ///--- API 127 | 128 | function RmdirCall(opts) { 129 | assert.object(opts, 'opts'); 130 | assert.optionalObject(opts.object, 'opts.object'); 131 | 132 | NfsCall.call(this, opts, true); 133 | 134 | this._object = opts.object || { 135 | dir: '', 136 | name: '' 137 | }; 138 | 139 | this._nfs_rmdir_call = true; // MDB 140 | } 141 | util.inherits(RmdirCall, NfsCall); 142 | Object.defineProperty(RmdirCall.prototype, 'object', { 143 | get: function object() { 144 | return (this._object.dir); 145 | } 146 | }); 147 | 148 | 149 | RmdirCall.prototype._transform = function _transform(chunk, enc, cb) { 150 | if (this.incoming) { 151 | var xdr = new XDR(chunk); 152 | this._object.dir = xdr.readString(); 153 | this._object.name = xdr.readString(); 154 | } else { 155 | this.push(chunk); 156 | } 157 | 158 | cb(); 159 | }; 160 | 161 | 162 | RmdirCall.prototype.writeHead = function writeHead() { 163 | var len = XDR.byteLength(this._object.dir) + 164 | XDR.byteLength(this._object.name); 165 | 166 | var xdr = this._serialize(len); 167 | 168 | xdr.writeString(this._object.dir); 169 | xdr.writeString(this._object.name); 170 | 171 | this.write(xdr.buffer()); 172 | }; 173 | 174 | 175 | RmdirCall.prototype.toString = function toString() { 176 | var fmt = '[object RmdirCall ]'; 177 | return (util.format(fmt, this.xid, this._object)); 178 | }; 179 | 180 | 181 | 182 | ///--- Exports 183 | 184 | module.exports = { 185 | RmdirCall: RmdirCall 186 | }; 187 | -------------------------------------------------------------------------------- /lib/nfs/rmdir_reply.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 | // 3.3.13 Procedure 13: RMDIR - Remove a Directory 8 | // 9 | // SYNOPSIS 10 | // 11 | // RMDIR3res NFSPROC3_RMDIR(RMDIR3args) = 13; 12 | // 13 | // struct RMDIR3args { 14 | // diropargs3 object; 15 | // }; 16 | // 17 | // struct RMDIR3resok { 18 | // wcc_data dir_wcc; 19 | // }; 20 | // 21 | // struct RMDIR3resfail { 22 | // wcc_data dir_wcc; 23 | // }; 24 | // 25 | // union RMDIR3res switch (nfsstat3 status) { 26 | // case NFS3_OK: 27 | // RMDIR3resok resok; 28 | // default: 29 | // RMDIR3resfail resfail; 30 | // }; 31 | // 32 | // DESCRIPTION 33 | // 34 | // Procedure RMDIR removes (deletes) a subdirectory from a 35 | // directory. If the directory entry of the subdirectory is 36 | // the last reference to the subdirectory, the subdirectory 37 | // may be destroyed. On entry, the arguments in RMDIR3args 38 | // are: 39 | // 40 | // object 41 | // A diropargs3 structure identifying the directory entry 42 | // to be removed: 43 | // 44 | // dir 45 | // The file handle for the directory from which the 46 | // subdirectory is to be removed. 47 | // 48 | // name 49 | // The name of the subdirectory to be removed. Refer to 50 | // General comments on filenames on page 30. 51 | // 52 | // On successful return, RMDIR3res.status is NFS3_OK and 53 | // RMDIR3res.resok contains: 54 | // 55 | // dir_wcc 56 | // Weak cache consistency data for the directory, 57 | // object.dir. For a client that requires only the 58 | // post-RMDIR directory attributes, these can be found in 59 | // dir_wcc.after. 60 | // 61 | // Otherwise, RMDIR3res.status contains the error on failure 62 | // and RMDIR3res.resfail contains the following: 63 | // 64 | // dir_wcc 65 | // Weak cache consistency data for the directory, 66 | // object.dir. For a client that requires only the 67 | // post-RMDIR directory attributes, these can be found in 68 | // dir_wcc.after. Note that even though the RMDIR failed, 69 | // full wcc_data is returned to allow the client to 70 | // determine whether the failing RMDIR changed the 71 | // directory. 72 | // 73 | // IMPLEMENTATION 74 | // 75 | // Note that on some servers, removal of a non-empty 76 | // directory is disallowed. 77 | // 78 | // On some servers, the filename, ".", is illegal. These 79 | // servers will return the error, NFS3ERR_INVAL. On some 80 | // servers, the filename, "..", is illegal. These servers 81 | // will return the error, NFS3ERR_EXIST. This would seem 82 | // inconsistent, but allows these servers to comply with 83 | // their own specific interface definitions. Clients should 84 | // be prepared to handle both cases. 85 | // 86 | // The client should not rely on the resources (disk space, 87 | // directory entry, and so on.) formerly associated with the 88 | // directory becoming immediately available. 89 | // 90 | // ERRORS 91 | // 92 | // NFS3ERR_NOENT 93 | // NFS3ERR_IO 94 | // NFS3ERR_ACCES 95 | // NFS3ERR_INVAL 96 | // NFS3ERR_EXIST 97 | // NFS3ERR_NOTDIR 98 | // NFS3ERR_NAMETOOLONG 99 | // NFS3ERR_ROFS 100 | // NFS3ERR_NOTEMPTY 101 | // NFS3ERR_STALE 102 | // NFS3ERR_BADHANDLE 103 | // NFS3ERR_NOTSUPP 104 | // NFS3ERR_SERVERFAULT 105 | // 106 | // SEE ALSO 107 | // 108 | // REMOVE. 109 | 110 | var fs = require('fs'); 111 | var path = require('path'); 112 | var util = require('util'); 113 | 114 | var assert = require('assert-plus'); 115 | var clone = require('clone'); 116 | var rpc = require('oncrpc'); 117 | 118 | var nfs_err = require('./errors'); 119 | var wcc_data = require('./wcc_data'); 120 | var NfsReply = require('./nfs_reply').NfsReply; 121 | 122 | 123 | 124 | ///--- Globals 125 | 126 | var XDR = rpc.XDR; 127 | 128 | 129 | 130 | ///--- API 131 | 132 | function RmdirReply(opts) { 133 | NfsReply.call(this, opts); 134 | 135 | this.status = 0; 136 | this.dir_wcc = opts.dir_wcc | null; 137 | 138 | this._nfs_rmdir_reply = true; // MDB 139 | } 140 | util.inherits(RmdirReply, NfsReply); 141 | RmdirReply.prototype._allowed_error_codes = [ 142 | nfs_err.NFS3ERR_NOENT, 143 | nfs_err.NFS3ERR_IO, 144 | nfs_err.NFS3ERR_ACCES, 145 | nfs_err.NFS3ERR_INVAL, 146 | nfs_err.NFS3ERR_EXIST, 147 | nfs_err.NFS3ERR_NOTDIR, 148 | nfs_err.NFS3ERR_ROFS, 149 | nfs_err.NFS3ERR_NAMETOOLONG, 150 | nfs_err.NFS3ERR_NOTEMPTY, 151 | nfs_err.NFS3ERR_STALE, 152 | nfs_err.NFS3ERR_BADHANDLE, 153 | nfs_err.NFS3ERR_NOTSUPP, 154 | nfs_err.NFS3ERR_SERVERFAULT 155 | ]; 156 | 157 | 158 | RmdirReply.prototype.set_dir_wcc = function set_dir_wcc() { 159 | this.dir_wcc = wcc_data.create(); 160 | return (this.dir_wcc); 161 | }; 162 | 163 | 164 | RmdirReply.prototype._transform = function _transform(chunk, enc, cb) { 165 | if (this.incoming) { 166 | var xdr = new XDR(chunk); 167 | 168 | this.status = xdr.readInt(); 169 | this.dir_wcc = wcc_data.parse(xdr); 170 | } else { 171 | this.push(chunk); 172 | } 173 | 174 | cb(); 175 | }; 176 | 177 | 178 | RmdirReply.prototype.writeHead = function writeHead() { 179 | var len = 4; 180 | 181 | len += wcc_data.length(this.dir_wcc); 182 | 183 | var xdr = this._serialize(len); 184 | 185 | xdr.writeInt(this.status); 186 | 187 | wcc_data.serialize(xdr, this.dir_wcc); 188 | 189 | this.write(xdr.buffer()); 190 | }; 191 | 192 | 193 | RmdirReply.prototype.toString = function toString() { 194 | var fmt = '[object RmdirReply ]'; 195 | return (util.format(fmt, this.xid, this.status, this.dir_wcc)); 196 | }; 197 | 198 | 199 | 200 | ///--- Exports 201 | 202 | module.exports = { 203 | RmdirReply: RmdirReply 204 | }; 205 | -------------------------------------------------------------------------------- /lib/nfs/sattr3.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 path = require('path'); 8 | var util = require('util'); 9 | 10 | var assert = require('assert-plus'); 11 | var clone = require('clone'); 12 | var rpc = require('oncrpc'); 13 | 14 | 15 | 16 | ///--- Globals 17 | 18 | var XDR = rpc.XDR; 19 | 20 | var time_how = { 21 | DONT_CHANGE: 0, 22 | SET_TO_SERVER_TIME: 1, 23 | SET_TO_CLIENT_TIME: 2 24 | }; 25 | 26 | // struct nfstime3 { 27 | // uint32 seconds; 28 | // uint32 nseconds; 29 | // }; 30 | 31 | // enum time_how { 32 | // DONT_CHANGE = 0, 33 | // SET_TO_SERVER_TIME = 1, 34 | // SET_TO_CLIENT_TIME = 2 35 | // }; 36 | 37 | // union set_mode3 switch (bool set_it) { 38 | // case TRUE: 39 | // mode3 mode; 40 | // default: 41 | // void; 42 | // }; 43 | 44 | // union set_uid3 switch (bool set_it) { 45 | // case TRUE: 46 | // uid3 uid; 47 | // default: 48 | // void; 49 | // }; 50 | 51 | // union set_gid3 switch (bool set_it) { 52 | // case TRUE: 53 | // gid3 gid; 54 | // default: 55 | // void; 56 | // }; 57 | 58 | // union set_size3 switch (bool set_it) { 59 | // case TRUE: 60 | // size3 size; 61 | // default: 62 | // void; 63 | // }; 64 | 65 | // union set_atime switch (time_how set_it) { 66 | // case SET_TO_CLIENT_TIME: 67 | // nfstime3 atime; 68 | // default: 69 | // void; 70 | // }; 71 | 72 | // union set_mtime switch (time_how set_it) { 73 | // case SET_TO_CLIENT_TIME: 74 | // nfstime3 mtime; 75 | // default: 76 | // void; 77 | // }; 78 | 79 | // struct sattr3 { 80 | // set_mode3 mode; 81 | // set_uid3 uid; 82 | // set_gid3 gid; 83 | // set_size3 size; 84 | // set_atime atime; 85 | // set_mtime mtime; 86 | // }; 87 | 88 | 89 | function parse_sattr3(xdr) { 90 | assert.object(xdr, 'xdr'); 91 | 92 | var mode; 93 | var uid; 94 | var gid; 95 | var size; 96 | var a_time; 97 | var m_time; 98 | 99 | if (xdr.readBool()) 100 | mode = xdr.readInt(); 101 | else 102 | mode = null; 103 | 104 | if (xdr.readBool()) 105 | uid = xdr.readInt(); 106 | else 107 | uid = null; 108 | 109 | if (xdr.readBool()) 110 | gid = xdr.readInt(); 111 | else 112 | gid = null; 113 | 114 | if (xdr.readBool()) 115 | size = xdr.readHyper(); 116 | else 117 | size = null; 118 | 119 | var how_a_time = xdr.readInt(); 120 | if (how_a_time === time_how.SET_TO_CLIENT_TIME) { 121 | a_time = { 122 | seconds: xdr.readInt(), 123 | nseconds: xdr.readInt() 124 | }; 125 | } else { 126 | a_time = null; 127 | } 128 | 129 | var how_m_time = xdr.readInt(); 130 | if (how_m_time === time_how.SET_TO_CLIENT_TIME) { 131 | m_time = { 132 | seconds: xdr.readInt(), 133 | nseconds: xdr.readInt() 134 | }; 135 | } else { 136 | m_time = null; 137 | } 138 | 139 | var sattr3 = { 140 | mode: mode, 141 | uid: uid, 142 | gid: gid, 143 | size: size, 144 | how_a_time: how_a_time, 145 | atime: a_time, 146 | how_m_time: how_m_time, 147 | mtime: m_time 148 | }; 149 | 150 | return (sattr3); 151 | } 152 | 153 | 154 | function serialize_sattr3(xdr, sattr3) { 155 | assert.object(xdr, 'xdr'); 156 | assert.object(sattr3, 'sattr3'); 157 | 158 | if (! sattr3.mode) { 159 | xdr.writeBool(false); 160 | } else { 161 | xdr.writeBool(true); 162 | xdr.writeInt(sattr3.mode); 163 | } 164 | 165 | if (! sattr3.uid) { 166 | xdr.writeBool(false); 167 | } else { 168 | xdr.writeBool(true); 169 | xdr.writeInt(sattr3.uid); 170 | } 171 | 172 | if (! sattr3.gid) { 173 | xdr.writeBool(false); 174 | } else { 175 | xdr.writeBool(true); 176 | xdr.writeInt(sattr3.gid); 177 | } 178 | 179 | if (! sattr3.size) { 180 | xdr.writeBool(false); 181 | } else { 182 | xdr.writeBool(true); 183 | xdr.writeHyper(sattr3.size); 184 | } 185 | 186 | xdr.writeInt(sattr3.how_a_time); 187 | if (sattr3.how_a_time === time_how.SET_TO_CLIENT_TIME) { 188 | xdr.writeInt(sattr3.atime.seconds); 189 | xdr.writeInt(sattr3.atime.nseconds); 190 | } 191 | 192 | xdr.writeInt(sattr3.how_m_time); 193 | if (sattr3.how_m_time === time_how.SET_TO_CLIENT_TIME) { 194 | xdr.writeInt(sattr3.mtime.seconds); 195 | xdr.writeInt(sattr3.mtime.nseconds); 196 | } 197 | 198 | return (xdr); 199 | } 200 | 201 | 202 | function XDR_length(sattr3) { 203 | assert.object(sattr3, 'sattr3'); 204 | 205 | var len = 0; 206 | 207 | len += 4; 208 | if (sattr3.mode) 209 | len += 4; 210 | 211 | len += 4; 212 | if (sattr3.uid) 213 | len += 4; 214 | 215 | len += 4; 216 | if (sattr3.gid) 217 | len += 4; 218 | 219 | len += 4; 220 | if (sattr3.size) 221 | len += 8; 222 | 223 | len += 4; 224 | if (sattr3.how_a_time === time_how.SET_TO_CLIENT_TIME) 225 | len += 8; 226 | 227 | len += 4; 228 | if (sattr3.how_m_time === time_how.SET_TO_CLIENT_TIME) 229 | len += 8; 230 | 231 | return (len); 232 | } 233 | 234 | 235 | ///--- Exports 236 | 237 | module.exports = { 238 | parse: parse_sattr3, 239 | serialize: serialize_sattr3, 240 | length: XDR_length, 241 | time_how: time_how 242 | }; 243 | -------------------------------------------------------------------------------- /lib/nfs/symlink_call.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 | // 3.3.10 Procedure 10: SYMLINK - Create a symbolic link 8 | // 9 | // SYNOPSIS 10 | // 11 | // SYMLINK3res NFSPROC3_SYMLINK(SYMLINK3args) = 10; 12 | // 13 | // struct symlinkdata3 { 14 | // sattr3 symlink_attributes; 15 | // nfspath3 symlink_data; 16 | // }; 17 | // 18 | // struct SYMLINK3args { 19 | // diropargs3 where; 20 | // symlinkdata3 symlink; 21 | // }; 22 | // 23 | // struct SYMLINK3resok { 24 | // post_op_fh3 obj; 25 | // post_op_attr obj_attributes; 26 | // wcc_data dir_wcc; 27 | // }; 28 | // 29 | // struct SYMLINK3resfail { 30 | // wcc_data dir_wcc; 31 | // }; 32 | // 33 | // union SYMLINK3res switch (nfsstat3 status) { 34 | // case NFS3_OK: 35 | // SYMLINK3resok resok; 36 | // default: 37 | // SYMLINK3resfail resfail; 38 | // }; 39 | // 40 | // DESCRIPTION 41 | // 42 | // Procedure SYMLINK creates a new symbolic link. On entry, 43 | // the arguments in SYMLINK3args are: 44 | // 45 | // where 46 | // The location of the symbolic link to be created: 47 | // 48 | // dir 49 | // The file handle for the directory in which the 50 | // symbolic link is to be created. 51 | // 52 | // name 53 | // The name that is to be associated with the created 54 | // symbolic link. Refer to General comments on 55 | // filenames on page 30. 56 | // 57 | // symlink 58 | // The symbolic link to create: 59 | // 60 | // symlink_attributes 61 | // The initial attributes for the symbolic link. 62 | // 63 | // symlink_data 64 | // The string containing the symbolic link data. 65 | // 66 | // On successful return, SYMLINK3res.status is NFS3_OK and 67 | // SYMLINK3res.resok contains: 68 | // 69 | // obj 70 | // The file handle for the newly created symbolic link. 71 | // 72 | // obj_attributes 73 | // The attributes for the newly created symbolic link. 74 | // 75 | // dir_wcc 76 | // Weak cache consistency data for the directory, 77 | // where.dir. For a client that requires only the 78 | // post-SYMLINK directory attributes, these can be found 79 | // in dir_wcc.after. 80 | // 81 | // Otherwise, SYMLINK3res.status contains the error on 82 | // failure and SYMLINK3res.resfail contains the following: 83 | // 84 | // dir_wcc 85 | // Weak cache consistency data for the directory, 86 | // where.dir. For a client that requires only the 87 | // post-SYMLINK directory attributes, these can be found 88 | // in dir_wcc.after. Even though the SYMLINK failed, full 89 | // wcc_data is returned to allow the client to determine 90 | // whether the failing SYMLINK changed the directory. 91 | // 92 | // IMPLEMENTATION 93 | // 94 | // Refer to General comments on filenames on page 30. 95 | // 96 | // For symbolic links, the actual file system node and its 97 | // contents are expected to be created in a single atomic 98 | // operation. That is, once the symbolic link is visible, 99 | // there must not be a window where a READLINK would fail or 100 | // return incorrect data. 101 | // 102 | // ERRORS 103 | // 104 | // NFS3ERR_IO 105 | // NFS3ERR_ACCES 106 | // NFS3ERR_EXIST 107 | // NFS3ERR_NOTDIR 108 | // NFS3ERR_NOSPC 109 | // NFS3ERR_ROFS 110 | // NFS3ERR_NAMETOOLONG 111 | // NFS3ERR_DQUOT 112 | // NFS3ERR_STALE 113 | // NFS3ERR_BADHANDLE 114 | // NFS3ERR_NOTSUPP 115 | // NFS3ERR_SERVERFAULT 116 | // 117 | // SEE ALSO 118 | // 119 | // READLINK, CREATE, MKDIR, MKNOD, FSINFO, and PATHCONF. 120 | 121 | var util = require('util'); 122 | 123 | var assert = require('assert-plus'); 124 | var rpc = require('oncrpc'); 125 | 126 | var sattr3 = require('./sattr3'); 127 | 128 | var NfsCall = require('./nfs_call').NfsCall; 129 | 130 | 131 | 132 | ///--- Globals 133 | 134 | var XDR = rpc.XDR; 135 | 136 | 137 | 138 | ///--- API 139 | 140 | function SymlinkCall(opts) { 141 | assert.object(opts, 'opts'); 142 | assert.optionalObject(opts.where, 'opts.where'); 143 | assert.optionalObject(opts.symlink_attributes, 'opts.symlink_attributes'); 144 | assert.optionalString(opts.symlink_data, 'opts.symlink_data'); 145 | 146 | NfsCall.call(this, opts, true); 147 | 148 | this.where = opts.where || { 149 | dir: '', 150 | name: '' 151 | }; 152 | this.symlink_attributes = opts.symlink_attributes || { 153 | mode: null, 154 | uid: null, 155 | gid: null, 156 | size: null, 157 | how_a_time: 0, 158 | atime: null, 159 | how_m_time: 0, 160 | mtime: null 161 | }; 162 | this.symlink_data = opts.symlink_data || ''; 163 | 164 | this._nfs_symlink_call = true; // MDB 165 | } 166 | util.inherits(SymlinkCall, NfsCall); 167 | Object.defineProperty(SymlinkCall.prototype, 'object', { 168 | get: function object() { 169 | return (this.where.dir); 170 | } 171 | }); 172 | 173 | 174 | SymlinkCall.prototype._transform = function _transform(chunk, enc, cb) { 175 | if (this.incoming) { 176 | var xdr = new XDR(chunk); 177 | this.where.dir = xdr.readString(); 178 | this.where.name = xdr.readString(); 179 | this.symlink_attributes = sattr3.parse(xdr); 180 | this.symlink_data = xdr.readString(); 181 | } else { 182 | this.push(chunk); 183 | } 184 | 185 | cb(); 186 | }; 187 | 188 | 189 | SymlinkCall.prototype.writeHead = function writeHead() { 190 | var len = XDR.byteLength(this.where.dir) + XDR.byteLength(this.where.name); 191 | 192 | len += sattr3.length(this.symlink_attributes); 193 | len += XDR.byteLength(this.symlink_data); 194 | 195 | var xdr = this._serialize(len); 196 | 197 | xdr.writeString(this.where.dir); 198 | xdr.writeString(this.where.name); 199 | sattr3.serialize(xdr, this.symlink_attributes); 200 | xdr.writeString(this.symlink_data); 201 | 202 | this.write(xdr.buffer()); 203 | }; 204 | 205 | 206 | SymlinkCall.prototype.toString = function toString() { 207 | var fmt = '[object SymlinkCall ]'; 209 | return (util.format(fmt, this.xid, 210 | this.where, this.symlink_attributes, this.symlink_data)); 211 | }; 212 | 213 | 214 | 215 | ///--- Exports 216 | 217 | module.exports = { 218 | SymlinkCall: SymlinkCall 219 | }; 220 | -------------------------------------------------------------------------------- /lib/nfs/symlink_reply.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 | // 3.3.10 Procedure 10: SYMLINK - Create a symbolic link 8 | // 9 | // SYNOPSIS 10 | // 11 | // SYMLINK3res NFSPROC3_SYMLINK(SYMLINK3args) = 10; 12 | // 13 | // struct symlinkdata3 { 14 | // sattr3 symlink_attributes; 15 | // nfspath3 symlink_data; 16 | // }; 17 | // 18 | // struct SYMLINK3args { 19 | // diropargs3 where; 20 | // symlinkdata3 symlink; 21 | // }; 22 | // 23 | // struct SYMLINK3resok { 24 | // post_op_fh3 obj; 25 | // post_op_attr obj_attributes; 26 | // wcc_data dir_wcc; 27 | // }; 28 | // 29 | // struct SYMLINK3resfail { 30 | // wcc_data dir_wcc; 31 | // }; 32 | // 33 | // union SYMLINK3res switch (nfsstat3 status) { 34 | // case NFS3_OK: 35 | // SYMLINK3resok resok; 36 | // default: 37 | // SYMLINK3resfail resfail; 38 | // }; 39 | // 40 | // DESCRIPTION 41 | // 42 | // Procedure SYMLINK creates a new symbolic link. On entry, 43 | // the arguments in SYMLINK3args are: 44 | // 45 | // where 46 | // The location of the symbolic link to be created: 47 | // 48 | // dir 49 | // The file handle for the directory in which the 50 | // symbolic link is to be created. 51 | // 52 | // name 53 | // The name that is to be associated with the created 54 | // symbolic link. Refer to General comments on 55 | // filenames on page 30. 56 | // 57 | // symlink 58 | // The symbolic link to create: 59 | // 60 | // symlink_attributes 61 | // The initial attributes for the symbolic link. 62 | // 63 | // symlink_data 64 | // The string containing the symbolic link data. 65 | // 66 | // On successful return, SYMLINK3res.status is NFS3_OK and 67 | // SYMLINK3res.resok contains: 68 | // 69 | // obj 70 | // The file handle for the newly created symbolic link. 71 | // 72 | // obj_attributes 73 | // The attributes for the newly created symbolic link. 74 | // 75 | // dir_wcc 76 | // Weak cache consistency data for the directory, 77 | // where.dir. For a client that requires only the 78 | // post-SYMLINK directory attributes, these can be found 79 | // in dir_wcc.after. 80 | // 81 | // Otherwise, SYMLINK3res.status contains the error on 82 | // failure and SYMLINK3res.resfail contains the following: 83 | // 84 | // dir_wcc 85 | // Weak cache consistency data for the directory, 86 | // where.dir. For a client that requires only the 87 | // post-SYMLINK directory attributes, these can be found 88 | // in dir_wcc.after. Even though the SYMLINK failed, full 89 | // wcc_data is returned to allow the client to determine 90 | // whether the failing SYMLINK changed the directory. 91 | // 92 | // IMPLEMENTATION 93 | // 94 | // Refer to General comments on filenames on page 30. 95 | // 96 | // For symbolic links, the actual file system node and its 97 | // contents are expected to be created in a single atomic 98 | // operation. That is, once the symbolic link is visible, 99 | // there must not be a window where a READLINK would fail or 100 | // return incorrect data. 101 | // 102 | // ERRORS 103 | // 104 | // NFS3ERR_IO 105 | // NFS3ERR_ACCES 106 | // NFS3ERR_EXIST 107 | // NFS3ERR_NOTDIR 108 | // NFS3ERR_NOSPC 109 | // NFS3ERR_ROFS 110 | // NFS3ERR_NAMETOOLONG 111 | // NFS3ERR_DQUOT 112 | // NFS3ERR_STALE 113 | // NFS3ERR_BADHANDLE 114 | // NFS3ERR_NOTSUPP 115 | // NFS3ERR_SERVERFAULT 116 | // 117 | // SEE ALSO 118 | // 119 | // READLINK, CREATE, MKDIR, MKNOD, FSINFO, and PATHCONF. 120 | 121 | var fs = require('fs'); 122 | var path = require('path'); 123 | var util = require('util'); 124 | 125 | var assert = require('assert-plus'); 126 | var clone = require('clone'); 127 | var rpc = require('oncrpc'); 128 | 129 | var nfs_err = require('./errors'); 130 | var fattr3 = require('./fattr3'); 131 | var wcc_data = require('./wcc_data'); 132 | var NfsReply = require('./nfs_reply').NfsReply; 133 | 134 | 135 | 136 | ///--- Globals 137 | 138 | var XDR = rpc.XDR; 139 | 140 | 141 | 142 | ///--- API 143 | 144 | function SymlinkReply(opts) { 145 | NfsReply.call(this, opts); 146 | 147 | this.status = 0; 148 | this.wcc_data = opts.wcc_data | {}; 149 | 150 | this._nfs_symlink_reply = true; // MDB 151 | } 152 | util.inherits(SymlinkReply, NfsReply); 153 | SymlinkReply.prototype._allowed_error_codes = [ 154 | nfs_err.NFS3ERR_IO, 155 | nfs_err.NFS3ERR_ACCES, 156 | nfs_err.NFS3ERR_EXIST, 157 | nfs_err.NFS3ERR_NOTDIR, 158 | nfs_err.NFS3ERR_NOSPC, 159 | nfs_err.NFS3ERR_ROFS, 160 | nfs_err.NFS3ERR_NAMETOOLONG, 161 | nfs_err.NFS3ERR_DQUOT, 162 | nfs_err.NFS3ERR_STALE, 163 | nfs_err.NFS3ERR_BADHANDLE, 164 | nfs_err.NFS3ERR_NOTSUPP, 165 | nfs_err.NFS3ERR_SERVERFAULT 166 | ]; 167 | 168 | 169 | SymlinkReply.prototype.setObjAttributes = function setObjAttributes(stats) { 170 | assert.ok(stats instanceof fs.Stats, 'fs.Stats'); 171 | 172 | this.obj_attributes = fattr3.create(stats); 173 | 174 | return (this.obj_attributes); 175 | }; 176 | 177 | 178 | SymlinkReply.prototype.set_dir_wcc = function set_dir_wcc() { 179 | this.dir_wcc = wcc_data.create(); 180 | return (this.dir_wcc); 181 | }; 182 | 183 | 184 | SymlinkReply.prototype._transform = function _transform(chunk, enc, cb) { 185 | if (this.incoming) { 186 | var xdr = new XDR(chunk); 187 | 188 | this.status = xdr.readInt(); 189 | if (this.status === 0) { 190 | if (xdr.readBool()) 191 | this.obj = xdr.readString(); 192 | if (xdr.readBool()) 193 | this.obj_attributes = fattr3.parse(xdr); 194 | } 195 | 196 | this.dir_wcc = wcc_data.parse(xdr); 197 | } else { 198 | this.push(chunk); 199 | } 200 | 201 | cb(); 202 | }; 203 | 204 | 205 | SymlinkReply.prototype.writeHead = function writeHead() { 206 | var len = 4; 207 | 208 | if (this.status === 0) { 209 | len += 4 + XDR.byteLength(this.obj); 210 | 211 | len += 4; 212 | if (this.obj_attributes) 213 | len += fattr3.XDR_SIZE; 214 | } 215 | 216 | len += wcc_data.length(this.dir_wcc); 217 | 218 | var xdr = this._serialize(len); 219 | 220 | xdr.writeInt(this.status); 221 | 222 | if (this.status === 0) { 223 | xdr.writeBool(true); 224 | xdr.writeString(this.obj); 225 | if (this.obj_attributes) { 226 | xdr.writeBool(true); 227 | fattr3.serialize(xdr, this.obj_attributes); 228 | } else { 229 | xdr.writeBool(false); 230 | } 231 | 232 | } 233 | 234 | wcc_data.serialize(xdr, this.dir_wcc); 235 | 236 | this.write(xdr.buffer()); 237 | }; 238 | 239 | 240 | SymlinkReply.prototype.toString = function toString() { 241 | var fmt = '[object SymlinkReply ]'; 243 | return (util.format(fmt, this.xid, this.status, this.obj, 244 | this.obj_attributes, this.dir_wcc)); 245 | }; 246 | 247 | 248 | 249 | ///--- Exports 250 | 251 | module.exports = { 252 | SymlinkReply: SymlinkReply 253 | }; 254 | -------------------------------------------------------------------------------- /lib/nfs/wcc_data.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 path = require('path'); 8 | var util = require('util'); 9 | 10 | var assert = require('assert-plus'); 11 | var clone = require('clone'); 12 | var rpc = require('oncrpc'); 13 | 14 | var fattr3 = require('./fattr3'); 15 | 16 | 17 | ///--- Globals 18 | 19 | var XDR = rpc.XDR; 20 | 21 | 22 | 23 | // struct nfstime3 { 24 | // uint32 seconds; 25 | // uint32 nseconds; 26 | // }; 27 | 28 | // struct wcc_attr { 29 | // size3 size; // uint64 30 | // nfstime3 mtime; 31 | // nfstime3 ctime; 32 | // }; 33 | 34 | // union pre_op_attr switch (bool attributes_follow) { 35 | // case TRUE: 36 | // wcc_attr attributes; 37 | // case FALSE: 38 | // void; 39 | // }; 40 | 41 | // union post_op_attr switch (bool attributes_follow) { 42 | // case TRUE: 43 | // fattr3 attributes; 44 | // case FALSE: 45 | // void; 46 | // }; 47 | 48 | // struct wcc_data { 49 | // pre_op_attr before; 50 | // post_op_attr after; 51 | // }; 52 | 53 | // To support the weak cache consistency data return object we must be 54 | // able to atomically stat the file before we make any changes, make 55 | // the changes, then stat the file again once we're done. 56 | 57 | function create_wcc_data() { 58 | var wcc_data = { 59 | before: null, 60 | after: null 61 | }; 62 | 63 | return (wcc_data); 64 | } 65 | 66 | 67 | function parse_wcc_data(xdr) { 68 | assert.object(xdr, 'xdr'); 69 | 70 | var before; 71 | var after; 72 | 73 | if (xdr.readBool()) { 74 | var size = xdr.readHyper(); 75 | 76 | var m_time = { 77 | seconds: xdr.readInt(), 78 | nseconds: xdr.readInt() 79 | }; 80 | 81 | var c_time = { 82 | seconds: xdr.readInt(), 83 | nseconds: xdr.readInt() 84 | }; 85 | 86 | before = { 87 | size: size, 88 | mtime: m_time, 89 | ctime: c_time 90 | }; 91 | } else { 92 | before = null; 93 | } 94 | 95 | if (xdr.readBool()) { 96 | after = fattr3.parse(xdr); 97 | } else { 98 | after = null; 99 | } 100 | 101 | var wcc_data = { 102 | before: before, 103 | after: after 104 | }; 105 | 106 | return (wcc_data); 107 | } 108 | 109 | 110 | function serialize_wcc_data(xdr, wcc_data) { 111 | if (wcc_data === undefined || wcc_data === null) { 112 | xdr.writeBool(false); 113 | xdr.writeBool(false); 114 | return (xdr); 115 | } 116 | 117 | if (! wcc_data.before) { 118 | xdr.writeBool(false); 119 | } else { 120 | xdr.writeBool(true); 121 | xdr.writeHyper(wcc_data.size); 122 | xdr.writeInt(wcc_data.mtime.seconds); 123 | xdr.writeInt(wcc_data.mtime.nseconds); 124 | xdr.writeInt(wcc_data.ctime.seconds); 125 | xdr.writeInt(wcc_data.ctime.nseconds); 126 | } 127 | 128 | if (! wcc_data.after) { 129 | xdr.writeBool(false); 130 | } else { 131 | xdr.writeBool(true); 132 | fattr3.serialize_fattr3(xdr, wcc_data.after); 133 | } 134 | 135 | return (xdr); 136 | } 137 | 138 | 139 | function XDR_length(wcc_data) { 140 | var len = 0; 141 | 142 | if (wcc_data === undefined || wcc_data === null) { 143 | len = 8; 144 | } else { 145 | len += 4; 146 | if (wcc_data.before) 147 | len += 24; 148 | 149 | len += 4; 150 | if (wcc_data.after) 151 | len += 24; 152 | } 153 | 154 | return (len); 155 | } 156 | 157 | 158 | ///--- Exports 159 | 160 | module.exports = { 161 | create: create_wcc_data, 162 | parse: parse_wcc_data, 163 | serialize: serialize_wcc_data, 164 | length: XDR_length 165 | }; 166 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nfs", 3 | "description": "Node.js bindings for NFS", 4 | "version": "0.1.1", 5 | "author": { 6 | "name": "Joyent, Inc.", 7 | "url": "http://joyent.com/" 8 | }, 9 | "license": "MPL-2.0", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/joyent/node-nfs.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/joyent/node-nfs/issues" 16 | }, 17 | "main": "lib/index.js", 18 | "dependencies": { 19 | "assert-plus": "0.1.4", 20 | "clone": "0.1.10", 21 | "oncrpc": "0.1.0", 22 | "once": "1.2.0" 23 | }, 24 | "devDependencies": { 25 | "bunyan": "0.22.0", 26 | "nodeunit": "0.8.1", 27 | "nodeunit-plus": "0.0.1", 28 | "node-uuid": "1.4.1", 29 | "statvfs": "2.1.0", 30 | "vasync": "1.4.0" 31 | }, 32 | "scripts": { 33 | "test": "for f in $(ls test/*.test.js) ; do nodeunit $f ; done" 34 | }, 35 | "engines": { 36 | "node": ">=0.10.18 <0.12" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /smf/manifests/portmap.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/helper.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 domain = require('domain'); 8 | 9 | var bunyan = require('bunyan'); 10 | var once = require('once'); 11 | 12 | 13 | 14 | ///--- Hacks 15 | 16 | var _require = require.bind(null); 17 | require = function () { 18 | if (require.cache[__dirname + '/helper.js']) 19 | delete require.cache[__dirname + '/helper.js']; 20 | 21 | _require.apply(require, arguments); 22 | }; 23 | 24 | 25 | 26 | ///--- Helpers 27 | 28 | function createLogger(name, stream) { 29 | var log = bunyan.createLogger({ 30 | level: (process.env.LOG_LEVEL || 'info'), 31 | name: name || process.argv[1], 32 | stream: stream || process.stdout, 33 | src: true 34 | }); 35 | return (log); 36 | } 37 | 38 | 39 | 40 | ///--- Exports 41 | 42 | module.exports = { 43 | 44 | after: function after(teardown) { 45 | module.parent.exports.tearDown = function _teardown(callback) { 46 | var d = domain.create(); 47 | var self = this; 48 | 49 | d.once('error', function (err) { 50 | console.error('after: uncaught error\n' + err.stack); 51 | process.exit(1); 52 | }); 53 | 54 | d.run(function () { 55 | teardown.call(self, once(callback)); 56 | }); 57 | }; 58 | }, 59 | 60 | before: function before(setup) { 61 | module.parent.exports.setUp = function _setup(callback) { 62 | var d = domain.create(); 63 | var self = this; 64 | 65 | d.once('error', function (err) { 66 | console.error('before: uncaught error\n' + err.stack); 67 | process.exit(1); 68 | }); 69 | 70 | d.run(function () { 71 | setup.call(self, once(callback)); 72 | }); 73 | }; 74 | }, 75 | 76 | test: function test(name, tester) { 77 | module.parent.exports[name] = function _(t) { 78 | var d = domain.create(); 79 | var self = this; 80 | 81 | d.once('error', function (err) { 82 | t.ifError(err); 83 | t.end(); 84 | }); 85 | 86 | d.add(t); 87 | d.run(function () { 88 | t.end = once(function () { 89 | t.done(); 90 | }); 91 | t.notOk = function notOk(ok, message) { 92 | return (t.ok(!ok, message)); 93 | }; 94 | 95 | tester.call(self, t); 96 | }); 97 | }; 98 | }, 99 | 100 | createLogger: createLogger 101 | }; 102 | 103 | 104 | 105 | Object.keys(module.exports).forEach(function (k) { 106 | global[k] = module.exports[k]; 107 | }); 108 | -------------------------------------------------------------------------------- /test/mount.test.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 util = require('util'); 8 | 9 | var assert = require('assert-plus'); 10 | var libuuid = require('node-uuid'); 11 | require('nodeunit-plus'); 12 | 13 | var nfs = require('../lib'); 14 | 15 | 16 | 17 | ///--- Setup/Teardown 18 | 19 | before(function (cb) { 20 | var self = this; 21 | var server = nfs.createMountServer({ 22 | log: createLogger('MountTestServer') 23 | }); 24 | 25 | assert.ok(server, 'server'); 26 | 27 | server.mounts = {}; 28 | 29 | server.on('uncaughtException', function (req, res, err) { 30 | console.error(err.stack); 31 | process.exit(1); 32 | }); 33 | 34 | server.dump(function dump(req, res, next) { 35 | Object.keys(server.mounts).forEach(function (k) { 36 | res.addMapping({ 37 | name: server.mounts[k], 38 | dirpath: k 39 | }); 40 | }); 41 | res.send(); 42 | next(); 43 | }); 44 | 45 | server.mnt(function mount(req, res, next) { 46 | assert.ok(req.dirpath); 47 | var uuid = libuuid.v4(); 48 | server.mounts[req.dirpath] = uuid; 49 | res.setFileHandle(uuid); 50 | res.send(); 51 | next(); 52 | }); 53 | 54 | server.umnt(function unmount(req, res, next) { 55 | assert.ok(req.dirpath); 56 | assert.ok(server.mounts[req.dirpath]); 57 | if (server.mounts[req.dirpath]) 58 | delete server.mounts[req.dirpath]; 59 | res.send(); 60 | next(); 61 | }); 62 | 63 | server.listen(function () { 64 | var addr = server.address(); 65 | 66 | var client = nfs.createMountClient({ 67 | log: createLogger('MountClient'), 68 | url: util.format('tcp://%s:%d', addr.address, addr.port) 69 | }); 70 | 71 | assert.ok(client, 'client'); 72 | 73 | client.once('connect', function () { 74 | self.client = client; 75 | self.server = server; 76 | cb(); 77 | }); 78 | }); 79 | }); 80 | 81 | 82 | after(function (cb) { 83 | var self = this; 84 | this.client.close(function () { 85 | self.server.close(cb); 86 | }); 87 | }); 88 | 89 | 90 | 91 | ///--- Tests 92 | 93 | test('dump', function (t) { 94 | this.client.dump(function (err, reply) { 95 | t.ifError(err); 96 | t.ok(reply); 97 | t.ok(reply.mappings); 98 | t.equal((reply.mappings || []).length, 0); 99 | t.end(); 100 | }); 101 | }); 102 | 103 | 104 | test('mount', function (t) { 105 | var self = this; 106 | 107 | this.client.mnt('/tmp', function (err, reply) { 108 | t.ifError(err); 109 | t.ok(reply); 110 | t.ok(reply.mountinfo); 111 | t.ok(reply.mountinfo.fhandle); 112 | t.ok(reply.mountinfo.auth_flavors); 113 | t.deepEqual(reply.mountinfo.auth_flavors, [1]); 114 | 115 | self.client.dump(function (d_err, d_reply) { 116 | t.ifError(d_err); 117 | t.ok(d_reply); 118 | t.equal(d_reply.mappings.length, 1); 119 | t.equal(d_reply.mappings[0].dirpath, '/tmp'); 120 | t.ok(d_reply.mappings[0].name); 121 | t.end(); 122 | }); 123 | }); 124 | }); 125 | 126 | 127 | test('unmount', function (t) { 128 | var dirpath = '/tmp'; 129 | var self = this; 130 | 131 | this.client.mnt(dirpath, function (err, reply) { 132 | t.ifError(err); 133 | t.ok(reply); 134 | t.ok(reply.mountinfo); 135 | t.ok(reply.mountinfo.fhandle); 136 | t.ok(reply.mountinfo.auth_flavors); 137 | t.deepEqual(reply.mountinfo.auth_flavors, [1]); 138 | 139 | self.client.umnt(dirpath, function (u_err, u_reply) { 140 | t.ifError(u_err); 141 | t.ok(u_reply); 142 | 143 | self.client.dump(function (d_err, d_reply) { 144 | t.ifError(d_err); 145 | t.ok(d_reply); 146 | t.equal(d_reply.mappings.length, 0); 147 | t.end(); 148 | }); 149 | }); 150 | }); 151 | }); 152 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 __dirname 113 | +define __filename 114 | +define after 115 | +define before 116 | +define test 117 | +define createLogger 118 | +define clearInterval 119 | +define clearTimeout 120 | +define console 121 | +define exports 122 | +define global 123 | +define module 124 | +define process 125 | +define require 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/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/jsstyle.conf: -------------------------------------------------------------------------------- 1 | indent=2 2 | doxygen 3 | unparenthesized-return=0 4 | blank-after-start-comment=0 5 | -------------------------------------------------------------------------------- /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.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 <