1 package dolda.jsvc.store;
4 import dolda.jsvc.util.*;
7 import java.security.*;
9 class FileStore extends Store {
10 private final java.io.File base;
11 private static final int smbuflimit;
12 private static int txserial = 0;
15 int res; /* Java is stupid, as usual... */
17 String p = System.getProperty("dolda.jsvc.store.smallbuf");
19 res = Integer.parseInt(p);
22 } catch(SecurityException e) {
28 private FileStore(Package pkg, java.io.File root) {
30 String nm = pkg.getName();
31 java.io.File base = root;
34 while((p2 = nm.indexOf('.', p)) >= 0) {
35 base = new java.io.File(base, nm.substring(p, p2));
38 this.base = new java.io.File(base, nm.substring(p));
39 AccessController.doPrivileged(new PrivilegedAction<Object>() {
41 if(!FileStore.this.base.exists()) {
42 if(!FileStore.this.base.mkdirs())
43 throw(new RuntimeException("Could not create store directory (Java won't tell me why)"));
50 private static String mangle(String in) {
51 byte[] bytes = in.getBytes(Misc.utf8);
52 StringBuilder buf = new StringBuilder();
53 for(int i = 0; i < bytes.length; i++) {
55 if(((b >= '0') && (b <= '9')) || ((b >= 'A') && (b <= 'Z')) || ((b >= 'a') && (b <= 'z'))) {
59 buf.append(Misc.int2hex((b & 0xf0) >> 4, true));
60 buf.append(Misc.int2hex(b & 0x0f, true));
63 return(buf.toString());
66 private static String demangle(String in) {
67 ByteArrayOutputStream buf = new ByteArrayOutputStream();
68 for(int i = 0; i < in.length(); i++) {
69 char c = in.charAt(i);
71 char d1 = in.charAt(i + 1);
72 char d2 = in.charAt(i + 2);
74 buf.write((byte)((Misc.hex2int(d1) << 4) | Misc.hex2int(d2)));
77 throw(new RuntimeException("Invalid filename in store"));
81 byte[] bytes = buf.toByteArray();
82 return(new String(bytes, Misc.utf8));
85 private class RFile implements File {
86 private final java.io.File fs;
87 private final String name;
89 private class TXStream extends OutputStream {
90 private FileOutputStream buf = null;
91 private java.io.File tmpfile;
92 private boolean closed = false;
94 private void init() throws IOException {
96 buf = AccessController.doPrivileged(new PrivilegedExceptionAction<FileOutputStream>() {
97 public FileOutputStream run() throws IOException {
98 synchronized(RFile.class) {
99 int serial = txserial++;
100 tmpfile = new java.io.File(fs.getPath() + ".new." + txserial);
101 if(tmpfile.exists()) {
102 if(!tmpfile.delete())
103 throw(new IOException("Could not delete previous temporary file (Java won't tell my why)"));
105 return(new FileOutputStream(tmpfile));
109 } catch(PrivilegedActionException e) {
110 throw((IOException)e.getCause());
114 public void write(byte[] b, int off, int len) throws IOException {
116 throw(new IOException("This file has already been committed"));
119 buf.write(b, off, len);
122 public void write(byte[] b) throws IOException {
124 throw(new IOException("This file has already been committed"));
130 public void write(int b) throws IOException {
132 throw(new IOException("This file has already been committed"));
138 public void flush() throws IOException {
144 public void close() throws IOException {
149 AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
150 public Object run() throws IOException {
151 if(!tmpfile.renameTo(fs)) {
153 if(!tmpfile.renameTo(fs))
154 throw(new IOException("Could not replace previous file contents with new (Java won't tell me why)"));
159 } catch(PrivilegedActionException e) {
160 throw((IOException)e.getCause());
165 private RFile(java.io.File fs, String name) {
170 public String name() {
174 public InputStream read() {
175 return(AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
176 public InputStream run() {
178 return(new FileInputStream(fs));
179 } catch(FileNotFoundException e) {
186 public OutputStream store() {
187 return(new TXStream());
190 public long mtime() {
191 return(AccessController.doPrivileged(new PrivilegedAction<Long>() {
193 return(fs.lastModified());
198 public void remove() {
199 AccessController.doPrivileged(new PrivilegedAction<Object>() {
200 public InputStream run() {
202 throw(new RuntimeException("Could not delete the file " + fs.getPath() + " (Java won't tell me why)"));
209 public File get(String name) {
210 return(new RFile(new java.io.File(base, mangle(name)), name));
213 public Iterator<File> iterator() {
214 final java.io.File[] ls = base.listFiles();
215 return(new Iterator<File>() {
217 private File cur = null;
219 public boolean hasNext() {
220 return(i < ls.length);
224 java.io.File f = ls[i++];
225 cur = new RFile(f, demangle(f.getName()));
229 public void remove() {
231 throw(new IllegalStateException());
238 public static void register() {
239 Store.register("file", new Factory() {
240 public Store create(String rootname, Package pkg) {
241 java.io.File root = new java.io.File(rootname);
242 ThreadContext ctx = ThreadContext.current();
244 if(ctx.server().name() != null)
245 root = new java.io.File(root, ctx.server().name());
247 return(new FileStore(pkg, root));