extents in e2fsprogs

Alexandre Ratchov <[email protected]>
Newsgroups gmane.comp.file-systems.ext2.devel
Message-ID <[email protected]>
hi Ted,

here is the patch for extents support (no types changes, no 64bit support);
It should apply cleanly on e2fsprogs-1.39

cheers,

-- 
Alexandre

_______________________________________________
Ext2-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ext2-devel
e2fsprogs-extents (text/plain, 18.1 KB)
Index: e2fsprogs-1.39/lib/ext2fs/block.c
===================================================================
--- e2fsprogs-1.39.orig/lib/ext2fs/block.c	2006-05-31 15:44:49.000000000 +0200
+++ e2fsprogs-1.39/lib/ext2fs/block.c	2006-06-14 13:25:47.000000000 +0200
@@ -17,6 +17,7 @@
 
 #include "ext2_fs.h"
 #include "ext2fs.h"
+#include "ext3_extents.h"
 
 struct block_context {
 	ext2_filsys	fs;
@@ -36,6 +37,102 @@ struct block_context {
 	void	*priv_data;
 };
 
+#define DEBUG_EXT 0
+
+#if DEBUG_EXT
+void show_header(struct ext3_extent_header *eh)
+{
+        printf("header: magic=%x, entries=%d, max=%d, depth=%d, generation=%d,",
+	       eh->eh_magic, eh->eh_entries, eh->eh_max, eh->eh_depth,
+	       eh->eh_generation);
+}
+
+void show_index(struct ext3_extent_header *eh)
+{
+        struct ext3_extent_idx *ix;
+        int i;
+
+        ix = EXT_FIRST_INDEX(eh);
+        printf("this node has %d indexs\n",eh->e_num);
+        for (i = 0; i < eh->eh_entries; i++, ix++) {
+                printf("index %d, block = %d, leaf = %d\n", i,
+		       ix->ei_block, ix->ei_leaf);
+        }
+}
+
+void show_extent(struct ext3_extent_header *eh)
+{
+        struct ext3_extent *ex;
+        int i;
+
+        ex = EXT_FIRST_EXTENT(eh);
+        printf("this node has %d extents\n", eh->e_len);
+}
+#else
+#define show_header(eh) do { } while (0)
+#define show_index(eh) do { } while (0)
+#define show_extent(eh) do { } while (0)
+#endif
+
+static int block_iterate_extents(struct ext3_extent_header *eh, ext2_filsys fs,
+				 struct block_context *ctx)
+{
+        int ret = 0;
+        int i,j;
+        char * block_buf;
+        blk_t  block_address;
+        struct ext3_extent *ex;
+        struct ext3_extent_idx *ix;
+        struct ext3_extent_header *nh;
+
+        if(eh->eh_depth == 0) {
+                show_extent(eh);
+                ex = EXT_FIRST_EXTENT(eh);
+                for (i = 0; i < eh->eh_entries; i++, ex++) {
+                        for(j=0; j< ex->ee_len; j++) {
+                                block_address = ex->ee_start + j;
+				ret = (*ctx->func)(ctx->fs, &block_address,
+						   (ex->ee_block + j), 0,
+						   i, ctx->priv_data);
+                        }
+                }
+        } else {
+		show_index(eh);
+                ret = ext2fs_get_mem(fs->blocksize, &block_buf);
+                if (ret)
+                        return ret;
+                ix = EXT_FIRST_INDEX(eh);
+                for (i = 0; i < eh->eh_entries; i++, ix++) {
+			block_address = ix->ei_leaf;
+			ret = (*ctx->func)(ctx->fs, &block_address,
+					   BLOCK_COUNT_IND, 0, i, ctx->priv_data);
+			if (ret & BLOCK_ABORT) {
+				ext2fs_free_mem(&block_buf);
+				return ret;
+			}
+			ctx->errcode = io_channel_read_blk(ctx->fs->io,
+							   ix->ei_leaf, 1,
+							   block_buf);
+			if(ctx->errcode) {
+				ret |= BLOCK_ERROR;
+				ext2fs_free_mem(&block_buf);
+				return ret;
+			}
+			nh = (struct ext3_extent_header *)block_buf;
+			show_header(nh);
+			if (nh->eh_magic != EXT3_EXT_MAGIC)
+			{
+				ret |= BLOCK_ERROR;
+				ext2fs_free_mem(&block_buf);
+				return ret;
+			}
+			ret = block_iterate_extents(nh, fs, ctx);
+                }
+		ext2fs_free_mem(&block_buf);
+	}
+	return ret;
+}
+
 static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
 			     int ref_offset, struct block_context *ctx)
 {
@@ -283,6 +380,7 @@ errcode_t ext2fs_block_iterate2(ext2_fil
 	errcode_t	retval;
 	struct block_context ctx;
 	int	limit;
+        struct ext3_extent_header * eh;
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -338,7 +436,23 @@ errcode_t ext2fs_block_iterate2(ext2_fil
 				goto abort_exit;
 		}
 	}
-	
+
+	/*
+	 * Iterate over normal data blocks with extents
+	 */
+        ext2fs_read_inode(fs, ino, &inode);
+        if (inode.i_flags & EXT3_EXTENTS_FL) {
+                eh = (struct ext3_extent_header *)&inode.i_block[0];
+                if (eh->eh_magic == EXT3_EXT_MAGIC) {
+		        show_header(eh);
+                        ret = block_iterate_extents(eh, fs, &ctx);
+                        goto abort_exit;
+                } else {
+                        inode.i_flags &= ~(EXT3_EXTENTS_FL);
+                        ext2fs_write_inode(fs, ino, &inode);
+                }
+        }
+
 	/*
 	 * Iterate over normal data blocks
 	 */
Index: e2fsprogs-1.39/lib/ext2fs/bmap.c
===================================================================
--- e2fsprogs-1.39.orig/lib/ext2fs/bmap.c	2006-05-31 15:44:49.000000000 +0200
+++ e2fsprogs-1.39/lib/ext2fs/bmap.c	2006-06-14 13:25:47.000000000 +0200
@@ -17,6 +17,7 @@
 
 #include "ext2_fs.h"
 #include "ext2fs.h"
+#include "ext3_extents.h"
 
 #if defined(__GNUC__) && !defined(NO_INLINE_FUNCS)
 #define _BMAP_INLINE_	__inline__
@@ -31,6 +32,49 @@ extern errcode_t ext2fs_bmap(ext2_filsys
 
 #define inode_bmap(inode, nr) ((inode)->i_block[(nr)])
 
+static int block_extents_bmap(struct ext3_extent_header *eh, ext2_filsys fs,
+			      blk_t block, blk_t *phys_blk)
+{
+	int ret = 0;
+	int i;
+	char * block_buf;
+	struct ext3_extent *ex;
+	struct ext3_extent_idx *ix;
+	struct ext3_extent_header *nh;
+
+	if(eh->eh_depth == 0) {
+		ex = EXT_FIRST_EXTENT(eh);
+		for (i = 0; i < eh->eh_entries; i++, ex++)
+			if ( (ex->ee_block <= block) && (block < ex->ee_block +  ex->ee_len) ) {
+				*phys_blk = ex->ee_start + (block - ex->ee_block);
+				return 0;
+			}
+		return BLOCK_ERROR;
+	} else {
+		ret = ext2fs_get_mem(fs->blocksize, &block_buf);
+		if (ret)
+			return ret;
+		ix = EXT_FIRST_INDEX(eh);
+		for (i = 0; i < eh->eh_entries; i++, ix++) {
+			ret = io_channel_read_blk(fs->io,
+						  ix->ei_leaf, 1,
+						  block_buf);
+			if (ret) {
+				ext2fs_free_mem(&block_buf);
+				return BLOCK_ERROR;
+			}
+			nh = (struct ext3_extent_header *)block_buf;
+			if (nh->eh_magic != EXT3_EXT_MAGIC) {
+				ext2fs_free_mem(&block_buf);
+				return BLOCK_ERROR;
+			}
+			ret = block_extents_bmap(nh, fs, block, phys_blk);
+		}
+		ext2fs_free_mem(&block_buf);
+	}
+	return ret;
+}
+
 static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags, 
 					      blk_t ind, char *block_buf, 
 					      int *blocks_alloc,
@@ -144,6 +188,7 @@ errcode_t ext2fs_bmap(ext2_filsys fs, ex
 	char	*buf = 0;
 	errcode_t	retval = 0;
 	int		blocks_alloc = 0, inode_dirty = 0;
+	struct ext3_extent_header * eh;
 
 	if (!(bmap_flags & BMAP_SET))
 		*phys_blk = 0;
@@ -155,6 +200,15 @@ errcode_t ext2fs_bmap(ext2_filsys fs, ex
 			return retval;
 		inode = &inode_buf;
 	}
+
+	if (inode->i_flags & EXT3_EXTENTS_FL) {
+		eh = (struct ext3_extent_header *)&inode->i_block[0];
+		if (eh->eh_magic == EXT3_EXT_MAGIC)
+			return block_extents_bmap(eh, fs, block, phys_blk);
+		else
+			return BLOCK_ERROR;
+	}
+
 	addr_per_block = (blk_t) fs->blocksize >> 2;
 
 	if (!block_buf) {
Index: e2fsprogs-1.39/lib/ext2fs/ext2fs.h
===================================================================
--- e2fsprogs-1.39.orig/lib/ext2fs/ext2fs.h	2006-06-14 12:35:51.000000000 +0200
+++ e2fsprogs-1.39/lib/ext2fs/ext2fs.h	2006-06-14 13:25:47.000000000 +0200
@@ -453,12 +453,14 @@ typedef struct ext2_icount *ext2_icount_
 					 EXT2_FEATURE_INCOMPAT_COMPRESSION|\
 					 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
 					 EXT2_FEATURE_INCOMPAT_META_BG|\
-					 EXT3_FEATURE_INCOMPAT_RECOVER)
+					 EXT3_FEATURE_INCOMPAT_RECOVER|\
+					 EXT3_FEATURE_INCOMPAT_EXTENTS)
 #else
 #define EXT2_LIB_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE|\
 					 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
 					 EXT2_FEATURE_INCOMPAT_META_BG|\
-					 EXT3_FEATURE_INCOMPAT_RECOVER)
+					 EXT3_FEATURE_INCOMPAT_RECOVER|\
+					 EXT3_FEATURE_INCOMPAT_EXTENTS)
 #endif
 #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
 					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE)
Index: e2fsprogs-1.39/lib/ext2fs/mkdir.c
===================================================================
--- e2fsprogs-1.39.orig/lib/ext2fs/mkdir.c	2006-05-31 15:44:49.000000000 +0200
+++ e2fsprogs-1.39/lib/ext2fs/mkdir.c	2006-06-14 13:25:47.000000000 +0200
@@ -25,6 +25,7 @@
 
 #include "ext2_fs.h"
 #include "ext2fs.h"
+#include "ext3_extents.h"
 
 #ifndef EXT2_FT_DIR
 #define EXT2_FT_DIR		2
@@ -83,10 +84,26 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, e
 	inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask);
 	inode.i_uid = inode.i_gid = 0;
 	inode.i_blocks = fs->blocksize / 512;
-	inode.i_block[0] = blk;
 	inode.i_links_count = 2;
 	inode.i_ctime = inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(NULL);
 	inode.i_size = fs->blocksize;
+	if (fs->super->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_64BIT) {
+		struct ext3_extent_header *eh = (struct ext3_extent_header *)inode.i_block;
+		struct ext3_extent *ex = EXT_FIRST_EXTENT(eh);
+
+		 eh->eh_magic = EXT3_EXT_MAGIC;
+		 eh->eh_depth = 0;
+		 eh->eh_entries = 1;
+		 eh->eh_max = (sizeof(inode.i_block) - sizeof(struct ext3_extent_header))
+			 / sizeof(struct ext3_extent);
+		 ex->ee_block = 0;
+		 ex->ee_start = blk;
+		 ex->ee_len = 1;
+
+		 inode.i_flags = EXT3_EXTENTS_FL;
+	} else {
+		inode.i_block[0] = blk;
+	}
 
 	/*
 	 * Write out the inode and inode data block
Index: e2fsprogs-1.39/lib/ext2fs/ext3_extents.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ e2fsprogs-1.39/lib/ext2fs/ext3_extents.h	2006-06-14 13:25:47.000000000 +0200
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2003,2004 Cluster File Systems, Inc, [email protected]
+ * Written by Alex Tomas <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public Licens
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-
+ */
+
+#ifndef _LINUX_EXT3_EXTENTS
+#define _LINUX_EXT3_EXTENTS
+
+/*
+ * with AGRESSIVE_TEST defined capacity of index/leaf blocks
+ * become very little, so index split, in-depth growing and
+ * other hard changes happens much more often
+ * this is for debug purposes only
+ */
+#define AGRESSIVE_TEST_
+
+/*
+ * if CHECK_BINSEARCH defined, then results of binary search
+ * will be checked by linear search
+ */
+#define CHECK_BINSEARCH_
+
+/*
+ * if EXT_DEBUG is defined you can use 'extdebug' mount option
+ * to get lots of info what's going on
+ */
+#define EXT_DEBUG
+#ifdef EXT_DEBUG
+#define ext_debug(tree,fmt,a...) 			\
+do {							\
+	if (test_opt((tree)->inode->i_sb, EXTDEBUG))	\
+		printk(fmt, ##a);			\
+} while (0);
+#else
+#define ext_debug(tree,fmt,a...)
+#endif
+
+/*
+ * if EXT_STATS is defined then stats numbers are collected
+ * these number will be displayed at umount time
+ */
+#define EXT_STATS_
+
+
+#define EXT3_ALLOC_NEEDED	3	/* block bitmap + group desc. + sb */
+
+/*
+ * ext3_inode has i_block array (total 60 bytes)
+ * first 4 bytes are used to store:
+ *  - tree depth (0 mean there is no tree yet. all extents in the inode)
+ *  - number of alive extents in the inode
+ */
+
+/*
+ * this is extent on-disk structure
+ * it's used at the bottom of the tree
+ */
+struct ext3_extent {
+	__u32	ee_block;	/* first logical block extent covers */
+	__u16	ee_len;		/* number of blocks covered by extent */
+	__u16	ee_start_hi;	/* high 16 bits of physical block */
+	__u32	ee_start;	/* low 32 bigs of physical block */
+};
+
+/*
+ * this is index on-disk structure
+ * it's used at all the levels, but the bottom
+ */
+struct ext3_extent_idx {
+	__u32	ei_block;	/* index covers logical blocks from 'block' */
+	__u32	ei_leaf;	/* pointer to the physical block of the next *
+				 * level. leaf or next index could bet here */
+	__u16	ei_leaf_hi;	/* high 16 bits of physical block */
+	__u16	ei_unused;
+};
+
+/*
+ * each block (leaves and indexes), even inode-stored has header
+ */
+struct ext3_extent_header {
+	__u16	eh_magic;	/* probably will support different formats */
+	__u16	eh_entries;	/* number of valid entries */
+	__u16	eh_max;		/* capacity of store in entries */
+	__u16	eh_depth;	/* has tree real underlaying blocks? */
+	__u32	eh_generation;	/* generation of the tree */
+};
+
+#define EXT3_EXT_MAGIC		0xf30a
+
+/*
+ * array of ext3_ext_path contains path to some extent
+ * creation/lookup routines use it for traversal/splitting/etc
+ * truncate uses it to simulate recursive walking
+ */
+struct ext3_ext_path {
+	__u32				p_block;
+	__u16				p_depth;
+	struct ext3_extent		*p_ext;
+	struct ext3_extent_idx		*p_idx;
+	struct ext3_extent_header	*p_hdr;
+	struct buffer_head		*p_bh;
+};
+
+/*
+ * structure for external API
+ */
+
+#define EXT_CONTINUE	0
+#define EXT_BREAK	1
+#define EXT_REPEAT	2
+
+
+#define EXT_MAX_BLOCK	0xffffffff
+#define EXT_CACHE_MARK	0xffff
+
+
+#define EXT_FIRST_EXTENT(__hdr__) \
+	((struct ext3_extent *) (((char *) (__hdr__)) +		\
+				 sizeof(struct ext3_extent_header)))
+#define EXT_FIRST_INDEX(__hdr__) \
+	((struct ext3_extent_idx *) (((char *) (__hdr__)) +	\
+				     sizeof(struct ext3_extent_header)))
+#define EXT_HAS_FREE_INDEX(__path__) \
+	((__path__)->p_hdr->eh_entries < (__path__)->p_hdr->eh_max)
+#define EXT_LAST_EXTENT(__hdr__) \
+	(EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->eh_entries - 1)
+#define EXT_LAST_INDEX(__hdr__) \
+	(EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->eh_entries - 1)
+#define EXT_MAX_EXTENT(__hdr__) \
+	(EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->eh_max - 1)
+#define EXT_MAX_INDEX(__hdr__) \
+	(EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->eh_max - 1)
+
+#define EXT_ROOT_HDR(tree) \
+	((struct ext3_extent_header *) (tree)->root)
+#define EXT_BLOCK_HDR(bh) \
+	((struct ext3_extent_header *) (bh)->b_data)
+#define EXT_DEPTH(_t_)	\
+	(((struct ext3_extent_header *)((_t_)->root))->eh_depth)
+#define EXT_GENERATION(_t_)	\
+	(((struct ext3_extent_header *)((_t_)->root))->eh_generation)
+
+
+#define EXT_ASSERT(__x__) if (!(__x__)) BUG();
+
+
+/*
+ * this structure is used to gather extents from the tree via ioctl
+ */
+struct ext3_extent_buf {
+	unsigned long start;
+	int buflen;
+	void *buffer;
+	void *cur;
+	int err;
+};
+
+/*
+ * this structure is used to collect stats info about the tree
+ */
+struct ext3_extent_tree_stats {
+	int depth;
+	int extents_num;
+	int leaf_num;
+};
+
+#ifdef __KERNEL__
+/*
+ * ext3_extents_tree is used to pass initial information
+ * to top-level extents API
+ */
+struct ext3_extents_helpers;
+struct ext3_extents_tree {
+	struct inode *inode;	/* inode which tree belongs to */
+	void *root;		/* ptr to data top of tree resides at */
+	void *buffer;		/* will be passed as arg to ^^ routines	*/
+	int buffer_len;
+	void *private;
+	struct ext3_extent *cex;/* last found extent */
+	struct ext3_extents_helpers *ops;
+};
+
+struct ext3_extents_helpers {
+	int (*get_write_access)(handle_t *h, void *buffer);
+	int (*mark_buffer_dirty)(handle_t *h, void *buffer);
+	int (*mergable)(struct ext3_extent *ex1, struct ext3_extent *ex2);
+	int (*remove_extent_credits)(struct ext3_extents_tree *,
+					struct ext3_extent *, unsigned long,
+					unsigned long);
+	int (*remove_extent)(struct ext3_extents_tree *,
+				struct ext3_extent *, unsigned long,
+				unsigned long);
+	int (*new_block)(handle_t *, struct ext3_extents_tree *,
+				struct ext3_ext_path *, struct ext3_extent *,
+				int *);
+};
+
+/*
+ * to be called by ext3_ext_walk_space()
+ * negative retcode - error
+ * positive retcode - signal for ext3_ext_walk_space(), see below
+ * callback must return valid extent (passed or newly created)
+ */
+typedef int (*ext_prepare_callback)(struct ext3_extents_tree *,
+					struct ext3_ext_path *,
+					struct ext3_extent *, int);
+void ext3_init_tree_desc(struct ext3_extents_tree *, struct inode *);
+extern int ext3_extent_tree_init(handle_t *, struct ext3_extents_tree *);
+extern int ext3_ext_calc_credits_for_insert(struct ext3_extents_tree *, struct ext3_ext_path *);
+extern int ext3_ext_insert_extent(handle_t *, struct ext3_extents_tree *, struct ext3_ext_path *, struct ext3_extent *);
+extern int ext3_ext_walk_space(struct ext3_extents_tree *, unsigned long, unsigned long, ext_prepare_callback);
+extern int ext3_ext_remove_space(struct ext3_extents_tree *, unsigned long, unsigned long);
+extern struct ext3_ext_path * ext3_ext_find_extent(struct ext3_extents_tree *, int, struct ext3_ext_path *);
+
+static inline void
+ext3_ext_invalidate_cache(struct ext3_extents_tree *tree)
+{
+	if (tree->cex)
+		tree->cex->ee_len = 0;
+}
+#endif /* __KERNEL__ */
+
+
+#endif /* _LINUX_EXT3_EXTENTS */
+
Index: e2fsprogs-1.39/e2fsck/pass1.c
===================================================================
--- e2fsprogs-1.39.orig/e2fsck/pass1.c	2006-05-31 15:44:49.000000000 +0200
+++ e2fsprogs-1.39/e2fsck/pass1.c	2006-06-14 13:25:47.000000000 +0200
@@ -806,21 +806,30 @@ void e2fsck_pass1(e2fsck_t ctx)
 			ctx->fs_sockets_count++;
 		} else
 			mark_inode_bad(ctx, ino);
-		if (inode->i_block[EXT2_IND_BLOCK])
-			ctx->fs_ind_count++;
-		if (inode->i_block[EXT2_DIND_BLOCK])
-			ctx->fs_dind_count++;
-		if (inode->i_block[EXT2_TIND_BLOCK])
-			ctx->fs_tind_count++;
-		if (inode->i_block[EXT2_IND_BLOCK] ||
-		    inode->i_block[EXT2_DIND_BLOCK] ||
-		    inode->i_block[EXT2_TIND_BLOCK] ||
-		    inode->i_file_acl) {
+		if (fs->super->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_64BIT) {
+		    if (inode->i_flags & EXT3_EXTENTS_FL) {
 			inodes_to_process[process_inode_count].ino = ino;
 			inodes_to_process[process_inode_count].inode = *inode;
 			process_inode_count++;
-		} else
-			check_blocks(ctx, &pctx, block_buf);
+		    } else
+			    mark_inode_bad(ctx, ino);
+		} else {
+			if (inode->i_block[EXT2_IND_BLOCK])
+				ctx->fs_ind_count++;
+			if (inode->i_block[EXT2_DIND_BLOCK])
+				ctx->fs_dind_count++;
+			if (inode->i_block[EXT2_TIND_BLOCK])
+				ctx->fs_tind_count++;
+			if (inode->i_block[EXT2_IND_BLOCK] ||
+			    inode->i_block[EXT2_DIND_BLOCK] ||
+			    inode->i_block[EXT2_TIND_BLOCK] ||
+			    inode->i_file_acl) {
+				inodes_to_process[process_inode_count].ino = ino;
+				inodes_to_process[process_inode_count].inode = *inode;
+				process_inode_count++;
+			} else
+				    check_blocks(ctx, &pctx, block_buf);
+		}
 
 		if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
 			return;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.