1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.microemu.cldc.file;
28
29 import java.io.DataInputStream;
30 import java.io.DataOutputStream;
31 import java.io.File;
32 import java.io.FileInputStream;
33 import java.io.FileOutputStream;
34 import java.io.FilenameFilter;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.io.OutputStream;
38 import java.io.RandomAccessFile;
39 import java.lang.reflect.Method;
40 import java.security.AccessControlContext;
41 import java.security.AccessController;
42 import java.security.PrivilegedAction;
43 import java.security.PrivilegedExceptionAction;
44 import java.util.Enumeration;
45 import java.util.Vector;
46
47 import javax.microedition.io.file.ConnectionClosedException;
48 import javax.microedition.io.file.FileConnection;
49
50 public class FileSystemFileConnection implements FileConnection {
51
52 private String fsRootConfig;
53
54 private File fsRoot;
55
56 private String host;
57
58 private String fullPath;
59
60 private File file;
61
62 private boolean isRoot;
63
64 private boolean isDirectory;
65
66 private Throwable locationClosedFrom = null;
67
68 private FileSystemConnectorImpl notifyClosed;
69
70 private InputStream opendInputStream;
71
72 private OutputStream opendOutputStream;
73
74 private final static char DIR_SEP = '/';
75
76 private final static String DIR_SEP_STR = "/";
77
78
79 private AccessControlContext acc;
80
81 private static boolean java15 = false;
82
83 FileSystemFileConnection(String fsRootConfig, String name, FileSystemConnectorImpl notifyClosed) throws IOException {
84
85 int hostEnd = name.indexOf(DIR_SEP);
86 if (hostEnd == -1) {
87 throw new IOException("Invalid path " + name);
88 }
89 this.fsRootConfig = fsRootConfig;
90 this.notifyClosed = notifyClosed;
91
92 host = name.substring(0, hostEnd);
93 fullPath = name.substring(hostEnd + 1);
94 if (fullPath.length() == 0) {
95 throw new IOException("Invalid path " + name);
96 }
97 int rootEnd = fullPath.indexOf(DIR_SEP);
98 isRoot = ((rootEnd == -1) || (rootEnd == fullPath.length() - 1));
99 if (fullPath.charAt(fullPath.length() - 1) == DIR_SEP) {
100 fullPath = fullPath.substring(0, fullPath.length() - 1);
101 }
102 acc = AccessController.getContext();
103 AccessController.doPrivileged(new PrivilegedAction() {
104 public Object run() {
105 fsRoot = getRoot(FileSystemFileConnection.this.fsRootConfig);
106 file = new File(fsRoot, fullPath);
107 isDirectory = file.isDirectory();
108 return null;
109 }
110 }, acc);
111 }
112
113 private Object doPrivilegedIO(PrivilegedExceptionAction action) throws IOException {
114 return FileSystemConnectorImpl.doPrivilegedIO(action, acc);
115 }
116
117 private abstract class PrivilegedBooleanAction implements PrivilegedAction {
118 public Object run() {
119 return new Boolean(getBoolean());
120 }
121
122 abstract boolean getBoolean();
123 }
124
125 private boolean doPrivilegedBoolean(PrivilegedBooleanAction action) {
126 return ((Boolean) AccessController.doPrivileged(action)).booleanValue();
127 }
128
129 public static File getRoot(String fsRootConfig) {
130 try {
131 if (fsRootConfig == null) {
132 File fsRoot = new File(System.getProperty("user.home") + "/.microemulator/filesystem");
133 if (!fsRoot.exists()) {
134 if (!fsRoot.mkdirs()) {
135 throw new RuntimeException("Can't create filesystem root " + fsRoot.getAbsolutePath());
136 }
137
138 File rootC = new File(fsRoot, "c");
139 if (!rootC.exists()) {
140 rootC.mkdirs();
141 }
142 File rootE = new File(fsRoot, "e");
143 if (!rootE.exists()) {
144 rootE.mkdirs();
145 }
146 }
147 return fsRoot;
148 } else {
149 File fsRoot = new File(fsRootConfig);
150 if (!fsRoot.isDirectory()) {
151 throw new RuntimeException("Can't find filesystem root " + fsRoot.getAbsolutePath());
152 }
153 return fsRoot;
154 }
155 } catch (SecurityException e) {
156 System.out.println("Cannot access user.home " + e);
157 return null;
158 }
159 }
160
161 static Enumeration listRoots(String fsRootConfig, String fsSingleConfig) {
162 File[] files;
163 if (fsSingleConfig != null) {
164 files = new File[1];
165 files[0] = getRoot(fsRootConfig + fsSingleConfig);
166 } else {
167 files = getRoot(fsRootConfig).listFiles();
168 if (files == null) {
169 return (new Vector()).elements();
170 }
171 }
172 Vector list = new Vector();
173 for (int i = 0; i < files.length; i++) {
174 File file = files[i];
175 if (file.isHidden()) {
176 continue;
177 }
178 if (file.isDirectory()) {
179 list.add(file.getName() + DIR_SEP);
180 }
181 }
182 return list.elements();
183 }
184
185 public long availableSize() {
186 throwClosed();
187 if (fsRoot == null) {
188 return -1;
189 }
190 return getFileValueJava6("getFreeSpace");
191 }
192
193 public long totalSize() {
194 throwClosed();
195 if (fsRoot == null) {
196 return -1;
197 }
198 return getFileValueJava6("getTotalSpace");
199 }
200
201 public boolean canRead() {
202 throwClosed();
203 return doPrivilegedBoolean(new PrivilegedBooleanAction() {
204 public boolean getBoolean() {
205 return file.canRead();
206 }
207 });
208 }
209
210 public boolean canWrite() {
211 throwClosed();
212 return doPrivilegedBoolean(new PrivilegedBooleanAction() {
213 public boolean getBoolean() {
214 return file.canWrite();
215 }
216 });
217 }
218
219 public void create() throws IOException {
220 throwClosed();
221 doPrivilegedIO(new PrivilegedExceptionAction() {
222 public Object run() throws IOException {
223 if (!file.createNewFile()) {
224 throw new IOException("File already exists " + file.getAbsolutePath());
225 }
226 return null;
227 }
228 });
229 }
230
231 public void delete() throws IOException {
232 throwClosed();
233 doPrivilegedIO(new PrivilegedExceptionAction() {
234 public Object run() throws IOException {
235 if (!file.delete()) {
236 throw new IOException("Unable to delete " + file.getAbsolutePath());
237 }
238 return null;
239 }
240 });
241 }
242
243 public long directorySize(final boolean includeSubDirs) throws IOException {
244 throwClosed();
245 return ((Long) doPrivilegedIO(new PrivilegedExceptionAction() {
246 public Object run() throws IOException {
247 if (!file.isDirectory()) {
248 throw new IOException("Not a directory " + file.getAbsolutePath());
249 }
250 return new Long(directorySize(file, includeSubDirs));
251 }
252 })).longValue();
253 }
254
255 private static long directorySize(File dir, boolean includeSubDirs) throws IOException {
256 long size = 0;
257
258 File[] files = dir.listFiles();
259 if (files == null) {
260 return 0L;
261 }
262 for (int i = 0; i < files.length; i++) {
263 File child = files[i];
264
265 if (includeSubDirs && child.isDirectory()) {
266 size += directorySize(child, true);
267 } else {
268 size += child.length();
269 }
270 }
271
272 return size;
273 }
274
275 public boolean exists() {
276 throwClosed();
277 return doPrivilegedBoolean(new PrivilegedBooleanAction() {
278 public boolean getBoolean() {
279 return file.exists();
280 }
281 });
282 }
283
284 public long fileSize() throws IOException {
285 throwClosed();
286 return ((Long) doPrivilegedIO(new PrivilegedExceptionAction() {
287 public Object run() throws IOException {
288 return new Long(file.length());
289 }
290 })).longValue();
291 }
292
293 public String getName() {
294
295 throwClosed();
296
297 if (isRoot) {
298 return "";
299 }
300
301 if (this.isDirectory) {
302 return this.file.getName() + DIR_SEP;
303 } else {
304 return this.file.getName();
305 }
306 }
307
308 public String getPath() {
309
310 throwClosed();
311
312
313
314 if (isRoot) {
315 return DIR_SEP + fullPath + DIR_SEP;
316 }
317
318 int pathEnd = fullPath.lastIndexOf(DIR_SEP);
319 if (pathEnd == -1) {
320 return DIR_SEP_STR;
321 }
322 return DIR_SEP + fullPath.substring(0, pathEnd + 1);
323 }
324
325 public String getURL() {
326
327 throwClosed();
328
329
330
331
332 return Connection.PROTOCOL + this.host + DIR_SEP + fullPath + ((this.isDirectory) ? DIR_SEP_STR : "");
333 }
334
335 public boolean isDirectory() {
336 throwClosed();
337 return this.isDirectory;
338 }
339
340 public boolean isHidden() {
341 throwClosed();
342 return doPrivilegedBoolean(new PrivilegedBooleanAction() {
343 public boolean getBoolean() {
344 return file.isHidden();
345 }
346 });
347 }
348
349 public long lastModified() {
350 throwClosed();
351 return ((Long) AccessController.doPrivileged(new PrivilegedAction() {
352 public Object run() {
353 return new Long(file.lastModified());
354 }
355 }, acc)).longValue();
356 }
357
358 public void mkdir() throws IOException {
359 throwClosed();
360 doPrivilegedIO(new PrivilegedExceptionAction() {
361 public Object run() throws IOException {
362 if (!file.mkdir()) {
363 throw new IOException("Can't create directory " + file.getAbsolutePath());
364 }
365 return null;
366 }
367 });
368 }
369
370 public Enumeration list() throws IOException {
371 return this.list(null, false);
372 }
373
374 public Enumeration list(final String filter, final boolean includeHidden) throws IOException {
375 throwClosed();
376 return (Enumeration) doPrivilegedIO(new PrivilegedExceptionAction() {
377 public Object run() throws IOException {
378 return listPrivileged(filter, includeHidden);
379 }
380 });
381 }
382
383 private Enumeration listPrivileged(String filter, boolean includeHidden) throws IOException {
384 if (!this.file.isDirectory()) {
385 throw new IOException("Not a directory " + this.file.getAbsolutePath());
386 }
387
388 FilenameFilter filenameFilter = null;
389
390 File[] files = this.file.listFiles(filenameFilter);
391 if (files == null) {
392 return (new Vector()).elements();
393 }
394 Vector list = new Vector();
395 for (int i = 0; i < files.length; i++) {
396 File child = files[i];
397 if ((!includeHidden) && (child.isHidden())) {
398 continue;
399 }
400 if (child.isDirectory()) {
401 list.add(child.getName() + DIR_SEP);
402 } else {
403 list.add(child.getName());
404 }
405 }
406 return list.elements();
407 }
408
409 public boolean isOpen() {
410 return (this.file != null);
411 }
412
413 private void throwOpenDirectory() throws IOException {
414 if (this.isDirectory) {
415 throw new IOException("Unable to open Stream on directory");
416 }
417 }
418
419 public InputStream openInputStream() throws IOException {
420 throwClosed();
421 throwOpenDirectory();
422
423 if (this.opendInputStream != null) {
424 throw new IOException("InputStream already opened");
425 }
426
427
428
429
430 this.opendInputStream = (InputStream) doPrivilegedIO(new PrivilegedExceptionAction() {
431 public Object run() throws IOException {
432 return new FileInputStream(file) {
433 public void close() throws IOException {
434 FileSystemFileConnection.this.opendInputStream = null;
435 super.close();
436 }
437 };
438 }
439 });
440 return this.opendInputStream;
441 }
442
443 public DataInputStream openDataInputStream() throws IOException {
444 return new DataInputStream(openInputStream());
445 }
446
447 public OutputStream openOutputStream() throws IOException {
448 return openOutputStream(false);
449 }
450
451 private OutputStream openOutputStream(final boolean append) throws IOException {
452 throwClosed();
453 throwOpenDirectory();
454
455 if (this.opendOutputStream != null) {
456 throw new IOException("OutputStream already opened");
457 }
458
459
460
461
462 this.opendOutputStream = (OutputStream) doPrivilegedIO(new PrivilegedExceptionAction() {
463 public Object run() throws IOException {
464 return new FileOutputStream(file, append) {
465 public void close() throws IOException {
466 FileSystemFileConnection.this.opendOutputStream = null;
467 super.close();
468 }
469 };
470 }
471 });
472 return this.opendOutputStream;
473 }
474
475 public DataOutputStream openDataOutputStream() throws IOException {
476 return new DataOutputStream(openOutputStream());
477 }
478
479 public OutputStream openOutputStream(long byteOffset) throws IOException {
480 throwClosed();
481 throwOpenDirectory();
482 if (this.opendOutputStream != null) {
483 throw new IOException("OutputStream already opened");
484 }
485 truncate(byteOffset);
486 return openOutputStream(true);
487 }
488
489 public void rename(final String newName) throws IOException {
490 throwClosed();
491 if (newName.indexOf(DIR_SEP) != -1) {
492 throw new IllegalArgumentException("Name contains path specification " + newName);
493 }
494 doPrivilegedIO(new PrivilegedExceptionAction() {
495 public Object run() throws IOException {
496 File newFile = new File(file.getParentFile(), newName);
497 if (!file.renameTo(newFile)) {
498 throw new IOException("Unable to rename " + file.getAbsolutePath() + " to "
499 + newFile.getAbsolutePath());
500 }
501 return null;
502 }
503 });
504 this.fullPath = this.getPath() + newName;
505 }
506
507 public void setFileConnection(String s) throws IOException {
508 throwClosed();
509
510 }
511
512 public void setHidden(boolean hidden) throws IOException {
513 throwClosed();
514 }
515
516 private void fileSetJava16(String mehtodName, final Boolean param) throws IOException {
517 if (java15) {
518 throw new IOException("Not supported on Java version < 6");
519 }
520
521 try {
522 final Method setWritable = file.getClass().getMethod(mehtodName, new Class[] { boolean.class });
523 doPrivilegedIO(new PrivilegedExceptionAction() {
524 public Object run() throws IOException {
525 try {
526 setWritable.invoke(file, new Object[] { param });
527 } catch (Exception e) {
528 throw new IOException(e.getCause().getMessage());
529 }
530 file.setReadOnly();
531 return null;
532 }
533 });
534 } catch (NoSuchMethodException e) {
535 java15 = true;
536 throw new IOException("Not supported on Java version < 6");
537 }
538 }
539
540 private long getFileValueJava6(String mehtodName) throws SecurityException {
541 if (java15) {
542 throw new SecurityException("Not supported on Java version < 6");
543 }
544
545 try {
546 final Method getter = file.getClass().getMethod(mehtodName, new Class[] {});
547 Long rc = (Long) doPrivilegedIO(new PrivilegedExceptionAction() {
548 public Object run() throws IOException {
549 try {
550 return getter.invoke(file, new Object[] {});
551 } catch (Exception e) {
552 throw new IOException(e.getCause().getMessage());
553 }
554 }
555 });
556 return rc.longValue();
557 } catch (IOException e) {
558 throw new SecurityException(e.getMessage());
559 } catch (NoSuchMethodException e) {
560 java15 = true;
561 throw new SecurityException("Not supported on Java version < 6");
562 }
563 }
564
565 public void setReadable(boolean readable) throws IOException {
566 throwClosed();
567 fileSetJava16("setReadable", new Boolean(readable));
568 }
569
570 public void setWritable(boolean writable) throws IOException {
571 throwClosed();
572 if (!writable) {
573 doPrivilegedIO(new PrivilegedExceptionAction() {
574 public Object run() throws IOException {
575 file.setReadOnly();
576 return null;
577 }
578 });
579 } else {
580 fileSetJava16("setWritable", new Boolean(writable));
581 }
582 }
583
584 public void truncate(final long byteOffset) throws IOException {
585 throwClosed();
586 doPrivilegedIO(new PrivilegedExceptionAction() {
587 public Object run() throws IOException {
588 RandomAccessFile raf = new RandomAccessFile(file, "rw");
589 try {
590 raf.setLength(byteOffset);
591 } finally {
592 raf.close();
593 }
594 return null;
595 }
596 });
597 }
598
599 public long usedSize() {
600 try {
601 return fileSize();
602 } catch (IOException e) {
603 return -1;
604 }
605 }
606
607 public void close() throws IOException {
608 if (this.file != null) {
609 if (this.notifyClosed != null) {
610 this.notifyClosed.notifyClosed(this);
611 }
612 locationClosedFrom = new Throwable();
613 locationClosedFrom.fillInStackTrace();
614 this.file = null;
615 }
616 }
617
618 private void throwClosed() throws ConnectionClosedException {
619 if (this.file == null) {
620 if (locationClosedFrom != null) {
621 locationClosedFrom.printStackTrace();
622 }
623 throw new ConnectionClosedException("Connection already closed");
624 }
625 }
626 }