forked from rrcarlosr/Jetpack
509 lines
18 KiB
C
509 lines
18 KiB
C
/*
|
|
* Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
|
|
*
|
|
* GRUB -- GRand Unified Bootloader
|
|
* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
|
|
*
|
|
* (C) Copyright 2003 - 2004
|
|
* Sysgo AG, <www.elinos.com>, Pavel Bartusek <pba@sysgo.com>
|
|
*
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
|
|
/* An implementation for the ReiserFS filesystem ported from GRUB.
|
|
* Some parts of this code (mainly the structures and defines) are
|
|
* from the original reiser fs code, as found in the linux kernel.
|
|
*/
|
|
|
|
#ifndef __BYTE_ORDER
|
|
#if defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
|
|
#define __BYTE_ORDER __LITTLE_ENDIAN
|
|
#elif defined(__BIG_ENDIAN) && !defined(__LITTLE_ENDIAN)
|
|
#define __BYTE_ORDER __BIG_ENDIAN
|
|
#else
|
|
#error "unable to define __BYTE_ORDER"
|
|
#endif
|
|
#endif /* not __BYTE_ORDER */
|
|
|
|
#define FSYS_BUFLEN 0x8000
|
|
#define FSYS_BUF fsys_buf
|
|
|
|
/* This is the new super block of a journaling reiserfs system */
|
|
struct reiserfs_super_block
|
|
{
|
|
__u32 s_block_count; /* blocks count */
|
|
__u32 s_free_blocks; /* free blocks count */
|
|
__u32 s_root_block; /* root block number */
|
|
__u32 s_journal_block; /* journal block number */
|
|
__u32 s_journal_dev; /* journal device number */
|
|
__u32 s_journal_size; /* size of the journal on FS creation. used to make sure they don't overflow it */
|
|
__u32 s_journal_trans_max; /* max number of blocks in a transaction. */
|
|
__u32 s_journal_magic; /* random value made on fs creation */
|
|
__u32 s_journal_max_batch; /* max number of blocks to batch into a trans */
|
|
__u32 s_journal_max_commit_age; /* in seconds, how old can an async commit be */
|
|
__u32 s_journal_max_trans_age; /* in seconds, how old can a transaction be */
|
|
__u16 s_blocksize; /* block size */
|
|
__u16 s_oid_maxsize; /* max size of object id array */
|
|
__u16 s_oid_cursize; /* current size of object id array */
|
|
__u16 s_state; /* valid or error */
|
|
char s_magic[16]; /* reiserfs magic string indicates that file system is reiserfs */
|
|
__u16 s_tree_height; /* height of disk tree */
|
|
__u16 s_bmap_nr; /* amount of bitmap blocks needed to address each block of file system */
|
|
__u16 s_version;
|
|
char s_unused[128]; /* zero filled by mkreiserfs */
|
|
};
|
|
|
|
|
|
#define sb_root_block(sbp) (__le32_to_cpu((sbp)->s_root_block))
|
|
#define sb_journal_block(sbp) (__le32_to_cpu((sbp)->s_journal_block))
|
|
#define set_sb_journal_block(sbp,v) ((sbp)->s_journal_block = __cpu_to_le32(v))
|
|
#define sb_journal_size(sbp) (__le32_to_cpu((sbp)->s_journal_size))
|
|
#define sb_blocksize(sbp) (__le16_to_cpu((sbp)->s_blocksize))
|
|
#define set_sb_blocksize(sbp,v) ((sbp)->s_blocksize = __cpu_to_le16(v))
|
|
#define sb_version(sbp) (__le16_to_cpu((sbp)->s_version))
|
|
#define set_sb_version(sbp,v) ((sbp)->s_version = __cpu_to_le16(v))
|
|
|
|
|
|
#define REISERFS_MAX_SUPPORTED_VERSION 2
|
|
#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs"
|
|
#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
|
|
#define REISER3FS_SUPER_MAGIC_STRING "ReIsEr3Fs"
|
|
|
|
#define MAX_HEIGHT 7
|
|
|
|
/* must be correct to keep the desc and commit structs at 4k */
|
|
#define JOURNAL_TRANS_HALF 1018
|
|
|
|
/* first block written in a commit. */
|
|
struct reiserfs_journal_desc {
|
|
__u32 j_trans_id; /* id of commit */
|
|
__u32 j_len; /* length of commit. len +1 is the commit block */
|
|
__u32 j_mount_id; /* mount id of this trans*/
|
|
__u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the first blocks */
|
|
char j_magic[12];
|
|
};
|
|
|
|
/* last block written in a commit */
|
|
struct reiserfs_journal_commit {
|
|
__u32 j_trans_id; /* must match j_trans_id from the desc block */
|
|
__u32 j_len; /* ditto */
|
|
__u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the last blocks */
|
|
char j_digest[16]; /* md5 sum of all the blocks involved, including desc and commit. not used, kill it */
|
|
};
|
|
|
|
/* this header block gets written whenever a transaction is considered
|
|
fully flushed, and is more recent than the last fully flushed
|
|
transaction.
|
|
fully flushed means all the log blocks and all the real blocks are
|
|
on disk, and this transaction does not need to be replayed.
|
|
*/
|
|
struct reiserfs_journal_header {
|
|
/* id of last fully flushed transaction */
|
|
__u32 j_last_flush_trans_id;
|
|
/* offset in the log of where to start replay after a crash */
|
|
__u32 j_first_unflushed_offset;
|
|
/* mount id to detect very old transactions */
|
|
__u32 j_mount_id;
|
|
};
|
|
|
|
/* magic string to find desc blocks in the journal */
|
|
#define JOURNAL_DESC_MAGIC "ReIsErLB"
|
|
|
|
|
|
/*
|
|
* directories use this key as well as old files
|
|
*/
|
|
struct offset_v1
|
|
{
|
|
/*
|
|
* for regular files this is the offset to the first byte of the
|
|
* body, contained in the object-item, as measured from the start of
|
|
* the entire body of the object.
|
|
*
|
|
* for directory entries, k_offset consists of hash derived from
|
|
* hashing the name and using few bits (23 or more) of the resulting
|
|
* hash, and generation number that allows distinguishing names with
|
|
* hash collisions. If number of collisions overflows generation
|
|
* number, we return EEXIST. High order bit is 0 always
|
|
*/
|
|
__u32 k_offset;
|
|
__u32 k_uniqueness;
|
|
};
|
|
|
|
struct offset_v2 {
|
|
/*
|
|
* for regular files this is the offset to the first byte of the
|
|
* body, contained in the object-item, as measured from the start of
|
|
* the entire body of the object.
|
|
*
|
|
* for directory entries, k_offset consists of hash derived from
|
|
* hashing the name and using few bits (23 or more) of the resulting
|
|
* hash, and generation number that allows distinguishing names with
|
|
* hash collisions. If number of collisions overflows generation
|
|
* number, we return EEXIST. High order bit is 0 always
|
|
*/
|
|
|
|
#if defined(__LITTLE_ENDIAN_BITFIELD)
|
|
/* little endian version */
|
|
__u64 k_offset:60;
|
|
__u64 k_type: 4;
|
|
#elif defined(__BIG_ENDIAN_BITFIELD)
|
|
/* big endian version */
|
|
__u64 k_type: 4;
|
|
__u64 k_offset:60;
|
|
#else
|
|
#error "__LITTLE_ENDIAN_BITFIELD or __BIG_ENDIAN_BITFIELD must be defined"
|
|
#endif
|
|
} __attribute__ ((__packed__));
|
|
|
|
#define TYPE_MAXTYPE 3
|
|
#define TYPE_ANY 15
|
|
|
|
#if (__BYTE_ORDER == __BIG_ENDIAN)
|
|
typedef union {
|
|
struct offset_v2 offset_v2;
|
|
__u64 linear;
|
|
} __attribute__ ((__packed__)) offset_v2_esafe_overlay;
|
|
|
|
static inline __u16 offset_v2_k_type( const struct offset_v2 *v2 )
|
|
{
|
|
offset_v2_esafe_overlay tmp = *(const offset_v2_esafe_overlay *)v2;
|
|
tmp.linear = __le64_to_cpu( tmp.linear );
|
|
return (tmp.offset_v2.k_type <= TYPE_MAXTYPE)?tmp.offset_v2.k_type:TYPE_ANY;
|
|
}
|
|
|
|
static inline loff_t offset_v2_k_offset( const struct offset_v2 *v2 )
|
|
{
|
|
offset_v2_esafe_overlay tmp = *(const offset_v2_esafe_overlay *)v2;
|
|
tmp.linear = __le64_to_cpu( tmp.linear );
|
|
return tmp.offset_v2.k_offset;
|
|
}
|
|
#elif (__BYTE_ORDER == __LITTLE_ENDIAN)
|
|
# define offset_v2_k_type(v2) ((v2)->k_type)
|
|
# define offset_v2_k_offset(v2) ((v2)->k_offset)
|
|
#else
|
|
#error "__BYTE_ORDER must be __LITTLE_ENDIAN or __BIG_ENDIAN"
|
|
#endif
|
|
|
|
struct key
|
|
{
|
|
/* packing locality: by default parent directory object id */
|
|
__u32 k_dir_id;
|
|
/* object identifier */
|
|
__u32 k_objectid;
|
|
/* the offset and node type (old and new form) */
|
|
union
|
|
{
|
|
struct offset_v1 v1;
|
|
struct offset_v2 v2;
|
|
}
|
|
u;
|
|
};
|
|
|
|
#define KEY_SIZE (sizeof (struct key))
|
|
|
|
/* Header of a disk block. More precisely, header of a formatted leaf
|
|
or internal node, and not the header of an unformatted node. */
|
|
struct block_head
|
|
{
|
|
__u16 blk_level; /* Level of a block in the tree. */
|
|
__u16 blk_nr_item; /* Number of keys/items in a block. */
|
|
__u16 blk_free_space; /* Block free space in bytes. */
|
|
struct key blk_right_delim_key; /* Right delimiting key for this block (supported for leaf level nodes
|
|
only) */
|
|
};
|
|
#define BLKH_SIZE (sizeof (struct block_head))
|
|
#define DISK_LEAF_NODE_LEVEL 1 /* Leaf node level. */
|
|
|
|
struct item_head
|
|
{
|
|
/* Everything in the tree is found by searching for it based on
|
|
* its key.*/
|
|
struct key ih_key;
|
|
union {
|
|
/* The free space in the last unformatted node of an
|
|
indirect item if this is an indirect item. This
|
|
equals 0xFFFF iff this is a direct item or stat data
|
|
item. Note that the key, not this field, is used to
|
|
determine the item type, and thus which field this
|
|
union contains. */
|
|
__u16 ih_free_space;
|
|
/* Iff this is a directory item, this field equals the
|
|
number of directory entries in the directory item. */
|
|
__u16 ih_entry_count;
|
|
} __attribute__ ((__packed__)) u;
|
|
__u16 ih_item_len; /* total size of the item body */
|
|
__u16 ih_item_location; /* an offset to the item body
|
|
* within the block */
|
|
__u16 ih_version; /* 0 for all old items, 2 for new
|
|
ones. Highest bit is set by fsck
|
|
temporary, cleaned after all
|
|
done */
|
|
} __attribute__ ((__packed__));
|
|
|
|
/* size of item header */
|
|
#define IH_SIZE (sizeof (struct item_head))
|
|
|
|
#define ITEM_VERSION_1 0
|
|
#define ITEM_VERSION_2 1
|
|
|
|
#define ih_version(ih) (__le16_to_cpu((ih)->ih_version))
|
|
|
|
#define IH_KEY_OFFSET(ih) (ih_version(ih) == ITEM_VERSION_1 \
|
|
? __le32_to_cpu((ih)->ih_key.u.v1.k_offset) \
|
|
: offset_v2_k_offset(&((ih)->ih_key.u.v2)))
|
|
|
|
#define IH_KEY_ISTYPE(ih, type) (ih_version(ih) == ITEM_VERSION_1 \
|
|
? __le32_to_cpu((ih)->ih_key.u.v1.k_uniqueness) == V1_##type \
|
|
: offset_v2_k_type(&((ih)->ih_key.u.v2)) == V2_##type)
|
|
|
|
/***************************************************************************/
|
|
/* DISK CHILD */
|
|
/***************************************************************************/
|
|
/* Disk child pointer: The pointer from an internal node of the tree
|
|
to a node that is on disk. */
|
|
struct disk_child {
|
|
__u32 dc_block_number; /* Disk child's block number. */
|
|
__u16 dc_size; /* Disk child's used space. */
|
|
__u16 dc_reserved;
|
|
};
|
|
|
|
#define DC_SIZE (sizeof(struct disk_child))
|
|
#define dc_block_number(dc_p) (__le32_to_cpu((dc_p)->dc_block_number))
|
|
|
|
|
|
/*
|
|
* old stat data is 32 bytes long. We are going to distinguish new one by
|
|
* different size
|
|
*/
|
|
struct stat_data_v1
|
|
{
|
|
__u16 sd_mode; /* file type, permissions */
|
|
__u16 sd_nlink; /* number of hard links */
|
|
__u16 sd_uid; /* owner */
|
|
__u16 sd_gid; /* group */
|
|
__u32 sd_size; /* file size */
|
|
__u32 sd_atime; /* time of last access */
|
|
__u32 sd_mtime; /* time file was last modified */
|
|
__u32 sd_ctime; /* time inode (stat data) was last changed (except changes to sd_atime and sd_mtime) */
|
|
union {
|
|
__u32 sd_rdev;
|
|
__u32 sd_blocks; /* number of blocks file uses */
|
|
} __attribute__ ((__packed__)) u;
|
|
__u32 sd_first_direct_byte; /* first byte of file which is stored
|
|
in a direct item: except that if it
|
|
equals 1 it is a symlink and if it
|
|
equals ~(__u32)0 there is no
|
|
direct item. The existence of this
|
|
field really grates on me. Let's
|
|
replace it with a macro based on
|
|
sd_size and our tail suppression
|
|
policy. Someday. -Hans */
|
|
} __attribute__ ((__packed__));
|
|
|
|
#define stat_data_v1(ih) (ih_version(ih) == ITEM_VERSION_1)
|
|
#define sd_v1_mode(sdp) ((sdp)->sd_mode)
|
|
#define sd_v1_nlink(sdp) (__le16_to_cpu((sdp)->sd_nlink))
|
|
#define sd_v1_uid(sdp) (__le16_to_cpu((sdp)->sd_uid))
|
|
#define sd_v1_gid(sdp) (__le16_to_cpu((sdp)->sd_gid))
|
|
#define sd_v1_size(sdp) (__le32_to_cpu((sdp)->sd_size))
|
|
#define sd_v1_mtime(sdp) (__le32_to_cpu((sdp)->sd_mtime))
|
|
|
|
/* Stat Data on disk (reiserfs version of UFS disk inode minus the
|
|
address blocks) */
|
|
struct stat_data {
|
|
__u16 sd_mode; /* file type, permissions */
|
|
__u16 sd_attrs; /* persistent inode flags */
|
|
__u32 sd_nlink; /* number of hard links */
|
|
__u64 sd_size; /* file size */
|
|
__u32 sd_uid; /* owner */
|
|
__u32 sd_gid; /* group */
|
|
__u32 sd_atime; /* time of last access */
|
|
__u32 sd_mtime; /* time file was last modified */
|
|
__u32 sd_ctime; /* time inode (stat data) was last changed (except changes to sd_atime and sd_mtime) */
|
|
__u32 sd_blocks;
|
|
union {
|
|
__u32 sd_rdev;
|
|
__u32 sd_generation;
|
|
/*__u32 sd_first_direct_byte; */
|
|
/* first byte of file which is stored in a
|
|
direct item: except that if it equals 1
|
|
it is a symlink and if it equals
|
|
~(__u32)0 there is no direct item. The
|
|
existence of this field really grates
|
|
on me. Let's replace it with a macro
|
|
based on sd_size and our tail
|
|
suppression policy? */
|
|
} __attribute__ ((__packed__)) u;
|
|
} __attribute__ ((__packed__));
|
|
|
|
#define stat_data_v2(ih) (ih_version(ih) == ITEM_VERSION_2)
|
|
#define sd_v2_mode(sdp) (__le16_to_cpu((sdp)->sd_mode))
|
|
#define sd_v2_nlink(sdp) (__le32_to_cpu((sdp)->sd_nlink))
|
|
#define sd_v2_size(sdp) (__le64_to_cpu((sdp)->sd_size))
|
|
#define sd_v2_uid(sdp) (__le32_to_cpu((sdp)->sd_uid))
|
|
#define sd_v2_gid(sdp) (__le32_to_cpu((sdp)->sd_gid))
|
|
#define sd_v2_mtime(sdp) (__le32_to_cpu((sdp)->sd_mtime))
|
|
|
|
#define sd_mode(sdp) (__le16_to_cpu((sdp)->sd_mode))
|
|
#define sd_size(sdp) (__le32_to_cpu((sdp)->sd_size))
|
|
#define sd_size_hi(sdp) (__le32_to_cpu((sdp)->sd_size_hi))
|
|
|
|
struct reiserfs_de_head
|
|
{
|
|
__u32 deh_offset; /* third component of the directory entry key */
|
|
__u32 deh_dir_id; /* objectid of the parent directory of the
|
|
object, that is referenced by directory entry */
|
|
__u32 deh_objectid;/* objectid of the object, that is referenced by
|
|
directory entry */
|
|
__u16 deh_location;/* offset of name in the whole item */
|
|
__u16 deh_state; /* whether 1) entry contains stat data (for
|
|
future), and 2) whether entry is hidden
|
|
(unlinked) */
|
|
};
|
|
|
|
#define DEH_SIZE (sizeof (struct reiserfs_de_head))
|
|
#define deh_offset(p_deh) (__le32_to_cpu((p_deh)->deh_offset))
|
|
#define deh_dir_id(p_deh) (__le32_to_cpu((p_deh)->deh_dir_id))
|
|
#define deh_objectid(p_deh) (__le32_to_cpu((p_deh)->deh_objectid))
|
|
#define deh_location(p_deh) (__le16_to_cpu((p_deh)->deh_location))
|
|
#define deh_state(p_deh) (__le16_to_cpu((p_deh)->deh_state))
|
|
|
|
|
|
#define DEH_Statdata (1 << 0) /* not used now */
|
|
#define DEH_Visible (1 << 2)
|
|
|
|
#define SD_OFFSET 0
|
|
#define SD_UNIQUENESS 0
|
|
#define DOT_OFFSET 1
|
|
#define DOT_DOT_OFFSET 2
|
|
#define DIRENTRY_UNIQUENESS 500
|
|
|
|
#define V1_TYPE_STAT_DATA 0x0
|
|
#define V1_TYPE_DIRECT 0xffffffff
|
|
#define V1_TYPE_INDIRECT 0xfffffffe
|
|
#define V1_TYPE_DIRECTORY_MAX 0xfffffffd
|
|
#define V2_TYPE_STAT_DATA 0
|
|
#define V2_TYPE_INDIRECT 1
|
|
#define V2_TYPE_DIRECT 2
|
|
#define V2_TYPE_DIRENTRY 3
|
|
|
|
#define REISERFS_ROOT_OBJECTID 2
|
|
#define REISERFS_ROOT_PARENT_OBJECTID 1
|
|
#define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024)
|
|
/* the spot for the super in versions 3.5 - 3.5.11 (inclusive) */
|
|
#define REISERFS_OLD_DISK_OFFSET_IN_BYTES (8 * 1024)
|
|
#define REISERFS_OLD_BLOCKSIZE 4096
|
|
|
|
#define S_ISREG(mode) (((mode) & 0170000) == 0100000)
|
|
#define S_ISDIR(mode) (((mode) & 0170000) == 0040000)
|
|
#define S_ISLNK(mode) (((mode) & 0170000) == 0120000)
|
|
|
|
#define PATH_MAX 1024 /* include/linux/limits.h */
|
|
#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */
|
|
|
|
/* The size of the node cache */
|
|
#define FSYSREISER_CACHE_SIZE 24*1024
|
|
#define FSYSREISER_MIN_BLOCKSIZE SECTOR_SIZE
|
|
#define FSYSREISER_MAX_BLOCKSIZE FSYSREISER_CACHE_SIZE / 3
|
|
|
|
/* Info about currently opened file */
|
|
struct fsys_reiser_fileinfo
|
|
{
|
|
__u32 k_dir_id;
|
|
__u32 k_objectid;
|
|
};
|
|
|
|
/* In memory info about the currently mounted filesystem */
|
|
struct fsys_reiser_info
|
|
{
|
|
/* The last read item head */
|
|
struct item_head *current_ih;
|
|
/* The last read item */
|
|
char *current_item;
|
|
/* The information for the currently opened file */
|
|
struct fsys_reiser_fileinfo fileinfo;
|
|
/* The start of the journal */
|
|
__u32 journal_block;
|
|
/* The size of the journal */
|
|
__u32 journal_block_count;
|
|
/* The first valid descriptor block in journal
|
|
(relative to journal_block) */
|
|
__u32 journal_first_desc;
|
|
|
|
/* The ReiserFS version. */
|
|
__u16 version;
|
|
/* The current depth of the reiser tree. */
|
|
__u16 tree_depth;
|
|
/* SECTOR_SIZE << blocksize_shift == blocksize. */
|
|
__u8 blocksize_shift;
|
|
/* 1 << full_blocksize_shift == blocksize. */
|
|
__u8 fullblocksize_shift;
|
|
/* The reiserfs block size (must be a power of 2) */
|
|
__u16 blocksize;
|
|
/* The number of cached tree nodes */
|
|
__u16 cached_slots;
|
|
/* The number of valid transactions in journal */
|
|
__u16 journal_transactions;
|
|
|
|
unsigned int blocks[MAX_HEIGHT];
|
|
unsigned int next_key_nr[MAX_HEIGHT];
|
|
};
|
|
|
|
/* The cached s+tree blocks in FSYS_BUF, see below
|
|
* for a more detailed description.
|
|
*/
|
|
#define ROOT ((char *) ((int) FSYS_BUF))
|
|
#define CACHE(i) (ROOT + ((i) << INFO->fullblocksize_shift))
|
|
#define LEAF CACHE (DISK_LEAF_NODE_LEVEL)
|
|
|
|
#define BLOCKHEAD(cache) ((struct block_head *) cache)
|
|
#define ITEMHEAD ((struct item_head *) ((int) LEAF + BLKH_SIZE))
|
|
#define KEY(cache) ((struct key *) ((int) cache + BLKH_SIZE))
|
|
#define DC(cache) ((struct disk_child *) \
|
|
((int) cache + BLKH_SIZE + KEY_SIZE * nr_item))
|
|
/* The fsys_reiser_info block.
|
|
*/
|
|
#define INFO \
|
|
((struct fsys_reiser_info *) ((int) FSYS_BUF + FSYSREISER_CACHE_SIZE))
|
|
/*
|
|
* The journal cache. For each transaction it contains the number of
|
|
* blocks followed by the real block numbers of this transaction.
|
|
*
|
|
* If the block numbers of some transaction won't fit in this space,
|
|
* this list is stopped with a 0xffffffff marker and the remaining
|
|
* uncommitted transactions aren't cached.
|
|
*/
|
|
#define JOURNAL_START ((__u32 *) (INFO + 1))
|
|
#define JOURNAL_END ((__u32 *) (FSYS_BUF + FSYS_BUFLEN))
|
|
|
|
|
|
static __inline__ unsigned long
|
|
log2 (unsigned long word)
|
|
{
|
|
#ifdef __I386__
|
|
__asm__ ("bsfl %1,%0"
|
|
: "=r" (word)
|
|
: "r" (word));
|
|
return word;
|
|
#else
|
|
int i;
|
|
|
|
for(i=0; i<(8*sizeof(word)); i++)
|
|
if ((1<<i) & word)
|
|
return i;
|
|
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static __inline__ int
|
|
is_power_of_two (unsigned long word)
|
|
{
|
|
return (word & -word) == word;
|
|
}
|
|
|
|
extern const char *bb_mode_string(int mode);
|
|
extern int reiserfs_devread (int sector, int byte_offset, int byte_len, char *buf);
|