#! /usr/bin/env python
#@+leo-ver=4
#@+node:@file freenetfs.py
#@@first
"""
A FUSE-based filesystem for freenet
Written May 2006 by aum
Released under the GNU Lesser General Public License
Requires:
- python2.3 or later
- FUSE kernel module installed and loaded
(apt-get install fuse-source, crack tarball, build and install)
- python2.3-fuse
- libfuse2
"""
#@+others
#@+node:imports
import sys, os, time, stat, errno
from StringIO import StringIO
import thread
from threading import Lock
import traceback
from Queue import Queue
import sha, md5
from UserString import MutableString
from errno import *
from stat import *
try:
import warnings
warnings.filterwarnings('ignore',
'Python C API version mismatch',
RuntimeWarning,
)
except:
pass
import sys
from errno import *
import fcp
from fcp.xmlobject import XMLFile
from fcp.node import guessMimetype, base64encode, base64decode, uriIsPrivate
#@-node:imports
#@+node:globals
argv = sys.argv
argc = len(argv)
progname = argv[0]
fcpHost = fcp.node.defaultFCPHost
fcpPort = fcp.node.defaultFCPPort
defaultVerbosity = fcp.DETAIL
quiet = 0
myuid = os.getuid()
mygid = os.getgid()
inodes = {}
inodesNext = 1
# set this to disable hits to node, for debugging
_no_node = 0
# special filenames in freedisk toplevel dirs
freediskSpecialFiles = [
'.privatekey', '.publickey', '.cmd', '.status', ".passwd",
]
showAllExceptions = False
#@-node:globals
#@+node:class ErrnoWrapper
class ErrnoWrapper:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kw):
try:
return apply(self.func, args, kw)
except (IOError, OSError), detail:
if showAllExceptions:
traceback.print_exc()
# Sometimes this is an int, sometimes an instance...
if hasattr(detail, "errno"): detail = detail.errno
return -detail
#@-node:class ErrnoWrapper
#@+node:class FreenetBaseFS
class FreenetBaseFS:
#@ @+others
#@+node:attribs
multithreaded = 0
flags = 1
debug = False
fcpHost = fcpHost
fcpPort = fcpPort
verbosity = defaultVerbosity
allow_other = False
kernel_cache = False
config = os.path.join(os.path.expanduser("~"), ".freediskrc")
# Files and directories already present in the filesytem.
# Note - directories must end with "/"
initialFiles = [
"/",
"/get/",
"/put/",
"/keys/",
"/usr/",
"/cmds/",
]
chrFiles = [
]
#@-node:attribs
#@+node:__init__
def __init__(self, mountpoint, *args, **kw):
"""
Create a freenetfs
Arguments:
- mountpoint - the dir in the filesystem at which to mount the fs
- other args get passed to fuse
Keywords:
- multithreaded - whether to run the fs multithreaded, default True
- fcpHost - hostname of FCP service
- fcpPort - port number of FCP service
- verbosity - defaults to fcp.DETAIL
- config - location of config file
- debug - whether to run in debug mode, default False
"""
self.log("FreenetBaseFS.__init__: args=%s kw=%s" % (args, kw))
for k in ['multithreaded',
'fcpHost',
'fcpPort',
'verbosity',
'debug',
]:
if kw.has_key(k):
v = kw.pop(k)
try:
v = int(v)
except:
pass
setattr(self, k, v)
self.optlist = list(args)
self.optdict = dict(kw)
self.mountpoint = mountpoint
#if not self.config:
# raise Exception("Missing 'config=filename.conf' argument")
#self.loadConfig()
self.setupFiles()
self.setupFreedisks()
# do stuff to set up your filesystem here, if you want
#thread.start_new_thread(self.mythread, ())
if 0:
self.log("xmp.py:Xmp:mountpoint: %s" % repr(self.mountpoint))
self.log("xmp.py:Xmp:unnamed mount options: %s" % self.optlist)
self.log("xmp.py:Xmp:named mount options: %s" % self.optdict)
try:
self.node = None
self.connectToNode()
except:
raise
pass
#@-node:__init__
#@+node:command handlers
# methods which handle filesystem commands
#@+others
#@+node:executeCommand
def executeCommand(self, cmd):
"""
Executes a single-line command that was submitted as
a base64-encoded filename in /cmds/
"""
self.log("executeCommand:cmd=%s" % repr(cmd))
try:
cmd, args = cmd.split(" ", 1)
args = args.split("|")
except:
return "error\nInvalid command %s" % repr(cmd)
method = getattr(self, "cmd_"+cmd, None)
if method:
return method(*args)
else:
return "error\nUnrecognised command %s" % repr(cmd)
#@-node:executeCommand
#@+node:cmd_hello
def cmd_hello(self, *args):
return "ok\nhello: args=%s" % repr(args)
#@-node:cmd_hello
#@+node:cmd_mount
def cmd_mount(self, *args):
"""
tries to mount a freedisk
arguments:
- diskname
- uri (may be public or private)
- password
"""
#print "mount: args=%s" % repr(args)
try:
name, uri, passwd = args
except:
return "error\nmount: invalid arguments %s" % repr(args)
try:
self.addDisk(name, uri, passwd)
except:
return "error\nmount: failed to mount disk %s" % name
return "ok\nmount: successfully mounted disk %s" % name
#@-node:cmd_mount
#@+node:cmd_umount
def cmd_umount(self, *args):
"""
tries to unmount a freedisk
arguments:
- diskname
"""
#print "mount: args=%s" % repr(args)
try:
name = args[0]
except:
return "error\numount: invalid arguments %s" % repr(args)
try:
self.delDisk(name)
except:
traceback.print_exc()
return "error\numount: failed to unmount freedisk '%s'" % name
return "ok\numount: successfully unmounted freedisk %s" % name
#@-node:cmd_umount
#@+node:cmd_update
def cmd_update(self, *args):
"""
Does an update of a freedisk from freenet
"""
#print "update: args=%s" % repr(args)
try:
name = args[0]
except:
return "error\nupdate: invalid arguments %s" % repr(args)
try:
self.updateDisk(name)
except:
traceback.print_exc()
return "error\nupdate: failed to update freedisk '%s'" % name
return "ok\nupdate: successfully updated freedisk '%s'" % name
#@-node:cmd_update
#@+node:cmd_commit
def cmd_commit(self, *args):
"""
Does an commit of a freedisk into freenet
"""
try:
name = args[0]
except:
return "error\ninvalid arguments %s" % repr(args)
try:
uri = self.commitDisk(name)
except:
traceback.print_exc()
return "error\nfailed to commit freedisk '%s'" % name
return "ok\n%s" % uri
#@-node:cmd_commit
#@-others
#@-node:command handlers
#@+node:fs primitives
# primitives required for actual fs operations
#@+others
#@+node:chmod
def chmod(self, path, mode):
ret = os.chmod(path, mode)
self.log("chmod: path=%s mode=%s\n => %s" % (path, mode, ret))
return ret
#@-node:chmod
#@+node:chown
def chown(self, path, user, group):
ret = os.chown(path, user, group)
self.log("chmod: path=%s user=%s group=%s\n => %s" % (path, user, group, ret))
return ret
#@-node:chown
#@+node:fsync
def fsync(self, path, isfsyncfile):
self.log("fsync: path=%s, isfsyncfile=%s" % (path, isfsyncfile))
return 0
#@-node:fsync
#@+node:getattr
def getattr(self, path):
rec = self.files.get(path, None)
if not rec:
# each of these code segments should assign a record to 'rec',
# or raise an IOError
# retrieving a key?
if path.startswith("/keys/"):
#@ <<generate keypair>>
#@+node:<<generate keypair>>
# generate a new keypair
self.connectToNode()
pubkey, privkey = self.node.genkey()
rec = self.addToCache(
path=path,
isreg=True,
data=pubkey+"\n"+privkey+"\n",
perm=0444,
)
#@-node:<<generate keypair>>
#@nl
elif path.startswith("/get/"):
#@ <<retrieve/cache key>>
#@+node:<<retrieve/cache key>>