Source code: com/neuron/jaffer/OS_Volume.java
1 /*
2 * Copyright (c) 2003 Stewart Allen <stewart@neuron.com>. All rights reserved.
3 * This program is free software. See the 'License' file for details.
4 */
5
6 package com.neuron.jaffer;
7
8 import java.io.*;
9 import java.net.*;
10 import java.util.*;
11
12 class OS_Volume extends AFP_Volume
13 {
14 private Hashtable allNodes;
15 private String volName;
16 private File rootDir;
17 private int nextID = 32;
18 private boolean readonly;
19 private String passwd;
20
21 OS_Volume(String vname, File droot)
22 {
23 this.volName = vname;
24 this.rootDir = droot;
25 this.allNodes = new Hashtable();
26
27 OSNode root = new OSNode(0, 1, "", null);
28 OSNode vol = new OSNode(1, 2, vname, droot);
29
30 root.addOffspring(vol);
31 addNode(root);
32 }
33
34 // custom methods
35 public void setReadOnly(boolean ro)
36 {
37 this.readonly = ro;
38 }
39
40 public void setPassword(String pwd)
41 {
42 this.passwd = pwd;
43 }
44
45 private synchronized int getNextID()
46 {
47 return nextID++;
48 }
49
50 private void addNode(OSNode node)
51 {
52 allNodes.put(new Integer(node.getNodeID()), node);
53 }
54
55 // abstract methods
56 public String getName()
57 {
58 return volName;
59 }
60
61 public int getCreateDate()
62 {
63 return 0xa;
64 }
65
66 public int getModifiedDate()
67 {
68 return 0xb;
69 }
70
71 public int getBackupDate()
72 {
73 return 0x80000000;
74 }
75
76 public void setBackupDate(int d)
77 {
78 }
79
80 public void setModifiedDate(int d)
81 {
82 }
83
84 public int getAttributes()
85 {
86 return
87 (readonly ? VOL_ATTR_READONLY : 0) |
88 (getPassword() != null ? VOL_ATTR_PASSWORD : 0) |
89 0;
90 }
91
92 public void setAttributes(int attr)
93 {
94 }
95
96 public int getSignature()
97 {
98 return VOL_SIG_FIXED;
99 //return VOL_SIG_VARIABLE;
100 }
101
102 public int getBlockSize()
103 {
104 return 0x1000;
105 }
106
107 public int getBytesFree()
108 {
109 return 0x55555555;
110 }
111
112 public int getBytesTotal()
113 {
114 return 0x66666666;
115 }
116
117 public long getExtBytesFree()
118 {
119 return 0x4545454545l;
120 }
121
122 public long getExtBytesTotal()
123 {
124 return 0x4646464646l;
125 }
126
127 public boolean hasUnixPrivs()
128 {
129 return false;
130 }
131
132 public String getPassword()
133 {
134 return passwd;
135 }
136
137 public AFP_CNode getCNode(int id)
138 {
139 return (AFP_CNode)allNodes.get(new Integer(id));
140 }
141
142 // utility methods
143 public String flagsToString(int flags)
144 {
145 if (Utility.hasBits(flags, AFP_CNode.MODE_WRITE) && !readonly)
146 {
147 return "rw";
148 }
149 else
150 {
151 return "r";
152 }
153 }
154
155 private boolean canOpen(File file, int flags)
156 {
157 boolean can = false;
158 if (Utility.hasBits(flags, AFP_CNode.MODE_WRITE))
159 {
160 can = file.exists() ? file.canWrite() : file.getParentFile().canWrite();
161 }
162 else
163 {
164 can = file.exists() && file.canRead();
165 }
166 return can;
167 }
168
169 // -------------------------------------------------------------------------
170
171 private class OSNode extends AFP_CNode
172 {
173 private int pid;
174 private Hashtable nodes;
175 private String name;
176 private File file;
177 private byte[] finderInfo;
178 private long resourceLength;
179 private volatile long modDate;
180
181 OSNode(int pid, int id, String name, File file)
182 {
183 super(id);
184 this.pid = pid;
185 this.file = file;
186 this.name = name;
187 this.nodes = new Hashtable();
188 this.resourceLength = -1;
189 }
190
191 // -- custom methods --
192 public void setParentID(int id)
193 {
194 this.pid = id;
195 }
196
197 public synchronized void addOffspring(OSNode nn)
198 {
199 if (nodes == null)
200 {
201 nodes = new Hashtable();
202 }
203 nn.setParentID(getNodeID());
204 nodes.put(nn.getRealName(), nn);
205 addNode(nn);
206 }
207
208 public synchronized OSNode addOffspring(String name, File file)
209 {
210 if (nodes == null)
211 {
212 nodes = new Hashtable();
213 }
214 OSNode nn = new OSNode(getNodeID(), getNextID(), name, file);
215 nodes.put(name, nn);
216 addNode(nn);
217 return nn;
218 }
219
220 private void saveResourceInfo()
221 {
222 if (finderInfo == null && resourceLength < 0)
223 {
224 return;
225 }
226 try
227 {
228 ResourceFork rf = getResourceFork(MODE_WRITE);
229 if (rf == null)
230 {
231 return;
232 }
233 if (finderInfo != null)
234 {
235 rf.writeFinderInfo(finderInfo);
236 }
237 /*
238 if (resourceLength >= 0)
239 {
240 rf.setLength(resourceLength);
241 }
242 */
243 rf.close();
244 }
245 catch (Exception ex)
246 {
247 ex.printStackTrace();
248 }
249 }
250
251 private void cacheResourceInfo()
252 {
253 if (finderInfo != null && resourceLength >= 0)
254 {
255 return;
256 }
257 try
258 {
259 ResourceFork rf = getResourceFork(MODE_READ);
260 if (rf == null)
261 {
262 return;
263 }
264 if (finderInfo == null)
265 {
266 finderInfo = new byte[16];
267 }
268 rf.readFinderInfo(finderInfo);
269 resourceLength = rf.getLength();
270 rf.close();
271 }
272 catch (Exception ex)
273 {
274 ex.printStackTrace();
275 }
276 }
277
278 public synchronized void scanDir()
279 {
280 if (file == null)
281 {
282 return;
283 }
284 long lm = file.lastModified();
285 if (lm <= modDate)
286 {
287 return;
288 }
289 modDate = lm;
290 String f[] = file.list();
291 if (f == null || f.length == 0)
292 {
293 nodes.clear();
294 return;
295 }
296 Hashtable tmp = new Hashtable(f.length*2);
297 // add nodes for unknown keys
298 for (int i=0; i<f.length; i++)
299 {
300 if (f[i].startsWith("._"))
301 {
302 continue;
303 }
304 File nn = new File(file, f[i]);
305 // skip unknowns
306 if (!(nn.isFile() || nn.isDirectory()))
307 {
308 continue;
309 }
310 if (nodes.get(f[i]) == null)
311 {
312 addOffspring(f[i], nn);
313 }
314 tmp.put(f[i], f[i]);
315 }
316 // remove stale handles
317 for (Enumeration e = nodes.keys(); e.hasMoreElements(); )
318 {
319 Object key = e.nextElement();
320 if (tmp.get(key) == null)
321 {
322 nodes.remove(key);
323 }
324 }
325 }
326
327 public String getRealName()
328 {
329 return name;
330 }
331
332
333 public String toString()
334 {
335 return "'"+name+"' {"+getNodeID()+"} "+nodes+" ";
336 }
337
338 private ResourceFork getResourceFork(int flags)
339 {
340 return (ResourceFork)openResourceFork(flags);
341 }
342
343 private File getResourceForkFile()
344 {
345 return new File(file.getParentFile(), "._"+file.getName());
346 }
347
348 // abstract methods
349 public int getNodeType()
350 {
351 if (getNodeID() < 32) { return NODE_DIRECTORY; }
352 if (file == null) { return NODE_UNKNOWN; }
353 if (file.isFile()) { return NODE_FILE; }
354 if (file.isDirectory()) { return NODE_DIRECTORY; }
355 return NODE_UNKNOWN;
356 }
357
358 public boolean delete()
359 {
360 OSNode parent = (OSNode)getCNode(getParentNodeID());
361 if (parent != null && file.delete())
362 {
363 getResourceForkFile().delete();
364 parent.modDate = 0;
365 return true;
366 }
367 else
368 {
369 return false;
370 }
371 }
372
373 public int getParentNodeID()
374 {
375 return pid;
376 }
377
378 public int getOffspringCount()
379 {
380 scanDir();
381 return nodes.size();
382 }
383
384 public int getAttributes()
385 {
386 return 0;
387 }
388
389 public void setAttributes(int attr)
390 {
391 }
392
393 public int getOwnerID()
394 {
395 return 0;
396 }
397
398 public int getGroupID()
399 {
400 return 0;
401 }
402
403 public int getCreateDate()
404 {
405 return Utility.unix2afpTime(file.lastModified());
406 }
407
408 public void setCreateDate(int date)
409 {
410 }
411
412 public int getModifiedDate()
413 {
414 return Utility.unix2afpTime(file.lastModified());
415 }
416
417 public void setModifiedDate(int date)
418 {
419 file.setLastModified(Utility.afp2unixTime(date));
420 }
421
422 public int getBackupDate()
423 {
424 return 0x80000000;
425 }
426
427 public void setBackupDate(int date)
428 {
429 }
430
431 public byte[] getFinderInfo()
432 {
433 cacheResourceInfo();
434 return finderInfo;
435 }
436
437 public void setFinderInfo(byte b[])
438 {
439 finderInfo = b;
440 saveResourceInfo();
441 }
442
443 public String getLongName()
444 {
445 return name;
446 }
447
448 public String getShortName()
449 {
450 return name;
451 }
452
453 public String getUTF8Name()
454 {
455 return name;
456 }
457
458 public byte[] getUnixPrivs()
459 {
460 return null;
461 }
462
463 public void setUnixPrivs(byte b[])
464 {
465 }
466
467 public int getAccessRights()
468 {
469 boolean read = file.canRead();
470 boolean write = file.canWrite();
471 //System.out.println("Access on "+file+" read="+read+" write="+write);
472 return ACCESS_EVERYTHING ^ (
473 (read ? 0 : ACCESS_OWNER_READ | ACCESS_GROUP_READ | ACCESS_ALL_READ | ACCESS_UA_READ ) |
474 (write ? 0 : ACCESS_OWNER_WRITE | ACCESS_GROUP_WRITE | ACCESS_ALL_WRITE | ACCESS_UA_WRITE)
475 );
476 }
477
478 public synchronized boolean moveTo(AFP_CNode dir, String name)
479 {
480 if (!dir.isDirectory())
481 {
482 return false;
483 }
484 File targetFile = ((OSNode)dir).file;
485 File newFile = empty(name) ? targetFile : new File(targetFile, name);
486 if (file.renameTo(newFile))
487 {
488 OSNode parent = (OSNode)getCNode(getParentNodeID());
489 if (parent != null)
490 {
491 parent.modDate = 0;
492 }
493 getResourceForkFile().renameTo(new File(newFile.getParentFile(), "._"+newFile.getName()));
494 file = newFile;
495 return true;
496 }
497 else
498 {
499 return false;
500 }
501 }
502
503 public AFP_CNode createDirectory(String name)
504 {
505 AFP_CNode ndir = getOffspringByName(name);
506 if (ndir != null)
507 {
508 return null;
509 }
510 File nfdir = new File(file, name);
511 if (nfdir.mkdirs())
512 {
513 return addOffspring(name, nfdir);
514 }
515 else
516 {
517 return null;
518 }
519 }
520
521 public AFP_CNode createFile(String name)
522 {
523 AFP_CNode ndir = getOffspringByName(name);
524 if (ndir != null)
525 {
526 return null;
527 }
528 File nfile = new File(file, name);
529 try
530 {
531 if (nfile.createNewFile())
532 {
533 return addOffspring(name, nfile);
534 }
535 else
536 {
537 return null;
538 }
539 }
540 catch (Exception ex)
541 {
542 return null;
543 }
544 }
545
546 public AFP_CNode getOffspringByName(String name)
547 {
548 scanDir();
549 return (AFP_CNode)nodes.get(name);
550 }
551
552 public Enumeration getOffspringEnumeration()
553 {
554 scanDir();
555 return nodes.elements();
556 }
557
558 public int getLaunchLimit()
559 {
560 return 0;
561 }
562
563 public int getDataForkLen()
564 {
565 return (int)Math.min(file.length(), 0xffffffffl);
566 }
567
568 public int getResourceForkLen()
569 {
570 cacheResourceInfo();
571 return (int)Math.min(resourceLength, 0xffffffffl);
572 }
573
574 public long getExtDataForkLen()
575 {
576 return file.length();
577 }
578
579 public long getExtResourceForkLen()
580 {
581 cacheResourceInfo();
582 return resourceLength;
583 }
584
585 public AFP_Fork openFileFork(int flags)
586 {
587 if (!canOpen(file, flags))
588 {
589 return null;
590 }
591 try
592 {
593 return new DataFork(file, flags);
594 }
595 catch (Exception ex)
596 {
597 ex.printStackTrace();
598 return null;
599 }
600 }
601
602 public AFP_Fork openResourceFork(int flags)
603 {
604 // you can't create a resource fork if you don't have
605 // the same priviledges on the data fork
606 if (getNodeID() < 32 || !canOpen(getResourceForkFile(), flags) || !canOpen(file, flags))
607 {
608 return null;
609 }
610 try
611 {
612 return new ResourceFork(getResourceForkFile(), flags);
613 }
614 catch (Exception ex)
615 {
616 ex.printStackTrace();
617 return null;
618 }
619 }
620 }
621
622 // -------------------------------------------------------------------------
623
624 private class ResourceFork extends DataFork
625 {
626 private File file;
627
628 ResourceFork(File file, int flags)
629 throws IOException
630 {
631 super(new AppleDouble(file, flagsToString(flags)));
632 this.file = file;
633 }
634
635 public File file()
636 {
637 return file;
638 }
639
640 public void renameTo(File dst)
641 {
642 if (file.renameTo(dst))
643 {
644 file = dst;
645 }
646 }
647
648 public int getForkType()
649 {
650 return FORK_RESOURCE;
651 }
652
653 public void readFinderInfo(byte b[])
654 throws IOException
655 {
656 ((AppleDouble)nativeFile()).readFinderInfo(b);
657 }
658
659 public void writeFinderInfo(byte b[])
660 throws IOException
661 {
662 ((AppleDouble)nativeFile()).writeFinderInfo(b);
663 }
664 }
665
666 // -------------------------------------------------------------------------
667
668 private class DataFork extends AFP_Fork
669 {
670 private RandomAccessFile file;
671
672 DataFork(File file, int flags)
673 throws IOException
674 {
675 this(new RandomAccessFile(file, flagsToString(flags)));
676 }
677
678 DataFork(RandomAccessFile file)
679 throws IOException
680 {
681 this.file = file;
682 }
683
684 public RandomAccessFile nativeFile()
685 {
686 return file;
687 }
688
689 public int getForkType()
690 {
691 return FORK_DATA;
692 }
693
694 public void readRange(long offset, long length, ByteWriter ww)
695 throws IOException
696 {
697 length = Math.min(getLength() - offset, length);
698 if (length < 0 || offset < 0)
699 {
700 throw new EOFException();
701 }
702 file.seek(offset);
703 ww.readFromFile(file, length);
704 }
705
706 public long writeRange(long offset, long length, ByteReader rr)
707 throws IOException
708 {
709 file.seek(offset);
710 return rr.writeToFile(file, length);
711 }
712
713 public long getLength()
714 throws IOException
715 {
716 return file.length();
717 }
718
719 public void setLength(long len)
720 throws IOException
721 {
722 file.setLength(len);
723 }
724
725 public void flush()
726 throws IOException
727 {
728 file.getFD().sync();
729 }
730
731 public void close()
732 {
733 try
734 {
735 file.close();
736 }
737 catch (Exception ex)
738 {
739 ex.printStackTrace();
740 }
741 }
742 }
743 }
744