KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > db > store > Inode


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.db.store;
31
32 import com.caucho.util.L10N;
33 import com.caucho.util.Log;
34 import com.caucho.vfs.OutputStreamWithBuffer;
35 import com.caucho.vfs.TempCharBuffer;
36
37 import java.io.IOException JavaDoc;
38 import java.io.InputStream JavaDoc;
39 import java.io.OutputStream JavaDoc;
40 import java.io.Reader JavaDoc;
41 import java.io.Writer JavaDoc;
42 import java.util.logging.Level JavaDoc;
43 import java.util.logging.Logger JavaDoc;
44
45 /**
46  * Represents the indexes for a BLOB or CLOB.
47  *
48  * The inode contains 16 long values
49  * <pre>
50  * 0) length of the saved file
51  * 1-14) direct fragment addresses (to 112k)
52  * 15) pointer to the indirect block
53  * </pre>
54  *
55  * The indirect block (an 8k fragment) itself is divided into sections:
56  * <pre>
57  * 0-511) single indirect fragment addresses (4M)
58  * 512-639) double indirect fragment addresses (1G, 2^30)
59  * 640-767) triple indirect fragment addresses (to 1T, 2^40)
60  * 768-895) quad indirect fragment addresses (to 1P, 2^50)
61  * 896-1023) penta indirect fragment addresses (to 1X, to 2^60)
62  * </pre>
63  */

64 public class Inode {
65   private static final L10N L = new L10N(Inode.class);
66   private static final Logger JavaDoc log = Log.open(Inode.class);
67   
68   public static final int INODE_SIZE = 128;
69   public static final int INLINE_BLOB_SIZE = 120;
70   public static final int INODE_BLOCK_SIZE = Store.FRAGMENT_SIZE;
71
72   public static final int INDIRECT_BLOCKS = INODE_BLOCK_SIZE / 8;
73
74   // direct addresses are stored in the inode itself (112k of data).
75
public static final int DIRECT_BLOCKS = 14;
76   // single indirect addresses are stored in the indirect block (4M data)
77
public static final int SINGLE_INDIRECT_BLOCKS = 512;
78   // double indirect addresses (2^30 = 1G data)
79
public static final int DOUBLE_INDIRECT_BLOCKS = 128;
80   // triple indirect addresses (2^40 = 1T data)
81
public static final int TRIPLE_INDIRECT_BLOCKS = 128;
82   // quad indirect addresses (2^50 = 2P data)
83
public static final int QUAD_INDIRECT_BLOCKS = 128;
84   // penta indirect addresses (2^60 = 2X data)
85
public static final int PENTA_INDIRECT_BLOCKS = 128;
86   
87   private static final byte []NULL_BYTES = new byte[INODE_SIZE];
88
89   private Store _store;
90   private StoreTransaction _xa;
91
92   private byte []_bytes = new byte[INODE_SIZE];
93
94   public Inode()
95   {
96   }
97
98   public Inode(Store store, StoreTransaction xa)
99   {
100     _store = store;
101     _xa = xa;
102   }
103
104   public Inode(Store store)
105   {
106     this(store, RawTransaction.create());
107   }
108
109   /**
110    * Returns the backing store.
111    */

112   public Store getStore()
113   {
114     return _store;
115   }
116   
117   /**
118    * Returns the buffer.
119    */

120   public byte []getBuffer()
121   {
122     return _bytes;
123   }
124
125   /**
126    * Returns the length.
127    */

128   public long getLength()
129   {
130     return readLong(_bytes, 0);
131   }
132
133   public void init(Store store, StoreTransaction xa,
134            byte []buffer, int offset)
135   {
136     _store = store;
137     _xa = xa;
138
139     System.arraycopy(buffer, offset, _bytes, 0, _bytes.length);
140   }
141
142   /**
143    * Opens a read stream to the inode.
144    */

145   public InputStream JavaDoc openInputStream()
146   {
147     return new BlobInputStream(this);
148   }
149
150   /**
151    * Writes the inode value to a stream.
152    */

153   public void writeToStream(OutputStreamWithBuffer os)
154     throws IOException JavaDoc
155   {
156     writeToStream(os, 0, Long.MAX_VALUE / 2);
157   }
158
159   /**
160    * Writes the inode value to a stream.
161    */

162   public void writeToStream(OutputStreamWithBuffer os,
163                 long offset, long length)
164     throws IOException JavaDoc
165   {
166     byte []buffer = os.getBuffer();
167     int writeLength = buffer.length;
168     int writeOffset = os.getBufferOffset();
169     
170     while (length > 0) {
171       int sublen = writeLength - writeOffset;
172
173       if (sublen == 0) {
174     buffer = os.nextBuffer(writeOffset);
175     writeOffset = os.getBufferOffset();
176     sublen = writeLength - writeOffset;
177       }
178
179       if (length < sublen)
180     sublen = (int) length;
181
182       int len = read(_bytes, 0, _store,
183              offset,
184              buffer, writeOffset, sublen);
185
186       if (len <= 0)
187     break;
188
189       writeOffset += len;
190       offset += len;
191       length -= len;
192     }
193
194     os.setBufferOffset(writeOffset);
195   }
196
197   /**
198    * Writes the inode value to a stream.
199    */

200   public void writeToWriter(Writer JavaDoc writer)
201     throws IOException JavaDoc
202   {
203     TempCharBuffer tempBuffer = TempCharBuffer.allocate();
204
205     char []buffer = tempBuffer.getBuffer();
206     int writeLength = buffer.length;
207     long offset = 0;
208     
209     while (true) {
210       int sublen = writeLength;
211
212       int len = read(_bytes, 0, _store,
213              offset,
214              buffer, 0, sublen);
215
216       if (len <= 0)
217     break;
218
219       writer.write(buffer, 0, len);
220
221       offset += 2 * len;
222     }
223
224     TempCharBuffer.free(tempBuffer);
225   }
226
227   /**
228    * Reads into a buffer.
229    *
230    * @param inode the inode buffer
231    * @param inodeOffset the offset of the inode data in the buffer
232    * @param store the owning store
233    * @param fileOffset the offset into the file to read
234    * @param buffer the buffer receiving the data
235    * @param bufferOffset the offset into the receiving buffer
236    * @param bufferLength the maximum number of bytes to read
237    *
238    * @return the number of bytes read
239    */

240   static int read(byte []inode, int inodeOffset,
241           Store store,
242           long fileOffset,
243           byte []buffer, int bufferOffset, int bufferLength)
244     throws IOException JavaDoc
245   {
246     long fileLength = readLong(inode, inodeOffset);
247
248     int sublen = bufferLength;
249     if (fileLength - fileOffset < sublen)
250       sublen = (int) (fileLength - fileOffset);
251       
252     if (sublen <= 0)
253       return -1;
254
255     if (fileLength <= Inode.INLINE_BLOB_SIZE) {
256       System.arraycopy(inode, inodeOffset + 8 + (int) fileOffset,
257                buffer, bufferOffset, sublen);
258
259       return sublen;
260     }
261
262     long fragAddr = readFragmentAddr(inode, inodeOffset, store, fileOffset);
263     int fragOffset = (int) (fileOffset % Inode.INODE_BLOCK_SIZE);
264
265     if (INODE_BLOCK_SIZE - fragOffset < sublen)
266       sublen = INODE_BLOCK_SIZE - fragOffset;
267
268     store.readFragment(fragAddr, fragOffset, buffer, bufferOffset, sublen);
269     
270     return sublen;
271   }
272   
273   /**
274    * Updates the buffer. Called only from the blob classes.
275    */

276   static void append(byte []inode, int inodeOffset,
277              Store store, StoreTransaction xa,
278              byte []buffer, int offset, int length)
279     throws IOException JavaDoc
280   {
281     long currentLength = readLong(inode, inodeOffset);
282     long newLength = currentLength + length;
283
284     writeLong(inode, inodeOffset, newLength);
285
286     if (newLength <= INLINE_BLOB_SIZE) {
287       System.arraycopy(buffer, offset,
288                inode, (int) (inodeOffset + 8 + currentLength),
289                length);
290     }
291     else {
292       // XXX: theoretically deal with case of appending to inline, although
293
// the blobs are the only writers and will avoid that case.
294

295       if (currentLength % INODE_BLOCK_SIZE != 0) {
296     long fragAddr = readFragmentAddr(inode, inodeOffset,
297                      store,
298                      currentLength);
299
300     if (fragAddr == 0)
301       throw new IllegalStateException JavaDoc("inode: illegal fragment at " + currentLength);
302
303     int fragOffset = (int) (currentLength % INODE_BLOCK_SIZE);
304     int sublen = length;
305
306     if (INODE_BLOCK_SIZE - fragOffset < sublen)
307       sublen = INODE_BLOCK_SIZE - fragOffset;
308
309     store.writeFragment(xa, fragAddr, fragOffset, buffer, offset, sublen);
310
311     offset += sublen;
312     length -= sublen;
313
314     currentLength += sublen;
315       }
316       
317       while (length > 0) {
318     int sublen = length;
319
320     if (INODE_BLOCK_SIZE < sublen)
321       sublen = INODE_BLOCK_SIZE;
322
323     long fragAddr = store.allocateFragment(xa);
324
325     if (fragAddr == 0)
326       throw new IllegalStateException JavaDoc(L.l("illegal fragment"));
327
328     writeFragmentAddr(inode, inodeOffset,
329               store, xa,
330               currentLength, fragAddr);
331
332     store.writeFragment(xa, fragAddr, 0, buffer, offset, sublen);
333
334     offset += sublen;
335     length -= sublen;
336
337     currentLength += sublen;
338       }
339     }
340   }
341
342   /**
343    * Reads into a buffer.
344    *
345    * @param inode the inode buffer
346    * @param inodeOffset the offset of the inode data in the buffer
347    * @param store the owning store
348    * @param fileOffset the offset into the file to read
349    * @param buffer the buffer receiving the data
350    * @param bufferOffset the offset into the receiving buffer
351    * @param bufferLength the maximum number of chars to read
352    *
353    * @return the number of characters read
354    */

355   static int read(byte []inode, int inodeOffset, Store store,
356           long fileOffset,
357           char []buffer, int bufferOffset, int bufferLength)
358     throws IOException JavaDoc
359   {
360     long fileLength = readLong(inode, inodeOffset);
361
362     int sublen = (int) (fileLength - fileOffset) / 2;
363     if (bufferLength < sublen)
364       sublen = bufferLength;
365       
366     if (sublen <= 0)
367       return -1;
368
369     if (fileLength <= Inode.INLINE_BLOB_SIZE) {
370       int baseOffset = inodeOffset + 8 + (int) fileOffset;
371
372       for (int i = 0; i < sublen; i++) {
373     char ch = (char) (((inode[baseOffset] & 0xff) << 8) +
374               ((inode[baseOffset + 1] & 0xff)));
375
376     buffer[bufferOffset + i] = ch;
377
378     baseOffset += 2;
379       }
380
381       return sublen;
382     }
383
384     long fragAddr = readFragmentAddr(inode, inodeOffset, store, fileOffset);
385     int fragOffset = (int) (fileOffset % Inode.INODE_BLOCK_SIZE);
386
387     if (INODE_BLOCK_SIZE - fragOffset < 2 * sublen)
388       sublen = (INODE_BLOCK_SIZE - fragOffset) / 2;
389
390     store.readFragment(fragAddr, fragOffset, buffer, bufferOffset, sublen);
391     
392     return sublen;
393   }
394
395   /**
396    * Updates the buffer. Called only from the clob classes.
397    */

398   static void append(byte []inode, int inodeOffset,
399              Store store, StoreTransaction xa,
400              char []buffer, int offset, int length)
401     throws IOException JavaDoc
402   {
403     long currentLength = readLong(inode, inodeOffset);
404     long newLength = currentLength + length;
405     
406     writeLong(inode, inodeOffset, newLength);
407
408     if (newLength <= INLINE_BLOB_SIZE) {
409       int writeOffset = (int) (inodeOffset + 8 + currentLength);
410       
411       for (int i = 0; i < length; i++) {
412     char ch = buffer[offset + i];
413
414     inode[writeOffset++] = (byte) (ch >> 8);
415     inode[writeOffset++] = (byte) (ch);
416       }
417     }
418     else {
419       // XXX: theoretically deal with case of appending to inline, although
420
// the blobs are the only writers and will avoid that case.
421

422       if (currentLength % INODE_BLOCK_SIZE != 0) {
423     long fragAddr = readFragmentAddr(inode, inodeOffset,
424                      store,
425                      currentLength);
426
427     int fragOffset = (int) (currentLength % INODE_BLOCK_SIZE);
428     int sublen = 2 * length;
429
430     if (INODE_BLOCK_SIZE - fragOffset < sublen)
431       sublen = INODE_BLOCK_SIZE - fragOffset;
432
433     store.writeFragment(xa, fragAddr, fragOffset, buffer, offset, sublen);
434
435     offset += sublen / 2;
436     length -= sublen / 2;
437
438     currentLength += sublen;
439       }
440       
441       while (length > 0) {
442     int sublen = 2 * length;
443
444     if (INODE_BLOCK_SIZE < sublen)
445       sublen = INODE_BLOCK_SIZE;
446
447     long fragAddr = store.allocateFragment(xa);
448
449     writeFragmentAddr(inode, inodeOffset,
450               store, xa,
451               currentLength, fragAddr);
452
453     store.writeFragment(xa, fragAddr, 0, buffer, offset, sublen);
454
455     offset += sublen / 2;
456     length -= sublen / 2;
457
458     currentLength += sublen;
459       }
460     }
461   }
462
463   /**
464    * Opens a byte output stream to the inode.
465    */

466   public OutputStream JavaDoc openOutputStream()
467   {
468     return new BlobOutputStream(this);
469   }
470
471   /**
472    * Closes the output stream.
473    */

474   void closeOutputStream()
475   {
476     try {
477       _store.saveAllocation();
478     } catch (Throwable JavaDoc e) {
479       log.log(Level.FINER, e.toString(), e);
480     }
481   }
482
483   /**
484    * Opens a char reader to the inode.
485    */

486   public Reader JavaDoc openReader()
487   {
488     return new ClobReader(this);
489   }
490
491   /**
492    * Opens a char writer to the inode.
493    */

494   public Writer JavaDoc openWriter()
495   {
496     return new ClobWriter(this);
497   }
498
499   /**
500    * Deletes the inode
501    */

502   public void remove()
503   {
504     synchronized (_bytes) {
505       long length = readLong(_bytes, 0);
506
507       byte []bytes = _bytes;
508
509       try {
510     if (length <= INLINE_BLOB_SIZE || bytes == null)
511       return;
512
513     long initLength = length;
514     for (; length > 0; length -= INODE_BLOCK_SIZE) {
515       long fragAddr = readFragmentAddr(bytes, 0, _store, length - 1);
516
517       if ((fragAddr & Store.BLOCK_MASK) == 0) {
518         String JavaDoc msg = _store + ": inode block " + Long.toHexString(length) + " has 0 fragment";
519         throw stateError(msg);
520       }
521       else if (fragAddr < 0) {
522         String JavaDoc msg = _store + ": inode block " + Long.toHexString(length) + " has invalid fragment " + Long.toHexString(fragAddr);
523         
524         throw stateError(msg);
525       }
526
527       _store.deleteFragment(_xa, fragAddr);
528
529       int fragCount = (int) ((length - 1) / INODE_BLOCK_SIZE);
530
531       int dblFragCount = fragCount - DIRECT_BLOCKS - SINGLE_INDIRECT_BLOCKS;
532
533       // remove the double indirect blocks
534
if (dblFragCount >= 0 &&
535           dblFragCount % INDIRECT_BLOCKS == 0) {
536         fragAddr = readLong(bytes, (DIRECT_BLOCKS + 1) * 8);
537         
538         int dblIndex = (int) (fragCount / INDIRECT_BLOCKS);
539
540         fragAddr = _store.readFragmentLong(fragAddr, dblIndex);
541
542         if (fragAddr != 0)
543           _store.deleteFragment(_xa, fragAddr);
544       }
545
546       // remove the indirect blocks
547
if (fragCount == DIRECT_BLOCKS) {
548         fragAddr = readLong(bytes, (DIRECT_BLOCKS + 1) * 8);
549
550         if (fragAddr != 0) {
551           _store.deleteFragment(_xa, fragAddr);
552         }
553       }
554     }
555       } catch (Throwable JavaDoc e) {
556     log.log(Level.WARNING, e.toString(), e);
557       } finally {
558     System.arraycopy(NULL_BYTES, 0, _bytes, 0, NULL_BYTES.length);
559
560     try {
561       _store.saveAllocation();
562     } catch (Throwable JavaDoc e) {
563       log.log(Level.FINE, e.toString(), e);
564     }
565       }
566     }
567   }
568
569   /**
570    * Clears the inode.
571    */

572   static void clear(byte []inode, int inodeOffset)
573   {
574     int end = inodeOffset + INODE_SIZE;
575
576     for (; inodeOffset < end; inodeOffset++)
577       inode[inodeOffset] = 0;
578   }
579
580   /**
581    * Returns the fragment id for the given offset.
582    */

583   static long readFragmentAddr(byte []inode, int inodeOffset,
584                    Store store,
585                    long fileOffset)
586     throws IOException JavaDoc
587   {
588     long fragCount = fileOffset / INODE_BLOCK_SIZE;
589     
590     if (fragCount < DIRECT_BLOCKS)
591       return readLong(inode, (int) (inodeOffset + 8 * (1 + fragCount)));
592     else if (fragCount < DIRECT_BLOCKS + SINGLE_INDIRECT_BLOCKS) {
593       long indirectAddr;
594       indirectAddr = readLong(inode, inodeOffset + (DIRECT_BLOCKS + 1) * 8);
595
596       if (indirectAddr == 0)
597     throw new IllegalStateException JavaDoc(L.l("null block id"));
598
599       int offset = (int) (8 * (fragCount - DIRECT_BLOCKS));
600
601       long fragAddr = store.readFragmentLong(indirectAddr, offset);
602
603       return fragAddr;
604     }
605     else if (fragCount < (DIRECT_BLOCKS +
606               SINGLE_INDIRECT_BLOCKS +
607               DOUBLE_INDIRECT_BLOCKS * INDIRECT_BLOCKS)) {
608       long indirectAddr;
609       indirectAddr = readLong(inode, inodeOffset + (DIRECT_BLOCKS + 1) * 8);
610
611       if (indirectAddr == 0)
612     throw new IllegalStateException JavaDoc(L.l("null block id"));
613
614       fragCount -= DIRECT_BLOCKS + SINGLE_INDIRECT_BLOCKS;
615
616       int index = (int) (fragCount / INDIRECT_BLOCKS);
617       
618       long doubleIndirectAddr = store.readFragmentLong(indirectAddr, index);
619                       
620       int offset = (int) (8 * (fragCount % INDIRECT_BLOCKS));
621
622       return store.readFragmentLong(doubleIndirectAddr, offset);
623     }
624     else
625       throw new IllegalStateException JavaDoc(L.l("Can't yet support data over 64M"));
626   }
627
628   /**
629    * Writes the block id into the inode.
630    */

631   private static void writeFragmentAddr(byte []inode, int offset,
632                     Store store, StoreTransaction xa,
633                     long fragLength, long fragAddr)
634     throws IOException JavaDoc
635   {
636     int fragCount = (int) (fragLength / Store.FRAGMENT_SIZE);
637     
638     // XXX: not sure if correct, needs XA?
639
if ((fragAddr & Store.BLOCK_MASK) == 0) {
640       String JavaDoc msg = store + ": inode block " + fragCount + " writing 0 fragment";
641       throw stateError(msg);
642     }
643
644     if (fragCount < DIRECT_BLOCKS) {
645       writeLong(inode, offset + (fragCount + 1) * 8, fragAddr);
646     }
647     else if (fragCount < DIRECT_BLOCKS + SINGLE_INDIRECT_BLOCKS) {
648       long indAddr = readLong(inode, offset + (DIRECT_BLOCKS + 1) * 8);
649
650       if (indAddr == 0) {
651     indAddr = store.allocateFragment(xa);
652
653     writeLong(inode, offset + (DIRECT_BLOCKS + 1) * 8, indAddr);
654       }
655
656       int fragOffset = 8 * (fragCount - DIRECT_BLOCKS);
657       
658       store.writeFragmentLong(xa, indAddr, fragOffset, fragAddr);
659     }
660     else if (fragCount < (DIRECT_BLOCKS +
661               SINGLE_INDIRECT_BLOCKS +
662               DOUBLE_INDIRECT_BLOCKS * INDIRECT_BLOCKS)) {
663       long indAddr = readLong(inode, offset + (DIRECT_BLOCKS + 1) * 8);
664
665       if (indAddr == 0) {
666     indAddr = store.allocateFragment(xa);
667
668     writeLong(inode, offset + (DIRECT_BLOCKS + 1) * 8, indAddr);
669       }
670
671       int count = fragCount - DIRECT_BLOCKS - SINGLE_INDIRECT_BLOCKS;
672
673       int dblIndCount = count / INDIRECT_BLOCKS;
674
675       long dblIndAddr = store.readFragmentLong(indAddr, dblIndCount * 8);
676
677       if (dblIndAddr == 0) {
678     dblIndAddr = store.allocateFragment(xa);
679
680     store.writeFragmentLong(xa, indAddr, dblIndCount * 8, dblIndAddr);
681       }
682
683       int fragOffset = 8 * (count % INDIRECT_BLOCKS);
684       
685       store.writeFragmentLong(xa, dblIndAddr, fragOffset, fragAddr);
686     }
687     else
688       throw new IllegalStateException JavaDoc(L.l("Can't yet support data over 64M"));
689   }
690
691   /**
692    * Reads the long.
693    */

694   public static long readLong(byte []buffer, int offset)
695   {
696     return (((buffer[offset + 0] & 0xffL) << 56) +
697         ((buffer[offset + 1] & 0xffL) << 48) +
698         ((buffer[offset + 2] & 0xffL) << 40) +
699         ((buffer[offset + 3] & 0xffL) << 32) +
700         ((buffer[offset + 4] & 0xffL) << 24) +
701         ((buffer[offset + 5] & 0xffL) << 16) +
702         ((buffer[offset + 6] & 0xffL) << 8) +
703         ((buffer[offset + 7] & 0xffL)));
704   }
705
706   /**
707    * Writes the long.
708    */

709   public static void writeLong(byte []buffer, int offset, long v)
710   {
711     buffer[offset + 0] = (byte) (v >> 56);
712     buffer[offset + 1] = (byte) (v >> 48);
713     buffer[offset + 2] = (byte) (v >> 40);
714     buffer[offset + 3] = (byte) (v >> 32);
715     
716     buffer[offset + 4] = (byte) (v >> 24);
717     buffer[offset + 5] = (byte) (v >> 16);
718     buffer[offset + 6] = (byte) (v >> 8);
719     buffer[offset + 7] = (byte) (v);
720   }
721
722   /**
723    * Reads the short.
724    */

725   private static int readShort(byte []buffer, int offset)
726   {
727     return (((buffer[offset + 0] & 0xff) << 8) +
728         ((buffer[offset + 1] & 0xff)));
729   }
730
731   /**
732    * Writes the short.
733    */

734   private static void writeShort(byte []buffer, int offset, int v)
735   {
736     buffer[offset + 0] = (byte) (v >> 8);
737     buffer[offset + 1] = (byte) v;
738   }
739
740   private static IllegalStateException JavaDoc stateError(String JavaDoc msg)
741   {
742     IllegalStateException JavaDoc e = new IllegalStateException JavaDoc(msg);
743     e.fillInStackTrace();
744     log.log(Level.WARNING, e.toString(), e);
745     return e;
746   }
747 }
748
Popular Tags