root/fuse/HttpDirFS.py

Revision f3943cae42017a9f794e48fe58e3242760c41c90, 4.6 KB (checked in by Martin Collins <martin@…>, 11 months ago)

remove ugly lines

  • Property mode set to 100755
Line 
1#!/usr/bin/env python
2#
3# Copyright 2011 Martin Collins <martin@hatchlane.com>
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#   http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17# usage: ./HttpDirFs.py -o remote=http://www.exampe.com/tree <mountpoint>
18#
19# for fstab:
20# symlink/copy HttpDirFs.py to /usr/bin/httpdirfs
21# httpdirfs /mnt/point fuse remote=http://localhost/simpleindex,allow_other 0 0
22
23import fuse
24import errno
25import time
26import stat
27import sys
28import pycurl
29import curl
30
31fuse.fuse_python_api = (0, 2)
32
33class HttpDirLookup:
34  def __init__(self, path, head):
35    self.dirs     = dict()
36    self.files    = dict()
37    self.ctype    = ""
38    self.status   = ""
39    self.content  = ""
40
41    try:
42      c = pycurl.Curl();
43      c.setopt(c.URL, path)
44      c.setopt(c.FAILONERROR, True);
45      c.setopt(c.FOLLOWLOCATION, True);
46      if head:
47        c.setopt(c.NOBODY, True)
48      else:
49        c.setopt(c.WRITEFUNCTION, self.body_callback)
50      c.perform()
51      self.ctype  = c.getinfo(pycurl.CONTENT_TYPE);
52      self.status = c.getinfo(pycurl.HTTP_CODE);
53      c.close()
54    except:
55      return
56
57    if self.ctype == "text/simpleindex":
58      self.filter_content();
59
60  def filter_content(self):
61    while True:
62      pos = self.content.find("\n");
63
64      if pos == -1:
65        break
66
67      fn = self.content[:pos]
68      self.content = self.content[pos+1:]
69
70      if fn == '':
71        break
72
73      if fn[-1] == '/':
74        self.dirs[fn[:-1]] = 1
75      else:
76        self.files[fn] = 1
77
78  def body_callback(self, buf):
79    self.content += buf;
80   
81class HttpDirFS(fuse.Fuse):
82
83  def __init__(self, *args, **kw):
84    fuse.Fuse.__init__(self, *args, **kw)
85    self.remote   = ""
86    self.scratch  = ""
87    self.lookups  = dict()
88
89  def getattr(self, path):
90 
91    lookup = HttpDirLookup(self.remote + path, True);
92   
93    if lookup.status != 200:
94      return -errno.ENOENT
95
96    st = fuse.Stat();
97
98    if lookup.ctype == 'text/simpleindex':
99      st.st_mode = stat.S_IFDIR | 0555
100    else:
101      st.st_mode = stat.S_IFREG | 0444
102
103    st.st_atime = int(time.time())
104    st.st_mtime = st.st_atime
105    st.st_ctime = st.st_atime
106    st.st_nlink = 2
107    st.st_size  = 4096
108
109    return st;
110
111  def readdir(self, path, offset):
112
113    lookup = HttpDirLookup(self.remote + path, False);
114   
115    if lookup.status != 200:
116      return
117
118    for d in lookup.dirs:
119      yield fuse.Direntry(d)
120    for f in lookup.files:
121      yield fuse.Direntry(f)
122   
123  def read(self, path, size, offset):
124    if path not in self.lookups:
125      return -errno.ENOENT
126
127    return self.lookups[path].content[offset:size+offset]
128
129  def open(self, path, flags):
130    if path not in self.lookups:
131      lookup = HttpDirLookup(self.remote + path, False);
132      if lookup.status != 200:
133        return -errno.ENOENT
134      self.lookups[path] = lookup;
135    else:
136      lookup = self.lookups[path]
137
138  def release(self, path, flags):
139    if path not in self.lookups:
140      return -errno.ENOENT
141
142    del self.lookups[path]
143
144  def statfs ( self ):
145    st = fuse.StatVfs();
146    st.f_blocks = 1
147    st.f_bsize  = 1
148    st.f_bfree  = 0
149    return st;
150
151  # and all the read only stuff
152
153  def readlink ( self, path ):
154    return -errno.ENOSYS
155
156  def getdir(self, path):
157    return -errno.ENOSYS
158
159  def symlink ( self, targetPath, linkPath ):
160    return -errno.EROFS
161
162  def fsync ( self, path, isFsyncFile ):
163    return -errno.EROFS
164
165  def rmdir ( self, path ):
166    return -errno.EROFS
167 
168  def mkdir ( self, path, mode ):
169    return -errno.EROFS
170
171  def rename ( self, oldPath, newPath ):
172    return -errno.EROFS
173
174  def mknod(self, path, mode, dev ):
175    return -errno.EROFS
176
177  def write(self, buf, offset):
178    return -errno.EROFS
179
180  def truncate(self, path, len):
181    return -errno.EROFS
182
183  def unlink ( self, path ):
184    return -errno.EROFS
185
186  def chmod ( self, path, mode ):
187    return -errno.EROFS
188
189  def chown ( self, path, uid, gid ):
190    return -errno.EROFS
191
192  def utime ( self, path, times ):
193    return -errno.EROFS
194 
195
196def main():
197
198  usage = """
199HttpDirFS: A filesystem to allow navigating a WebServer that
200           uses SimpleIndexes (see mod_simpleindex)
201""" + fuse.Fuse.fusage
202
203  server = HttpDirFS(version="%prog " + fuse.__version__,
204                     usage=usage)
205
206  server.parser.add_option(mountopt="remote", metavar="PATH", help="base URL")
207  server.parse(values=server, errex=1)
208
209  if server.remote[:7] != "http://":
210    print >> sys.stderr, "Sorry, I only understand http:// sites"
211    sys.exit(1)
212
213  server.main()
214
215
216if __name__ == '__main__':
217  main()
Note: See TracBrowser for help on using the browser.