So, the problem is that "./obnam backup ." works, but "./obnam backup /foo/bar" does not. This is because the VFS layer tries to support a poor man's version of chroot. That's useful, for restores, so that it is hard to accidentally overwrite files, but I think I will skip that protection to make everything else easier.
I'll change the VFS layer so that it does not have a "root" concept. This may require quite a bit of change, though. For sftp, I'll need to figure out how to handle the case of paths relative to the user's remote home directory. The current API makes that easy.
I could support having a "base directory", but allowing it to change. That'll still mean the caller will need to handle a little bit more bureaucracy than currently, but not much. Definitely less than not having a base directory at all.
Let's see. Currently I have this:
fs = fsfactory.new('sftp://foo/~/bar/backup.store') fs.write_file('blockname', 'blockdata')
With a base directory, it would be the same.
Without base directory:
fs = fsfactory.new('sftp://foo/~/bar/backup.store') fs.write_file(os.path.join(WHAT, 'blockname'), 'blockdata)
It is not clear what WHAT should be, since the paths relative to the user's remote home directory cannot be easily expressed as pathnames, or at least I am not sure if sftp can handle that. Hm, I guess it could use getcwd. I'll check that.
Oh, it does work. Yay. Then I could do the base-less API like this:
fs = fsfactory.new('sftp://foo/~/bar/backup.store') basedir = os.path.join(fs.getcwd(), 'bar/backup.store') fs.write_file(os.path.join(basedir, 'blockname'), 'blockdata)
This just means that the os.path.join calls get moved from inside the API to the client of the API, and that's not good. It makes the caller do a whole lot of repetitive stuff, without much use.
chdir would fix most of that. Actually, that should make things much easier. Like this:
fs = fsfactory.new('sftp://foo/~/bar/backup.store') fs.write_file('blockname', 'blockdata)
This is still base-less, but the default working directory gets automatically set to bar/backup.store, and if the caller doesn't change it, they don't have to worry about it. Meanwhile, it is just as easy to use as the base-ful version.
This removes the whole concept of "basedir", but introduces "current working directory" in the VFS API. Not sure if that's such a big benefit, but at least it is a familiar concept, which should be a bit easier on people using the API.
Let's see what this would do for the backup case. Obnam gets some pathnames from the user, which may be relative. It converts them to absolute ones. Then it puts stuff into the store...
storefs = fsfactory.new(store_url) fs = fsfactory.new(root)
Now, if the pathname (in root) that the user has supplied is absolute, it should work fine, since the VFS layer does not care and lets the normal operating system pathname resolution routines work their magic. And if it is relative, it will still work.
So what I'll do is remove "basedir" completely from the VFS, and add the "chdir" and "getcwd" methods. The VFS initializer will still get the filesystem URL so that it can do a chdir to the path given in the URL. But the URL will not be stored anywhere.
Testing that's going to be a bitch, since it'll change the cwd for the process. Hmm. I could fake this in the LocalFS implementation by storing the basedir, and if the pathname given is relative, doing an os.path.join(basedir, pathname). Yeah, that'll do.
There, done. Next, I'll need to adapt all the code that uses the VFS API to this. Or do I? A simple black box test works now. But only for the current working directory.