Author: toad
Date: 2007-08-08 19:57:51 +0000 (Wed, 08 Aug 2007)
New Revision: 14536
Modified:
trunk/freenet/src/freenet/client/ArchiveManager.java
Log:
Other half of work on delete-before-use race: don't add the data to the LRU
until we have taken a copy-pointer (aka reader bucket).
Some refactoring.
Also makes accessing generated sub-metadata from extractMetadata() via
callbacks work (before this commit, really large implicit containers would have
gotten into some trouble).
Modified: trunk/freenet/src/freenet/client/ArchiveManager.java
===================================================================
--- trunk/freenet/src/freenet/client/ArchiveManager.java 2007-08-08
19:25:15 UTC (rev 14535)
+++ trunk/freenet/src/freenet/client/ArchiveManager.java 2007-08-08
19:57:51 UTC (rev 14536)
@@ -17,6 +17,7 @@
import freenet.keys.FreenetURI;
import freenet.support.LRUHashtable;
import freenet.support.Logger;
+import freenet.support.MutableBoolean;
import freenet.support.api.Bucket;
import freenet.support.io.BucketTools;
import freenet.support.io.FilenameGenerator;
@@ -177,7 +178,8 @@
logMINOR = Logger.shouldLog(Logger.MINOR, this);
- boolean gotElement = false;
+ MutableBoolean gotElement = element != null ? new
MutableBoolean() : null;
+
if(logMINOR) Logger.minor(this, "Extracting "+key);
ctx.onExtract();
ctx.removeAllCachedItems(); // flush cache anyway
@@ -253,12 +255,7 @@
out.close();
if(name.equals(".metadata"))
gotMetadata = true;
- ArchiveStoreItem item =
addStoreElement(ctx, key, name, temp);
- if((!gotElement) && element != null &&
name.equals(element)) {
- gotElement = true;
- // Let it throw, if it does
something is drastically wrong
-
callback.gotBucket(item.getReaderBucket());
- }
+ addStoreElement(ctx, key, name, temp,
gotElement, element, callback);
names.add(name);
trimStoredData();
}
@@ -266,17 +263,12 @@
// If no metadata, generate some
if(!gotMetadata) {
- ArchiveStoreItem item = generateMetadata(ctx,
key, names);
- if(element != null && (!gotElement) &&
element.equals(METADATA_NAME)) {
- gotElement = true;
- // Let it throw, if it does something
is drastically wrong
-
callback.gotBucket(item.getReaderBucket());
- }
+ generateMetadata(ctx, key, names, gotElement,
element, callback);
trimStoredData();
}
if(throwAtExit) throw new
ArchiveRestartException("Archive changed on re-fetch");
- if((!gotElement) && element != null)
+ if((!gotElement.value) && element != null)
callback.notInArchive();
} catch (IOException e) {
@@ -297,9 +289,12 @@
* @param ctx The context object.
* @param key The key from which the archive we are unpacking was
fetched.
* @param names Set of names in the archive.
+ * @param element2
+ * @param gotElement
+ * @param callbackName If we generate a
* @throws ArchiveFailureException
*/
- private ArchiveStoreItem generateMetadata(ArchiveStoreContext ctx,
FreenetURI key, HashSet names) throws ArchiveFailureException {
+ private ArchiveStoreItem generateMetadata(ArchiveStoreContext ctx,
FreenetURI key, HashSet names, MutableBoolean gotElement, String element2,
ArchiveExtractCallback callback) throws ArchiveFailureException {
/* What we have to do is to:
* - Construct a filesystem tree of the names.
* - Turn each level of the tree into a Metadata object,
including those below it, with
@@ -324,10 +319,10 @@
OutputStream os =
element.bucket.getOutputStream();
os.write(buf);
os.close();
- return addStoreElement(ctx, key, ".metadata",
element);
+ return addStoreElement(ctx, key, ".metadata",
element, gotElement, element2, callback);
} catch (MetadataUnresolvedException e) {
try {
- x = resolve(e, x, element, ctx, key);
+ x = resolve(e, x, element, ctx, key,
gotElement, element2, callback);
} catch (IOException e1) {
throw new
ArchiveFailureException("Failed to create metadata: "+e1, e1);
}
@@ -338,7 +333,7 @@
}
}
- private int resolve(MetadataUnresolvedException e, int x,
TempStoreElement element, ArchiveStoreContext ctx, FreenetURI key) throws
IOException {
+ private int resolve(MetadataUnresolvedException e, int x,
TempStoreElement element, ArchiveStoreContext ctx, FreenetURI key,
MutableBoolean gotElement, String element2, ArchiveExtractCallback callback)
throws IOException, ArchiveFailureException {
Metadata[] m = e.mustResolve;
for(int i=0;i<m.length;i++) {
try {
@@ -346,9 +341,9 @@
OutputStream os =
element.bucket.getOutputStream();
os.write(buf);
os.close();
- addStoreElement(ctx, key, ".metadata-"+(x++),
element);
+ addStoreElement(ctx, key, ".metadata-"+(x++),
element, gotElement, element2, callback);
} catch (MetadataUnresolvedException e1) {
- x = resolve(e, x, element, ctx, key);
+ x = resolve(e, x, element, ctx, key,
gotElement, element2, callback);
}
}
return x;
@@ -405,15 +400,32 @@
/**
* Add a store element.
+ * @param callbackName If set, the name of the file for which we must
call the callback if this file happens to
+ * match.
+ * @param gotElement Flag indicating whether we've already found the
file for the callback. If so we must not call
+ * it again.
+ * @param callback Callback to be called if we do find it. We must
getReaderBucket() before adding the data to the
+ * LRU, otherwise it may be deleted before it reaches the client.
+ * @throws ArchiveFailureException If a failure occurred resulting in
the data not being readable. Only happens if
+ * callback != null.
*/
- private ArchiveStoreItem addStoreElement(ArchiveStoreContext ctx,
FreenetURI key, String name, TempStoreElement temp) {
+ private ArchiveStoreItem addStoreElement(ArchiveStoreContext ctx,
FreenetURI key, String name, TempStoreElement temp, MutableBoolean gotElement,
String callbackName, ArchiveExtractCallback callback) throws
ArchiveFailureException {
RealArchiveStoreItem element = new RealArchiveStoreItem(this,
ctx, key, name, temp);
if(logMINOR) Logger.minor(this, "Adding store element:
"+element+" ( "+key+ ' ' +name+" size "+element.spaceUsed()+" )");
ArchiveStoreItem oldItem;
+ // Let it throw, if it does something is drastically wrong
+ Bucket matchBucket = null;
+ if((!gotElement.value) && name.equals(callbackName)) {
+ matchBucket = element.getReaderBucket();
+ }
synchronized (storedData) {
oldItem = (ArchiveStoreItem)
storedData.get(element.key);
storedData.push(element.key, element);
}
+ if(matchBucket != null) {
+ callback.gotBucket(matchBucket);
+ gotElement.value = true;
+ }
if(oldItem != null)
oldItem.close();
return element;