log.debug("Reading block "+nr+" (offset: "+nr*blockSize+") from disk");
	public byte[] getBlock(long nr) throws IOException{		if(isClosed())			throw new IOException("FS closed (fs instance: "+this+")");		//log.debug("blockCache size: "+blockCache.size());				int blockSize = superblock.getBlockSize();		Block result;				Integer key=new Integer((int)(nr));        synchronized(blockCache) {            //check if the block has already been retrieved            if(blockCache.containsKey(key)) {                    result=(Block)blockCache.get(key);                    return result.getData();            }        }        //perform the time-consuming disk read outside of the synchronized block	    //advantage:	    //      -the lock is held for a shorter time, so other blocks that are	    //       already in the cache can be returned immediately and	    //       do not have to wait for a long disk read 	    //disadvantage:	    //      -a single block can be retrieved more than once. However,         //		 the block will be put in the cache only once in the second         //		 synchronized block	    byte[] data = new byte[blockSize];	    getApi().read( nr*blockSize, data, 0, blockSize );		    //synchronize again	    synchronized(blockCache) {	            //check if the block has already been retrieved	            if(!blockCache.containsKey(key)) {	                    result=new Block(this, nr, data);	                    blockCache.put(key, result);	                    return result.getData();	            } else {		            //it is important to ALWAYS return the block that is in 		            //the cache (it is used in synchronization)	            	result=(Block)blockCache.get(key);                    return result.getData();	            }	    }	}