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.mkdirs())
42 throw(new RuntimeException("Could not create store directory (Java won't tell me why)"));
48 private static String mangle(String in) {
49 byte[] bytes = in.getBytes(Misc.utf8);
50 StringBuilder buf = new StringBuilder();
51 for(int i = 0; i < bytes.length; i++) {
53 if(((b >= '0') && (b <= '9')) || ((b >= 'A') && (b <= 'Z')) || ((b >= 'a') && (b <= 'z'))) {
57 buf.append(Misc.int2hex((b & 0xf0) >> 4, true));
58 buf.append(Misc.int2hex(b & 0x0f, true));
61 return(buf.toString());
64 private static String demangle(String in) {
65 ByteArrayOutputStream buf = new ByteArrayOutputStream();
66 for(int i = 0; i < in.length(); i++) {
67 char c = in.charAt(i);
69 char d1 = in.charAt(i + 1);
70 char d2 = in.charAt(i + 2);
72 buf.write((byte)((Misc.hex2int(d1) << 4) | Misc.hex2int(d2)));
75 throw(new RuntimeException("Invalid filename in store"));
79 byte[] bytes = buf.toByteArray();
80 return(new String(bytes, Misc.utf8));
83 private class RFile implements File {
84 private final java.io.File fs;
85 private final String name;
87 private class TXStream extends OutputStream {
88 private FileOutputStream buf = null;
89 private java.io.File tmpfile;
90 private boolean closed = false;
92 private void init() throws IOException {
94 buf = AccessController.doPrivileged(new PrivilegedExceptionAction<FileOutputStream>() {
95 public FileOutputStream run() throws IOException {
96 synchronized(RFile.class) {
97 int serial = txserial++;
98 tmpfile = new java.io.File(fs.getPath() + ".new." + txserial);
99 if(tmpfile.exists()) {
100 if(!tmpfile.delete())
101 throw(new IOException("Could not delete previous temporary file (Java won't tell my why)"));
103 return(new FileOutputStream(tmpfile));
107 } catch(PrivilegedActionException e) {
108 throw((IOException)e.getCause());
112 public void write(byte[] b, int off, int len) throws IOException {
114 throw(new IOException("This file has already been committed"));
117 buf.write(b, off, len);
120 public void write(byte[] b) throws IOException {
122 throw(new IOException("This file has already been committed"));
128 public void write(int b) throws IOException {
130 throw(new IOException("This file has already been committed"));
136 public void flush() throws IOException {
142 public void close() throws IOException {
147 AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
148 public Object run() throws IOException {
149 if(!tmpfile.renameTo(fs)) {
151 if(!tmpfile.renameTo(fs))
152 throw(new IOException("Could not replace previous file contents with new (Java won't tell me why)"));
157 } catch(PrivilegedActionException e) {
158 throw((IOException)e.getCause());
163 private RFile(java.io.File fs, String name) {
168 public String name() {
172 public InputStream read() {
173 return(AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
174 public InputStream run() {
176 return(new FileInputStream(fs));
177 } catch(FileNotFoundException e) {
184 public OutputStream store() {
185 return(new TXStream());
188 public long mtime() {
189 return(AccessController.doPrivileged(new PrivilegedAction<Long>() {
191 return(fs.lastModified());
196 public void remove() {
197 AccessController.doPrivileged(new PrivilegedAction<Object>() {
198 public InputStream run() {
200 throw(new RuntimeException("Could not delete the file " + fs.getPath() + " (Java won't tell me why)"));
207 public File get(String name) {
208 return(new RFile(new java.io.File(base, mangle(name)), name));
211 public Iterator<File> iterator() {
212 final java.io.File[] ls = base.listFiles();
213 return(new Iterator<File>() {
215 private File cur = null;
217 public boolean hasNext() {
218 return(i < ls.length);
222 java.io.File f = ls[i++];
223 cur = new RFile(f, demangle(f.getName()));
227 public void remove() {
229 throw(new IllegalStateException());
236 public static void register() {
237 Store.register("file", new Factory() {
238 public Store create(String rootname, Package pkg) {
239 java.io.File root = new java.io.File(rootname);
240 ThreadContext ctx = ThreadContext.current();
242 if(ctx.server().name() != null)
243 root = new java.io.File(root, ctx.server().name());
245 return(new FileStore(pkg, root));