mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-11 09:50:12 +00:00
Moving reiserfs from sys/gnu to sys/gnu/fs. This was discussed on arch@.
Reviewed by: mux (mentor) Approved by: re (scottl)
This commit is contained in:
parent
d6d3f5ac42
commit
ea7630862d
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=147477
@ -1,163 +0,0 @@
|
||||
$FreeBSD$
|
||||
|
||||
[LICENSING]
|
||||
|
||||
ReiserFS is hereby licensed under the GNU General
|
||||
Public License version 2.
|
||||
|
||||
Source code files that contain the phrase "licensing governed by
|
||||
reiserfs/README" are "governed files" throughout this file. Governed
|
||||
files are licensed under the GPL. The portions of them owned by Hans
|
||||
Reiser, or authorized to be licensed by him, have been in the past,
|
||||
and likely will be in the future, licensed to other parties under
|
||||
other licenses. If you add your code to governed files, and don't
|
||||
want it to be owned by Hans Reiser, put your copyright label on that
|
||||
code so the poor blight and his customers can keep things straight.
|
||||
All portions of governed files not labeled otherwise are owned by Hans
|
||||
Reiser, and by adding your code to it, widely distributing it to
|
||||
others or sending us a patch, and leaving the sentence in stating that
|
||||
licensing is governed by the statement in this file, you accept this.
|
||||
It will be a kindness if you identify whether Hans Reiser is allowed
|
||||
to license code labeled as owned by you on your behalf other than
|
||||
under the GPL, because he wants to know if it is okay to do so and put
|
||||
a check in the mail to you (for non-trivial improvements) when he
|
||||
makes his next sale. He makes no guarantees as to the amount if any,
|
||||
though he feels motivated to motivate contributors, and you can surely
|
||||
discuss this with him before or after contributing. You have the
|
||||
right to decline to allow him to license your code contribution other
|
||||
than under the GPL.
|
||||
|
||||
Further licensing options are available for commercial and/or other
|
||||
interests directly from Hans Reiser: hans@reiser.to. If you interpret
|
||||
the GPL as not allowing those additional licensing options, you read
|
||||
it wrongly, and Richard Stallman agrees with me, when carefully read
|
||||
you can see that those restrictions on additional terms do not apply
|
||||
to the owner of the copyright, and my interpretation of this shall
|
||||
govern for this license.
|
||||
|
||||
Finally, nothing in this license shall be interpreted to allow you to
|
||||
fail to fairly credit me, or to remove my credits, without my
|
||||
permission, unless you are an end user not redistributing to others.
|
||||
If you have doubts about how to properly do that, or about what is
|
||||
fair, ask. (Last I spoke with him Richard was contemplating how best
|
||||
to address the fair crediting issue in the next GPL version.)
|
||||
|
||||
[END LICENSING]
|
||||
|
||||
Reiserfs is a file system based on balanced tree algorithms, which is
|
||||
described at http://devlinux.com/namesys.
|
||||
|
||||
Stop reading here. Go there, then return.
|
||||
|
||||
Send bug reports to yura@namesys.botik.ru.
|
||||
|
||||
mkreiserfs and other utilities are in reiserfs/utils, or wherever your
|
||||
Linux provider put them. There is some disagreement about how useful
|
||||
it is for users to get their fsck and mkreiserfs out of sync with the
|
||||
version of reiserfs that is in their kernel, with many important
|
||||
distributors wanting them out of sync.:-) Please try to remember to
|
||||
recompile and reinstall fsck and mkreiserfs with every update of
|
||||
reiserfs, this is a common source of confusion. Note that some of the
|
||||
utilities cannot be compiled without accessing the balancing code
|
||||
which is in the kernel code, and relocating the utilities may require
|
||||
you to specify where that code can be found.
|
||||
|
||||
Yes, if you update your reiserfs kernel module you do have to
|
||||
recompile your kernel, most of the time. The errors you get will be
|
||||
quite cryptic if your forget to do so.
|
||||
|
||||
Real users, as opposed to folks who want to hack and then understand
|
||||
what went wrong, will want REISERFS_CHECK off.
|
||||
|
||||
Hideous Commercial Pitch: Spread your development costs across other OS
|
||||
vendors. Select from the best in the world, not the best in your
|
||||
building, by buying from third party OS component suppliers. Leverage
|
||||
the software component development power of the internet. Be the most
|
||||
aggressive in taking advantage of the commercial possibilities of
|
||||
decentralized internet development, and add value through your branded
|
||||
integration that you sell as an operating system. Let your competitors
|
||||
be the ones to compete against the entire internet by themselves. Be
|
||||
hip, get with the new economic trend, before your competitors do. Send
|
||||
email to hans@reiser.to.
|
||||
|
||||
To understand the code, after reading the website, start reading the
|
||||
code by reading reiserfs_fs.h first.
|
||||
|
||||
Hans Reiser was the project initiator, primary architect, source of all
|
||||
funding for the first 5.5 years, and one of the programmers. He owns
|
||||
the copyright.
|
||||
|
||||
Vladimir Saveljev was one of the programmers, and he worked long hours
|
||||
writing the cleanest code. He always made the effort to be the best he
|
||||
could be, and to make his code the best that it could be. What resulted
|
||||
was quite remarkable. I don't think that money can ever motivate someone
|
||||
to work the way he did, he is one of the most selfless men I know.
|
||||
|
||||
Yura helps with benchmarking, coding hashes, and block pre-allocation
|
||||
code.
|
||||
|
||||
Anatoly Pinchuk is a former member of our team who worked closely with
|
||||
Vladimir throughout the project's development. He wrote a quite
|
||||
substantial portion of the total code. He realized that there was a
|
||||
space problem with packing tails of files for files larger than a node
|
||||
that start on a node aligned boundary (there are reasons to want to node
|
||||
align files), and he invented and implemented indirect items and
|
||||
unformatted nodes as the solution.
|
||||
|
||||
Konstantin Shvachko, with the help of the Russian version of a VC,
|
||||
tried to put me in a position where I was forced into giving control
|
||||
of the project to him. (Fortunately, as the person paying the money
|
||||
for all salaries from my dayjob I owned all copyrights, and you can't
|
||||
really force takeovers of sole proprietorships.) This was something
|
||||
curious, because he never really understood the value of our project,
|
||||
why we should do what we do, or why innovation was possible in
|
||||
general, but he was sure that he ought to be controlling it. Every
|
||||
innovation had to be forced past him while he was with us. He added
|
||||
two years to the time required to complete reiserfs, and was a net
|
||||
loss for me. Mikhail Gilula was a brilliant innovator who also left
|
||||
in a destructive way that erased the value of his contributions, and
|
||||
that he was shown much generosity just makes it more painful.
|
||||
|
||||
Grigory Zaigralin was an extremely effective system administrator for
|
||||
our group.
|
||||
|
||||
Igor Krasheninnikov was wonderful at hardware procurement, repair, and
|
||||
network installation.
|
||||
|
||||
Jeremy Fitzhardinge wrote the teahash.c code, and he gives credit to a
|
||||
textbook he got the algorithm from in the code. Note that his analysis
|
||||
of how we could use the hashing code in making 32 bit NFS cookies work
|
||||
was probably more important than the actual algorithm. Colin Plumb also
|
||||
contributed to it.
|
||||
|
||||
Chris Mason dived right into our code, and in just a few months produced
|
||||
the journaling code that dramatically increased the value of ReiserFS.
|
||||
He is just an amazing programmer.
|
||||
|
||||
Igor Zagorovsky is writing much of the new item handler and extent code
|
||||
for our next major release.
|
||||
|
||||
Alexander Zarochentcev (sometimes known as zam, or sasha), wrote the
|
||||
resizer, and is hard at work on implementing allocate on flush. SGI
|
||||
implemented allocate on flush before us for XFS, and generously took
|
||||
the time to convince me we should do it also. They are great people,
|
||||
and a great company.
|
||||
|
||||
Yuri Shevchuk and Nikita Danilov are doing squid cache optimization.
|
||||
|
||||
Vitaly Fertman is doing fsck.
|
||||
|
||||
Jeff Mahoney, of SuSE, contributed a few cleanup fixes, most notably
|
||||
the endian safe patches which allow ReiserFS to run on any platform
|
||||
supported by the Linux kernel.
|
||||
|
||||
SuSE, IntegratedLinux.com, Ecila, MP3.com, bigstorage.com, and the
|
||||
Alpha PC Company made it possible for me to not have a day job
|
||||
anymore, and to dramatically increase our staffing. Ecila funded
|
||||
hypertext feature development, MP3.com funded journaling, SuSE funded
|
||||
core development, IntegratedLinux.com funded squid web cache
|
||||
appliances, bigstorage.com funded HSM, and the alpha PC company funded
|
||||
the alpha port. Many of these tasks were helped by sponsors other
|
||||
than the ones just named. SuSE has helped in much more than just
|
||||
funding....
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,90 +0,0 @@
|
||||
/*-
|
||||
* Copyright 2000 Hans Reiser
|
||||
* See README for licensing and copyright details
|
||||
*
|
||||
* Ported to FreeBSD by Jean-Sébastien Pédron <jspedron@club-internet.fr>
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _GNU_REISERFS_REISERFS_FS_I_H
|
||||
#define _GNU_REISERFS_REISERFS_FS_I_H
|
||||
|
||||
#include <sys/queue.h>
|
||||
|
||||
/* Bitmasks for i_flags field in reiserfs-specific part of inode */
|
||||
typedef enum {
|
||||
/*
|
||||
* This says what format of key do all items (but stat data) of
|
||||
* an object have. If this is set, that format is 3.6 otherwise
|
||||
* - 3.5
|
||||
*/
|
||||
i_item_key_version_mask = 0x0001,
|
||||
/* If this is unset, object has 3.5 stat data, otherwise, it has
|
||||
* 3.6 stat data with 64bit size, 32bit nlink etc. */
|
||||
i_stat_data_version_mask = 0x0002,
|
||||
/* File might need tail packing on close */
|
||||
i_pack_on_close_mask = 0x0004,
|
||||
/* Don't pack tail of file */
|
||||
i_nopack_mask = 0x0008,
|
||||
/* If those is set, "safe link" was created for this file during
|
||||
* truncate or unlink. Safe link is used to avoid leakage of disk
|
||||
* space on crash with some files open, but unlinked. */
|
||||
i_link_saved_unlink_mask = 0x0010,
|
||||
i_link_saved_truncate_mask = 0x0020,
|
||||
i_priv_object = 0x0080,
|
||||
i_has_xattr_dir = 0x0100,
|
||||
} reiserfs_inode_flags;
|
||||
|
||||
struct reiserfs_node {
|
||||
struct vnode *i_vnode;
|
||||
struct vnode *i_devvp;
|
||||
struct cdev *i_dev;
|
||||
ino_t i_number;
|
||||
|
||||
ino_t i_ino;
|
||||
|
||||
struct reiserfs_sb_info *i_reiserfs;
|
||||
|
||||
uint32_t i_flag; /* Flags, see below */
|
||||
uint32_t i_key[4]; /* Key is still 4 32 bit
|
||||
integers */
|
||||
uint32_t i_flags; /* Transient inode flags that
|
||||
are never stored on disk.
|
||||
Bitmasks for this field
|
||||
are defined above. */
|
||||
uint32_t i_first_direct_byte; /* Offset of first byte stored
|
||||
in direct item. */
|
||||
uint32_t i_attrs; /* Copy of persistent inode
|
||||
flags read from sd_attrs. */
|
||||
|
||||
uint16_t i_mode; /* IFMT, permissions. */
|
||||
uint16_t i_nlink; /* File link count. */
|
||||
uint64_t i_size; /* File byte count. */
|
||||
uint32_t i_bytes;
|
||||
uid_t i_uid; /* File owner. */
|
||||
gid_t i_gid; /* File group. */
|
||||
struct timespec i_atime; /* Last access time. */
|
||||
struct timespec i_mtime; /* Last modified time. */
|
||||
struct timespec i_ctime; /* Last inode change time. */
|
||||
|
||||
uint32_t i_blocks;
|
||||
uint32_t i_generation;
|
||||
};
|
||||
|
||||
#define VTOI(vp) ((struct reiserfs_node *)(vp)->v_data)
|
||||
#define ITOV(ip) ((ip)->i_vnode)
|
||||
|
||||
/* These flags are kept in i_flag. */
|
||||
#define IN_HASHED 0x0020 /* Inode is on hash list */
|
||||
|
||||
/* This overlays the fid structure (see mount.h) */
|
||||
struct rfid {
|
||||
uint16_t rfid_len; /* Length of structure */
|
||||
uint16_t rfid_pad; /* Force 32-bit alignment */
|
||||
ino_t rfid_dirid; /* File key */
|
||||
ino_t rfid_objectid;
|
||||
uint32_t rfid_gen; /* Generation number */
|
||||
};
|
||||
|
||||
#endif /* !defined _GNU_REISERFS_REISERFS_FS_I_H */
|
@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000 Hans Reiser
|
||||
* See README for licensing and copyright details
|
||||
*
|
||||
* Ported to FreeBSD by Jean-Sébastien Pédron <jspedron@club-internet.fr>
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _GNU_REISERFS_REISERFS_FS_SB_H
|
||||
#define _GNU_REISERFS_REISERFS_FS_SB_H
|
||||
|
||||
typedef uint32_t (*hashf_t)(const signed char *, int);
|
||||
|
||||
#define sb_block_count(sbp) (le32toh((sbp)->s_v1.s_block_count))
|
||||
#define set_sb_block_count(sbp,v) ((sbp)->s_v1.s_block_count = htole32(v))
|
||||
#define sb_free_blocks(sbp) (le32toh((sbp)->s_v1.s_free_blocks))
|
||||
#define set_sb_free_blocks(sbp,v) ((sbp)->s_v1.s_free_blocks = htole32(v))
|
||||
#define sb_root_block(sbp) (le32toh((sbp)->s_v1.s_root_block))
|
||||
|
||||
/* Bitmaps */
|
||||
struct reiserfs_bitmap_info {
|
||||
uint16_t first_zero_hint;
|
||||
uint16_t free_count;
|
||||
//struct buf *bp; /* The actual bitmap */
|
||||
caddr_t bp_data; /* The actual bitmap */
|
||||
};
|
||||
|
||||
/* ReiserFS union of in-core super block data */
|
||||
struct reiserfs_sb_info {
|
||||
struct reiserfs_super_block *s_rs;
|
||||
struct reiserfs_bitmap_info *s_ap_bitmap;
|
||||
struct vnode *s_devvp;
|
||||
|
||||
unsigned short s_mount_state;
|
||||
|
||||
hashf_t s_hash_function; /* Pointer to function which
|
||||
is used to sort names in
|
||||
directory. Set on mount */
|
||||
unsigned long s_mount_opt; /* ReiserFS's mount options
|
||||
are set here */
|
||||
int s_generation_counter; /* Increased by one every
|
||||
time the tree gets
|
||||
re-balanced */
|
||||
unsigned long s_properties; /* File system properties.
|
||||
Currently holds on-disk
|
||||
FS format */
|
||||
uint16_t s_blocksize;
|
||||
uint16_t s_blocksize_bits;
|
||||
char s_rd_only; /* Is it read-only ? */
|
||||
int s_is_unlinked_ok;
|
||||
};
|
||||
|
||||
#define sb_version(sbi) (le16toh((sbi)->s_v1.s_version))
|
||||
#define set_sb_version(sbi, v) ((sbi)->s_v1.s_version = htole16(v))
|
||||
|
||||
#define sb_blocksize(sbi) (le16toh((sbi)->s_v1.s_blocksize))
|
||||
#define set_sb_blocksize(sbi, v) ((sbi)->s_v1.s_blocksize = htole16(v))
|
||||
|
||||
#define sb_hash_function_code(sbi) \
|
||||
(le32toh((sbi)->s_v1.s_hash_function_code))
|
||||
#define set_sb_hash_function_code(sbi, v) \
|
||||
((sbi)->s_v1.s_hash_function_code = htole32(v))
|
||||
|
||||
#define sb_bmap_nr(sbi) (le16toh((sbi)->s_v1.s_bmap_nr))
|
||||
#define set_sb_bmap_nr(sbi, v) ((sbi)->s_v1.s_bmap_nr = htole16(v))
|
||||
|
||||
/* Definitions of reiserfs on-disk properties: */
|
||||
#define REISERFS_3_5 0
|
||||
#define REISERFS_3_6 1
|
||||
|
||||
enum reiserfs_mount_options {
|
||||
/* Mount options */
|
||||
REISERFS_LARGETAIL, /* Large tails will be created in a session */
|
||||
REISERFS_SMALLTAIL, /* Small (for files less than block size) tails
|
||||
will be created in a session */
|
||||
REPLAYONLY, /* Replay journal and return 0. Use by fsck */
|
||||
REISERFS_CONVERT, /* -o conv: causes conversion of old format super
|
||||
block to the new format. If not specified -
|
||||
old partition will be dealt with in a manner
|
||||
of 3.5.x */
|
||||
|
||||
/*
|
||||
* -o hash={tea, rupasov, r5, detect} is meant for properly mounting
|
||||
* reiserfs disks from 3.5.19 or earlier. 99% of the time, this option
|
||||
* is not required. If the normal autodection code can't determine
|
||||
* which hash to use (because both hases had the same value for a
|
||||
* file) use this option to force a specific hash. It won't allow you
|
||||
* to override the existing hash on the FS, so if you have a tea hash
|
||||
* disk, and mount with -o hash=rupasov, the mount will fail.
|
||||
*/
|
||||
FORCE_TEA_HASH, /* try to force tea hash on mount */
|
||||
FORCE_RUPASOV_HASH, /* try to force rupasov hash on mount */
|
||||
FORCE_R5_HASH, /* try to force rupasov hash on mount */
|
||||
FORCE_HASH_DETECT, /* try to detect hash function on mount */
|
||||
|
||||
REISERFS_DATA_LOG,
|
||||
REISERFS_DATA_ORDERED,
|
||||
REISERFS_DATA_WRITEBACK,
|
||||
|
||||
/*
|
||||
* used for testing experimental features, makes benchmarking new
|
||||
* features with and without more convenient, should never be used by
|
||||
* users in any code shipped to users (ideally)
|
||||
*/
|
||||
|
||||
REISERFS_NO_BORDER,
|
||||
REISERFS_NO_UNHASHED_RELOCATION,
|
||||
REISERFS_HASHED_RELOCATION,
|
||||
REISERFS_ATTRS,
|
||||
REISERFS_XATTRS,
|
||||
REISERFS_XATTRS_USER,
|
||||
REISERFS_POSIXACL,
|
||||
|
||||
REISERFS_TEST1,
|
||||
REISERFS_TEST2,
|
||||
REISERFS_TEST3,
|
||||
REISERFS_TEST4,
|
||||
};
|
||||
|
||||
#define reiserfs_r5_hash(sbi) \
|
||||
(REISERFS_SB(sbi)->s_mount_opt & (1 << FORCE_R5_HASH))
|
||||
#define reiserfs_rupasov_hash(sbi) \
|
||||
(REISERFS_SB(sbi)->s_mount_opt & (1 << FORCE_RUPASOV_HASH))
|
||||
#define reiserfs_tea_hash(sbi) \
|
||||
(REISERFS_SB(sbi)->s_mount_opt & (1 << FORCE_TEA_HASH))
|
||||
#define reiserfs_hash_detect(sbi) \
|
||||
(REISERFS_SB(sbi)->s_mount_opt & (1 << FORCE_HASH_DETECT))
|
||||
|
||||
#define reiserfs_attrs(sbi) \
|
||||
(REISERFS_SB(sbi)->s_mount_opt & (1 << REISERFS_ATTRS))
|
||||
|
||||
#define reiserfs_data_log(sbi) \
|
||||
(REISERFS_SB(sbi)->s_mount_opt & (1 << REISERFS_DATA_LOG))
|
||||
#define reiserfs_data_ordered(sbi) \
|
||||
(REISERFS_SB(sbi)->s_mount_opt & (1 << REISERFS_DATA_ORDERED))
|
||||
#define reiserfs_data_writeback(sbi) \
|
||||
(REISERFS_SB(sbi)->s_mount_opt & (1 << REISERFS_DATA_WRITEBACK))
|
||||
|
||||
#define SB_BUFFER_WITH_SB(sbi) (REISERFS_SB(sbi)->s_sbh)
|
||||
#define SB_AP_BITMAP(sbi) (REISERFS_SB(sbi)->s_ap_bitmap)
|
||||
|
||||
#endif /* !defined _GNU_REISERFS_REISERFS_FS_SB_H */
|
@ -1,217 +0,0 @@
|
||||
/*-
|
||||
* Copyright 2000 Hans Reiser
|
||||
* See README for licensing and copyright details
|
||||
*
|
||||
* Ported to FreeBSD by Jean-Sébastien Pédron <jspedron@club-internet.fr>
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <gnu/reiserfs/reiserfs_fs.h>
|
||||
|
||||
/*
|
||||
* Keyed 32-bit hash function using TEA in a Davis-Meyer function
|
||||
* H0 = Key
|
||||
* Hi = E Mi(Hi-1) + Hi-1
|
||||
*
|
||||
* (see Applied Cryptography, 2nd edition, p448).
|
||||
*
|
||||
* Jeremy Fitzhardinge <jeremy@zip.com.au> 1998
|
||||
*
|
||||
* Jeremy has agreed to the contents of README. -Hans
|
||||
* Yura's function is added (04/07/2000)
|
||||
*/
|
||||
|
||||
/*
|
||||
* keyed_hash
|
||||
* yura_hash
|
||||
* r5_hash
|
||||
*/
|
||||
|
||||
#define DELTA 0x9E3779B9
|
||||
#define FULLROUNDS 10 /* 32 is overkill, 16 is strong crypto */
|
||||
#define PARTROUNDS 6 /* 6 gets complete mixing */
|
||||
|
||||
/* a, b, c, d - data; h0, h1 - accumulated hash */
|
||||
#define TEACORE(rounds) \
|
||||
do { \
|
||||
int n; \
|
||||
uint32_t b0, b1; \
|
||||
uint32_t sum; \
|
||||
\
|
||||
n = rounds; \
|
||||
sum = 0; \
|
||||
b0 = h0; \
|
||||
b1 = h1; \
|
||||
\
|
||||
do { \
|
||||
sum += DELTA; \
|
||||
b0 += ((b1 << 4) + a) ^ (b1+sum) ^ ((b1 >> 5) + b); \
|
||||
b1 += ((b0 << 4) + c) ^ (b0+sum) ^ ((b0 >> 5) + d); \
|
||||
} while (--n); \
|
||||
\
|
||||
h0 += b0; \
|
||||
h1 += b1; \
|
||||
} while (0)
|
||||
|
||||
uint32_t
|
||||
keyed_hash(const signed char *msg, int len)
|
||||
{
|
||||
uint32_t k[] = { 0x9464a485, 0x542e1a94, 0x3e846bff, 0xb75bcfc3 };
|
||||
|
||||
uint32_t h0, h1;
|
||||
uint32_t a, b, c, d;
|
||||
uint32_t pad;
|
||||
int i;
|
||||
|
||||
h0 = k[0];
|
||||
h1 = k[1];
|
||||
|
||||
pad = (uint32_t)len | ((uint32_t)len << 8);
|
||||
pad |= pad << 16;
|
||||
|
||||
while(len >= 16) {
|
||||
a = (uint32_t)msg[ 0] |
|
||||
(uint32_t)msg[ 1] << 8 |
|
||||
(uint32_t)msg[ 2] << 16 |
|
||||
(uint32_t)msg[ 3] << 24;
|
||||
b = (uint32_t)msg[ 4] |
|
||||
(uint32_t)msg[ 5] << 8 |
|
||||
(uint32_t)msg[ 6] << 16 |
|
||||
(uint32_t)msg[ 7] << 24;
|
||||
c = (uint32_t)msg[ 8] |
|
||||
(uint32_t)msg[ 9] << 8 |
|
||||
(uint32_t)msg[10] << 16 |
|
||||
(uint32_t)msg[11] << 24;
|
||||
d = (uint32_t)msg[12] |
|
||||
(uint32_t)msg[13] << 8 |
|
||||
(uint32_t)msg[14] << 16 |
|
||||
(uint32_t)msg[15] << 24;
|
||||
|
||||
TEACORE(PARTROUNDS);
|
||||
|
||||
len -= 16;
|
||||
msg += 16;
|
||||
}
|
||||
|
||||
if (len >= 12) {
|
||||
a = (uint32_t)msg[ 0] |
|
||||
(uint32_t)msg[ 1] << 8 |
|
||||
(uint32_t)msg[ 2] << 16 |
|
||||
(uint32_t)msg[ 3] << 24;
|
||||
b = (uint32_t)msg[ 4] |
|
||||
(uint32_t)msg[ 5] << 8 |
|
||||
(uint32_t)msg[ 6] << 16 |
|
||||
(uint32_t)msg[ 7] << 24;
|
||||
c = (uint32_t)msg[ 8] |
|
||||
(uint32_t)msg[ 9] << 8 |
|
||||
(uint32_t)msg[10] << 16 |
|
||||
(uint32_t)msg[11] << 24;
|
||||
|
||||
d = pad;
|
||||
for(i = 12; i < len; i++) {
|
||||
d <<= 8;
|
||||
d |= msg[i];
|
||||
}
|
||||
} else if (len >= 8) {
|
||||
a = (uint32_t)msg[ 0] |
|
||||
(uint32_t)msg[ 1] << 8 |
|
||||
(uint32_t)msg[ 2] << 16 |
|
||||
(uint32_t)msg[ 3] << 24;
|
||||
b = (uint32_t)msg[ 4] |
|
||||
(uint32_t)msg[ 5] << 8 |
|
||||
(uint32_t)msg[ 6] << 16 |
|
||||
(uint32_t)msg[ 7] << 24;
|
||||
|
||||
c = d = pad;
|
||||
for(i = 8; i < len; i++) {
|
||||
c <<= 8;
|
||||
c |= msg[i];
|
||||
}
|
||||
} else if (len >= 4) {
|
||||
a = (uint32_t)msg[ 0] |
|
||||
(uint32_t)msg[ 1] << 8 |
|
||||
(uint32_t)msg[ 2] << 16 |
|
||||
(uint32_t)msg[ 3] << 24;
|
||||
|
||||
b = c = d = pad;
|
||||
for(i = 4; i < len; i++) {
|
||||
b <<= 8;
|
||||
b |= msg[i];
|
||||
}
|
||||
} else {
|
||||
a = b = c = d = pad;
|
||||
for(i = 0; i < len; i++) {
|
||||
a <<= 8;
|
||||
a |= msg[i];
|
||||
}
|
||||
}
|
||||
|
||||
TEACORE(FULLROUNDS);
|
||||
|
||||
/* return 0; */
|
||||
return (h0 ^ h1);
|
||||
}
|
||||
|
||||
/*
|
||||
* What follows in this file is copyright 2000 by Hans Reiser, and the
|
||||
* licensing of what follows is governed by README
|
||||
* */
|
||||
uint32_t
|
||||
yura_hash(const signed char *msg, int len)
|
||||
{
|
||||
int i;
|
||||
int j, pow;
|
||||
uint32_t a, c;
|
||||
|
||||
for (pow = 1, i = 1; i < len; i++)
|
||||
pow = pow * 10;
|
||||
|
||||
if (len == 1)
|
||||
a = msg[0] - 48;
|
||||
else
|
||||
a = (msg[0] - 48) * pow;
|
||||
|
||||
for (i = 1; i < len; i++) {
|
||||
c = msg[i] - 48;
|
||||
for (pow = 1, j = i; j < len - 1; j++)
|
||||
pow = pow * 10;
|
||||
a = a + c * pow;
|
||||
}
|
||||
|
||||
for (; i < 40; i++) {
|
||||
c = '0' - 48;
|
||||
for (pow = 1, j = i; j < len - 1; j++)
|
||||
pow = pow * 10;
|
||||
a = a + c * pow;
|
||||
}
|
||||
|
||||
for (; i < 256; i++) {
|
||||
c = i;
|
||||
for (pow = 1, j = i; j < len - 1; j++)
|
||||
pow = pow * 10;
|
||||
a = a + c * pow;
|
||||
}
|
||||
|
||||
a = a << 7;
|
||||
return (a);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
r5_hash(const signed char *msg, int len)
|
||||
{
|
||||
uint32_t a;
|
||||
const signed char *start;
|
||||
|
||||
a = 0;
|
||||
start = msg;
|
||||
|
||||
while (*msg && msg < start + len) {
|
||||
a += *msg << 4;
|
||||
a += *msg >> 4;
|
||||
a *= 11;
|
||||
msg++;
|
||||
}
|
||||
|
||||
return (a);
|
||||
}
|
@ -1,926 +0,0 @@
|
||||
/*-
|
||||
* Copyright 2000 Hans Reiser
|
||||
* See README for licensing and copyright details
|
||||
*
|
||||
* Ported to FreeBSD by Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <gnu/reiserfs/reiserfs_fs.h>
|
||||
|
||||
static b_strategy_t reiserfs_bufstrategy;
|
||||
|
||||
/*
|
||||
* Buffer operations for ReiserFS vnodes.
|
||||
* We punt on VOP_BMAP, so we need to do strategy on the file's vnode
|
||||
* rather than the underlying device's.
|
||||
*/
|
||||
static struct buf_ops reiserfs_vnbufops = {
|
||||
.bop_name = "ReiserFS",
|
||||
.bop_strategy = reiserfs_bufstrategy,
|
||||
};
|
||||
|
||||
/* Default io size devuned in super.c */
|
||||
extern int reiserfs_default_io_size;
|
||||
void inode_set_bytes(struct reiserfs_node *ip, off_t bytes);
|
||||
|
||||
/* Args for the create parameter of reiserfs_get_block */
|
||||
#define GET_BLOCK_NO_CREATE 0 /* Don't create new blocks or convert
|
||||
tails */
|
||||
#define GET_BLOCK_CREATE 1 /* Add anything you need to find block */
|
||||
#define GET_BLOCK_NO_HOLE 2 /* Return ENOENT for file holes */
|
||||
#define GET_BLOCK_READ_DIRECT 4 /* Read the tail if indirect item not
|
||||
found */
|
||||
#define GET_BLOCK_NO_ISEM 8 /* i_sem is not held, don't preallocate */
|
||||
#define GET_BLOCK_NO_DANGLE 16 /* Don't leave any transactions running */
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* vnode operations
|
||||
* -------------------------------------------------------------------*/
|
||||
|
||||
int
|
||||
reiserfs_read(struct vop_read_args *ap)
|
||||
{
|
||||
struct uio *uio;
|
||||
struct vnode *vp;
|
||||
struct reiserfs_node *ip;
|
||||
struct reiserfs_sb_info *sbi;
|
||||
|
||||
int error;
|
||||
long size;
|
||||
daddr_t lbn;
|
||||
off_t bytesinfile, offset;
|
||||
|
||||
uio = ap->a_uio;
|
||||
vp = ap->a_vp;
|
||||
ip = VTOI(vp);
|
||||
sbi = ip->i_reiserfs;
|
||||
|
||||
size = sbi->s_blocksize;
|
||||
|
||||
for (error = 0; uio->uio_resid > 0;) {
|
||||
if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0)
|
||||
break;
|
||||
|
||||
/* Compute the logical block number and its offset */
|
||||
lbn = uio->uio_offset / size;
|
||||
offset = uio->uio_offset % size;
|
||||
reiserfs_log(LOG_DEBUG, "logical block number: %ju\n",
|
||||
(intmax_t)lbn);
|
||||
reiserfs_log(LOG_DEBUG, "block offset: %ju\n",
|
||||
(intmax_t)offset);
|
||||
|
||||
/* Read file blocks */
|
||||
reiserfs_log(LOG_DEBUG, "reiserfs_get_block(%ju)\n",
|
||||
(intmax_t)lbn);
|
||||
if ((error = reiserfs_get_block(ip, lbn, offset, uio)) != 0) {
|
||||
reiserfs_log(LOG_DEBUG,
|
||||
"reiserfs_get_block returned the error %d\n",
|
||||
error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
reiserfs_bufstrategy(struct bufobj *bo, struct buf *bp)
|
||||
{
|
||||
struct vnode *vp;
|
||||
int rc;
|
||||
|
||||
vp = bo->bo_private;
|
||||
KASSERT(bo == &vp->v_bufobj, ("BO/VP mismatch: vp %p bo %p != %p",
|
||||
vp, &vp->v_bufobj, bo));
|
||||
rc = VOP_STRATEGY(vp, bp);
|
||||
KASSERT(rc == 0, ("ReiserFS VOP_STRATEGY failed: bp=%p, "
|
||||
"vp=%p, rc=%d", bp, vp, rc));
|
||||
}
|
||||
|
||||
int
|
||||
reiserfs_inactive(struct vop_inactive_args *ap)
|
||||
{
|
||||
int error;
|
||||
struct vnode *vp;
|
||||
struct thread *td;
|
||||
struct reiserfs_node *ip;
|
||||
|
||||
error = 0;
|
||||
vp = ap->a_vp;
|
||||
td = ap->a_td;
|
||||
ip = VTOI(vp);
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "deactivating inode used %d times\n",
|
||||
vp->v_usecount);
|
||||
if (prtactive && vrefcnt(vp) != 0)
|
||||
vprint("ReiserFS/reclaim: pushing active", vp);
|
||||
|
||||
#if 0
|
||||
/* Ignore inodes related to stale file handles. */
|
||||
if (ip->i_mode == 0)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If we are done with the inode, reclaim it so that it can be reused
|
||||
* immediately.
|
||||
*/
|
||||
if (ip->i_mode == 0) {
|
||||
reiserfs_log(LOG_DEBUG, "recyling\n");
|
||||
vrecycle(vp, td);
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
reiserfs_reclaim(struct vop_reclaim_args *ap)
|
||||
{
|
||||
struct reiserfs_node *ip;
|
||||
struct vnode *vp;
|
||||
|
||||
vp = ap->a_vp;
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "reclaiming inode used %d times\n",
|
||||
vp->v_usecount);
|
||||
if (prtactive && vrefcnt(vp) != 0)
|
||||
vprint("ReiserFS/reclaim: pushing active", vp);
|
||||
ip = VTOI(vp);
|
||||
|
||||
/* XXX Update this node (write to the disk) */
|
||||
|
||||
/* Remove the inode from its hash chain. */
|
||||
vfs_hash_remove(vp);
|
||||
|
||||
/* Purge old data structures associated with the inode. */
|
||||
if (ip->i_devvp) {
|
||||
reiserfs_log(LOG_DEBUG, "releasing device (0x%p)\n",
|
||||
ip->i_devvp);
|
||||
vrele(ip->i_devvp);
|
||||
ip->i_devvp = NULL;
|
||||
}
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "free private data\n");
|
||||
FREE(vp->v_data, M_REISERFSNODE);
|
||||
vp->v_data = NULL;
|
||||
vnode_destroy_vobject(vp);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* Functions from linux/fs/reiserfs/inode.c
|
||||
* -------------------------------------------------------------------*/
|
||||
|
||||
static void
|
||||
_make_cpu_key(struct cpu_key *key, int version,
|
||||
uint32_t dirid, uint32_t objectid, off_t offset, int type, int length)
|
||||
{
|
||||
|
||||
key->version = version;
|
||||
|
||||
key->on_disk_key.k_dir_id = dirid;
|
||||
key->on_disk_key.k_objectid = objectid;
|
||||
set_cpu_key_k_offset(key, offset);
|
||||
set_cpu_key_k_type(key, type);
|
||||
key->key_length = length;
|
||||
}
|
||||
|
||||
/*
|
||||
* Take base of inode_key (it comes from inode always) (dirid, objectid)
|
||||
* and version from an inode, set offset and type of key
|
||||
*/
|
||||
void
|
||||
make_cpu_key(struct cpu_key *key, struct reiserfs_node *ip, off_t offset,
|
||||
int type, int length)
|
||||
{
|
||||
|
||||
_make_cpu_key(key, get_inode_item_key_version(ip),
|
||||
le32toh(INODE_PKEY(ip)->k_dir_id),
|
||||
le32toh(INODE_PKEY(ip)->k_objectid),
|
||||
offset, type, length);
|
||||
}
|
||||
|
||||
int
|
||||
reiserfs_get_block(struct reiserfs_node *ip, long block, off_t offset,
|
||||
struct uio *uio)
|
||||
{
|
||||
caddr_t blk = NULL, p;
|
||||
struct cpu_key key;
|
||||
/* unsigned long offset; */
|
||||
INITIALIZE_PATH(path);
|
||||
struct buf *bp, *blk_bp;
|
||||
struct item_head *ih;
|
||||
struct reiserfs_sb_info *sbi;
|
||||
int blocknr, chars, done = 0, ret = 0, args = 0;
|
||||
|
||||
sbi = ip->i_reiserfs;
|
||||
|
||||
/* Prepare the key to look for the 'block'-th block of file */
|
||||
reiserfs_log(LOG_DEBUG, "prepare cpu key\n");
|
||||
make_cpu_key(&key, ip, (off_t)block * sbi->s_blocksize + 1, TYPE_ANY, 3);
|
||||
|
||||
/* research: */
|
||||
reiserfs_log(LOG_DEBUG, "search for position\n");
|
||||
if (search_for_position_by_key(sbi, &key, &path) != POSITION_FOUND) {
|
||||
reiserfs_log(LOG_DEBUG, "position not found\n");
|
||||
pathrelse(&path);
|
||||
#if 0
|
||||
if (blk)
|
||||
kunmap(bh_result->b_page);
|
||||
#endif
|
||||
/*
|
||||
* We do not return ENOENT if there is a hole but page is
|
||||
* uptodate, because it means that there is some MMAPED data
|
||||
* associated with it that is yet to be written to disk.
|
||||
*/
|
||||
if ((args & GET_BLOCK_NO_HOLE)/* &&
|
||||
!PageUptodate(bh_result->b_page)*/)
|
||||
return (ENOENT);
|
||||
return (0);
|
||||
}
|
||||
reiserfs_log(LOG_DEBUG, "position found\n");
|
||||
|
||||
bp = get_last_bp(&path);
|
||||
ih = get_ih(&path);
|
||||
|
||||
if (is_indirect_le_ih(ih)) {
|
||||
off_t xfersize;
|
||||
uint32_t *ind_item = (uint32_t *)B_I_PITEM(bp, ih);
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "item is INDIRECT\n");
|
||||
|
||||
blocknr = get_block_num(ind_item, path.pos_in_item);
|
||||
reiserfs_log(LOG_DEBUG, "block number: %d "
|
||||
"(ind_item=%p, pos_in_item=%u)\n",
|
||||
blocknr, ind_item, path.pos_in_item);
|
||||
|
||||
xfersize = MIN(sbi->s_blocksize - offset,
|
||||
ip->i_size - uio->uio_offset);
|
||||
xfersize = MIN(xfersize, uio->uio_resid);
|
||||
|
||||
if (blocknr) {
|
||||
ret = bread(sbi->s_devvp,
|
||||
blocknr * btodb(sbi->s_blocksize),
|
||||
sbi->s_blocksize, NOCRED, &blk_bp);
|
||||
reiserfs_log(LOG_DEBUG, "xfersize: %ju\n",
|
||||
(intmax_t)xfersize);
|
||||
ret = uiomove(blk_bp->b_data + offset, xfersize, uio);
|
||||
brelse(blk_bp);
|
||||
} else {
|
||||
/*
|
||||
* We do not return ENOENT if there is a hole but
|
||||
* page is uptodate, because it means That there
|
||||
* is some MMAPED data associated with it that
|
||||
* is yet to be written to disk.
|
||||
*/
|
||||
if ((args & GET_BLOCK_NO_HOLE)/* &&
|
||||
!PageUptodate(bh_result->b_page)*/)
|
||||
ret = (ENOENT);
|
||||
|
||||
/* Skip this hole */
|
||||
uio->uio_resid -= xfersize;
|
||||
uio->uio_offset += xfersize;
|
||||
}
|
||||
|
||||
pathrelse(&path);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "item should be DIRECT\n");
|
||||
|
||||
#if 0
|
||||
/* Requested data are in direct item(s) */
|
||||
if (!(args & GET_BLOCK_READ_DIRECT)) {
|
||||
/*
|
||||
* We are called by bmap. FIXME: we can not map block of
|
||||
* file when it is stored in direct item(s)
|
||||
*/
|
||||
pathrelse(&path);
|
||||
#if 0
|
||||
if (blk)
|
||||
kunmap(bh_result->b_page);
|
||||
#endif
|
||||
return (ENOENT);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* If we've got a direct item, and the buffer or page was uptodate, we
|
||||
* don't want to pull data off disk again. Skip to the end, where we
|
||||
* map the buffer and return
|
||||
*/
|
||||
if (buffer_uptodate(bh_result)) {
|
||||
goto finished;
|
||||
} else
|
||||
/*
|
||||
* grab_tail_page can trigger calls to reiserfs_get_block
|
||||
* on up to date pages without any buffers. If the page
|
||||
* is up to date, we don't want read old data off disk.
|
||||
* Set the up to date bit on the buffer instead and jump
|
||||
* to the end
|
||||
*/
|
||||
if (!bh_result->b_page || PageUptodate(bh_result->b_page)) {
|
||||
set_buffer_uptodate(bh_result);
|
||||
goto finished;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* Read file tail into part of page */
|
||||
offset = (cpu_key_k_offset(&key) - 1) & (PAGE_CACHE_SIZE - 1);
|
||||
fs_gen = get_generation(ip->i_reiserfs);
|
||||
copy_item_head(&tmp_ih, ih);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* We only want to kmap if we are reading the tail into the page. this
|
||||
* is not the common case, so we don't kmap until we are sure we need
|
||||
* to. But, this means the item might move if kmap schedules
|
||||
*/
|
||||
if (!blk) {
|
||||
blk = (char *)kmap(bh_result->b_page);
|
||||
if (fs_changed (fs_gen, sbi) && item_moved(&tmp_ih, &path))
|
||||
goto research;
|
||||
}
|
||||
blk += offset;
|
||||
memset(blk, 0, sbi->s_blocksize);
|
||||
#endif
|
||||
if (!blk) {
|
||||
reiserfs_log(LOG_DEBUG, "allocating buffer\n");
|
||||
blk = malloc(ip->i_size, M_REISERFSNODE, M_WAITOK | M_ZERO);
|
||||
if (!blk)
|
||||
return (ENOMEM);
|
||||
}
|
||||
/* p += offset; */
|
||||
|
||||
p = blk;
|
||||
do {
|
||||
if (!is_direct_le_ih(ih)) {
|
||||
reiserfs_log(LOG_ERR, "BUG\n");
|
||||
return (ENOENT); /* XXX Wrong error code */
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure we don't read more bytes than actually exist
|
||||
* in the file. This can happen in odd cases where i_size
|
||||
* isn't correct, and when direct item padding results in
|
||||
* a few extra bytes at the end of the direct item
|
||||
*/
|
||||
if ((le_ih_k_offset(ih) + path.pos_in_item) > ip->i_size)
|
||||
break;
|
||||
|
||||
if ((le_ih_k_offset(ih) - 1 + ih_item_len(ih)) > ip->i_size) {
|
||||
chars = ip->i_size - (le_ih_k_offset(ih) - 1) -
|
||||
path.pos_in_item;
|
||||
done = 1;
|
||||
} else {
|
||||
chars = ih_item_len(ih) - path.pos_in_item;
|
||||
}
|
||||
reiserfs_log(LOG_DEBUG, "copying %d bytes\n", chars);
|
||||
memcpy(p, B_I_PITEM(bp, ih) + path.pos_in_item, chars);
|
||||
if (done) {
|
||||
reiserfs_log(LOG_DEBUG, "copy done\n");
|
||||
break;
|
||||
}
|
||||
|
||||
p += chars;
|
||||
|
||||
if (PATH_LAST_POSITION(&path) != (B_NR_ITEMS(bp) - 1))
|
||||
/*
|
||||
* We done, if read direct item is not the last
|
||||
* item of node
|
||||
* FIXME: we could try to check right delimiting
|
||||
* key to see whether direct item continues in
|
||||
* the right neighbor or rely on i_size
|
||||
*/
|
||||
break;
|
||||
|
||||
/* Update key to look for the next piece */
|
||||
set_cpu_key_k_offset(&key, cpu_key_k_offset(&key) + chars);
|
||||
if (search_for_position_by_key(sbi, &key, &path) !=
|
||||
POSITION_FOUND)
|
||||
/*
|
||||
* We read something from tail, even if now we got
|
||||
* IO_ERROR
|
||||
*/
|
||||
break;
|
||||
|
||||
bp = get_last_bp(&path);
|
||||
ih = get_ih(&path);
|
||||
} while (1);
|
||||
|
||||
/* finished: */
|
||||
pathrelse(&path);
|
||||
/*
|
||||
* This buffer has valid data, but isn't valid for io. mapping it to
|
||||
* block #0 tells the rest of reiserfs it just has a tail in it
|
||||
*/
|
||||
ret = uiomove(blk, ip->i_size, uio);
|
||||
free(blk, M_REISERFSNODE);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute real number of used bytes by file
|
||||
* Following three functions can go away when we'll have enough space in
|
||||
* stat item
|
||||
*/
|
||||
static int
|
||||
real_space_diff(struct reiserfs_node *ip, int sd_size)
|
||||
{
|
||||
int bytes;
|
||||
off_t blocksize = ip->i_reiserfs->s_blocksize;
|
||||
|
||||
if (S_ISLNK(ip->i_mode) || S_ISDIR(ip->i_mode))
|
||||
return (sd_size);
|
||||
|
||||
/* End of file is also in full block with indirect reference, so round
|
||||
* up to the next block.
|
||||
*
|
||||
* There is just no way to know if the tail is actually packed on the
|
||||
* file, so we have to assume it isn't. When we pack the tail, we add
|
||||
* 4 bytes to pretend there really is an unformatted node pointer. */
|
||||
bytes = ((ip->i_size + (blocksize - 1)) >>
|
||||
ip->i_reiserfs->s_blocksize_bits) * UNFM_P_SIZE + sd_size;
|
||||
|
||||
return (bytes);
|
||||
}
|
||||
|
||||
static inline off_t
|
||||
to_real_used_space(struct reiserfs_node *ip, unsigned long blocks, int sd_size)
|
||||
{
|
||||
|
||||
if (S_ISLNK(ip->i_mode) || S_ISDIR(ip->i_mode)) {
|
||||
return ip->i_size + (off_t)(real_space_diff(ip, sd_size));
|
||||
}
|
||||
|
||||
return ((off_t)real_space_diff(ip, sd_size)) + (((off_t)blocks) << 9);
|
||||
}
|
||||
|
||||
void
|
||||
inode_set_bytes(struct reiserfs_node *ip, off_t bytes)
|
||||
{
|
||||
|
||||
ip->i_blocks = bytes >> 9;
|
||||
ip->i_bytes = bytes & 511;
|
||||
}
|
||||
|
||||
/* Called by read_locked_inode */
|
||||
static void
|
||||
init_inode(struct reiserfs_node *ip, struct path *path)
|
||||
{
|
||||
struct buf *bp;
|
||||
struct item_head *ih;
|
||||
uint32_t rdev;
|
||||
|
||||
bp = PATH_PLAST_BUFFER(path);
|
||||
ih = PATH_PITEM_HEAD(path);
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "copy the key (objectid=%d, dirid=%d)\n",
|
||||
ih->ih_key.k_objectid, ih->ih_key.k_dir_id);
|
||||
copy_key(INODE_PKEY(ip), &(ih->ih_key));
|
||||
/* ip->i_blksize = reiserfs_default_io_size; */
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "reset some inode structure members\n");
|
||||
REISERFS_I(ip)->i_flags = 0;
|
||||
#if 0
|
||||
REISERFS_I(ip)->i_prealloc_block = 0;
|
||||
REISERFS_I(ip)->i_prealloc_count = 0;
|
||||
REISERFS_I(ip)->i_trans_id = 0;
|
||||
REISERFS_I(ip)->i_jl = NULL;
|
||||
REISERFS_I(ip)->i_acl_access = NULL;
|
||||
REISERFS_I(ip)->i_acl_default = NULL;
|
||||
#endif
|
||||
|
||||
if (stat_data_v1(ih)) {
|
||||
reiserfs_log(LOG_DEBUG, "reiserfs/init_inode: stat data v1\n");
|
||||
struct stat_data_v1 *sd;
|
||||
unsigned long blocks;
|
||||
|
||||
sd = (struct stat_data_v1 *)B_I_PITEM(bp, ih);
|
||||
|
||||
reiserfs_log(LOG_DEBUG,
|
||||
"reiserfs/init_inode: filling more members\n");
|
||||
set_inode_item_key_version(ip, KEY_FORMAT_3_5);
|
||||
set_inode_sd_version(ip, STAT_DATA_V1);
|
||||
ip->i_mode = sd_v1_mode(sd);
|
||||
ip->i_nlink = sd_v1_nlink(sd);
|
||||
ip->i_uid = sd_v1_uid(sd);
|
||||
ip->i_gid = sd_v1_gid(sd);
|
||||
ip->i_size = sd_v1_size(sd);
|
||||
ip->i_atime.tv_sec = sd_v1_atime(sd);
|
||||
ip->i_mtime.tv_sec = sd_v1_mtime(sd);
|
||||
ip->i_ctime.tv_sec = sd_v1_ctime(sd);
|
||||
ip->i_atime.tv_nsec = 0;
|
||||
ip->i_ctime.tv_nsec = 0;
|
||||
ip->i_mtime.tv_nsec = 0;
|
||||
|
||||
reiserfs_log(LOG_DEBUG, " mode = %08x\n", ip->i_mode);
|
||||
reiserfs_log(LOG_DEBUG, " nlink = %d\n", ip->i_nlink);
|
||||
reiserfs_log(LOG_DEBUG, " owner = %d:%d\n", ip->i_uid,
|
||||
ip->i_gid);
|
||||
reiserfs_log(LOG_DEBUG, " size = %ju\n",
|
||||
(intmax_t)ip->i_size);
|
||||
reiserfs_log(LOG_DEBUG, " atime = %jd\n",
|
||||
(intmax_t)ip->i_atime.tv_sec);
|
||||
reiserfs_log(LOG_DEBUG, " mtime = %jd\n",
|
||||
(intmax_t)ip->i_mtime.tv_sec);
|
||||
reiserfs_log(LOG_DEBUG, " ctime = %jd\n",
|
||||
(intmax_t)ip->i_ctime.tv_sec);
|
||||
|
||||
ip->i_blocks = sd_v1_blocks(sd);
|
||||
ip->i_generation = le32toh(INODE_PKEY(ip)->k_dir_id);
|
||||
blocks = (ip->i_size + 511) >> 9;
|
||||
blocks = _ROUND_UP(blocks, ip->i_reiserfs->s_blocksize >> 9);
|
||||
if (ip->i_blocks > blocks) {
|
||||
/*
|
||||
* There was a bug in <= 3.5.23 when i_blocks could
|
||||
* take negative values. Starting from 3.5.17 this
|
||||
* value could even be stored in stat data. For such
|
||||
* files we set i_blocks based on file size. Just 2
|
||||
* notes: this can be wrong for sparce files. On-disk
|
||||
* value will be only updated if file's inode will
|
||||
* ever change.
|
||||
*/
|
||||
ip->i_blocks = blocks;
|
||||
}
|
||||
|
||||
rdev = sd_v1_rdev(sd);
|
||||
REISERFS_I(ip)->i_first_direct_byte =
|
||||
sd_v1_first_direct_byte(sd);
|
||||
|
||||
/*
|
||||
* An early bug in the quota code can give us an odd number
|
||||
* for the block count. This is incorrect, fix it here.
|
||||
*/
|
||||
if (ip->i_blocks & 1) {
|
||||
ip->i_blocks++ ;
|
||||
}
|
||||
inode_set_bytes(ip, to_real_used_space(ip, ip->i_blocks,
|
||||
SD_V1_SIZE));
|
||||
|
||||
/*
|
||||
* nopack is initially zero for v1 objects. For v2 objects,
|
||||
* nopack is initialised from sd_attrs
|
||||
*/
|
||||
REISERFS_I(ip)->i_flags &= ~i_nopack_mask;
|
||||
reiserfs_log(LOG_DEBUG, "...done\n");
|
||||
} else {
|
||||
reiserfs_log(LOG_DEBUG, "stat data v2\n");
|
||||
/*
|
||||
* New stat data found, but object may have old items
|
||||
* (directories and symlinks)
|
||||
*/
|
||||
struct stat_data *sd = (struct stat_data *)B_I_PITEM(bp, ih);
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "filling more members\n");
|
||||
ip->i_mode = sd_v2_mode(sd);
|
||||
ip->i_nlink = sd_v2_nlink(sd);
|
||||
ip->i_uid = sd_v2_uid(sd);
|
||||
ip->i_size = sd_v2_size(sd);
|
||||
ip->i_gid = sd_v2_gid(sd);
|
||||
ip->i_mtime.tv_sec = sd_v2_mtime(sd);
|
||||
ip->i_atime.tv_sec = sd_v2_atime(sd);
|
||||
ip->i_ctime.tv_sec = sd_v2_ctime(sd);
|
||||
ip->i_ctime.tv_nsec = 0;
|
||||
ip->i_mtime.tv_nsec = 0;
|
||||
ip->i_atime.tv_nsec = 0;
|
||||
|
||||
reiserfs_log(LOG_DEBUG, " mode = %08x\n", ip->i_mode);
|
||||
reiserfs_log(LOG_DEBUG, " nlink = %d\n", ip->i_nlink);
|
||||
reiserfs_log(LOG_DEBUG, " owner = %d:%d\n", ip->i_uid,
|
||||
ip->i_gid);
|
||||
reiserfs_log(LOG_DEBUG, " size = %ju\n",
|
||||
(intmax_t)ip->i_size);
|
||||
reiserfs_log(LOG_DEBUG, " atime = %jd\n",
|
||||
(intmax_t)ip->i_atime.tv_sec);
|
||||
reiserfs_log(LOG_DEBUG, " mtime = %jd\n",
|
||||
(intmax_t)ip->i_mtime.tv_sec);
|
||||
reiserfs_log(LOG_DEBUG, " ctime = %jd\n",
|
||||
(intmax_t)ip->i_ctime.tv_sec);
|
||||
|
||||
ip->i_blocks = sd_v2_blocks(sd);
|
||||
rdev = sd_v2_rdev(sd);
|
||||
reiserfs_log(LOG_DEBUG, " blocks = %u\n", ip->i_blocks);
|
||||
|
||||
if (S_ISCHR(ip->i_mode) || S_ISBLK(ip->i_mode))
|
||||
ip->i_generation = le32toh(INODE_PKEY(ip)->k_dir_id);
|
||||
else
|
||||
ip->i_generation = sd_v2_generation(sd);
|
||||
|
||||
if (S_ISDIR(ip->i_mode) || S_ISLNK(ip->i_mode))
|
||||
set_inode_item_key_version(ip, KEY_FORMAT_3_5);
|
||||
else
|
||||
set_inode_item_key_version(ip, KEY_FORMAT_3_6);
|
||||
|
||||
REISERFS_I(ip)->i_first_direct_byte = 0;
|
||||
set_inode_sd_version(ip, STAT_DATA_V2);
|
||||
inode_set_bytes(ip, to_real_used_space(ip, ip->i_blocks,
|
||||
SD_V2_SIZE));
|
||||
|
||||
/*
|
||||
* Read persistent inode attributes from sd and initalise
|
||||
* generic inode flags from them
|
||||
*/
|
||||
REISERFS_I(ip)->i_attrs = sd_v2_attrs(sd);
|
||||
sd_attrs_to_i_attrs(sd_v2_attrs(sd), ip);
|
||||
reiserfs_log(LOG_DEBUG, "...done\n");
|
||||
}
|
||||
|
||||
pathrelse(path);
|
||||
if (S_ISREG(ip->i_mode)) {
|
||||
reiserfs_log(LOG_DEBUG, "this inode is a regular file\n");
|
||||
//ip->i_op = &reiserfs_file_ip_operations;
|
||||
//ip->i_fop = &reiserfs_file_operations;
|
||||
//ip->i_mapping->a_ops = &reiserfs_address_space_operations ;
|
||||
} else if (S_ISDIR(ip->i_mode)) {
|
||||
reiserfs_log(LOG_DEBUG, "this inode is a directory\n");
|
||||
//ip->i_op = &reiserfs_dir_ip_operations;
|
||||
//ip->i_fop = &reiserfs_dir_operations;
|
||||
} else if (S_ISLNK(ip->i_mode)) {
|
||||
reiserfs_log(LOG_DEBUG, "this inode is a symlink\n");
|
||||
//ip->i_op = &reiserfs_symlink_ip_operations;
|
||||
//ip->i_mapping->a_ops = &reiserfs_address_space_operations;
|
||||
} else {
|
||||
reiserfs_log(LOG_DEBUG, "this inode is something unknown in "
|
||||
"this universe\n");
|
||||
ip->i_blocks = 0;
|
||||
//ip->i_op = &reiserfs_special_ip_operations;
|
||||
//init_special_ip(ip, ip->i_mode, new_decode_dev(rdev));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* reiserfs_read_locked_inode is called to read the inode off disk, and
|
||||
* it does a make_bad_inode when things go wrong. But, we need to make
|
||||
* sure and clear the key in the private portion of the inode, otherwise
|
||||
* a corresponding iput might try to delete whatever object the inode
|
||||
* last represented.
|
||||
*/
|
||||
static void
|
||||
reiserfs_make_bad_inode(struct reiserfs_node *ip) {
|
||||
|
||||
memset(INODE_PKEY(ip), 0, KEY_SIZE);
|
||||
//make_bad_inode(inode);
|
||||
}
|
||||
|
||||
void
|
||||
reiserfs_read_locked_inode(struct reiserfs_node *ip,
|
||||
struct reiserfs_iget_args *args)
|
||||
{
|
||||
INITIALIZE_PATH(path_to_sd);
|
||||
struct cpu_key key;
|
||||
unsigned long dirino;
|
||||
int retval;
|
||||
|
||||
dirino = args->dirid;
|
||||
|
||||
/*
|
||||
* Set version 1, version 2 could be used too, because stat data
|
||||
* key is the same in both versions
|
||||
*/
|
||||
key.version = KEY_FORMAT_3_5;
|
||||
key.on_disk_key.k_dir_id = dirino;
|
||||
key.on_disk_key.k_objectid = ip->i_number;
|
||||
key.on_disk_key.u.k_offset_v1.k_offset = SD_OFFSET;
|
||||
key.on_disk_key.u.k_offset_v1.k_uniqueness = SD_UNIQUENESS;
|
||||
|
||||
/* Look for the object's stat data */
|
||||
retval = search_item(ip->i_reiserfs, &key, &path_to_sd);
|
||||
if (retval == IO_ERROR) {
|
||||
reiserfs_log(LOG_ERR,
|
||||
"I/O failure occured trying to find stat"
|
||||
"data %u/%u\n",
|
||||
key.on_disk_key.k_dir_id, key.on_disk_key.k_objectid);
|
||||
reiserfs_make_bad_inode(ip);
|
||||
return;
|
||||
}
|
||||
if (retval != ITEM_FOUND) {
|
||||
/*
|
||||
* A stale NFS handle can trigger this without it being
|
||||
* an error
|
||||
*/
|
||||
reiserfs_log(LOG_ERR,
|
||||
"item not found (objectid=%u, dirid=%u)\n",
|
||||
key.on_disk_key.k_objectid, key.on_disk_key.k_dir_id);
|
||||
pathrelse(&path_to_sd);
|
||||
reiserfs_make_bad_inode(ip);
|
||||
ip->i_nlink = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
init_inode(ip, &path_to_sd);
|
||||
|
||||
/*
|
||||
* It is possible that knfsd is trying to access inode of a file
|
||||
* that is being removed from the disk by some other thread. As
|
||||
* we update sd on unlink all that is required is to check for
|
||||
* nlink here. This bug was first found by Sizif when debugging
|
||||
* SquidNG/Butterfly, forgotten, and found again after Philippe
|
||||
* Gramoulle <philippe.gramoulle@mmania.com> reproduced it.
|
||||
*
|
||||
* More logical fix would require changes in fs/inode.c:iput() to
|
||||
* remove inode from hash-table _after_ fs cleaned disk stuff up and
|
||||
* in iget() to return NULL if I_FREEING inode is found in hash-table.
|
||||
*/
|
||||
/*
|
||||
* Currently there is one place where it's ok to meet inode with
|
||||
* nlink == 0: processing of open-unlinked and half-truncated files
|
||||
* during mount (fs/reiserfs/super.c:finish_unfinished()).
|
||||
*/
|
||||
if((ip->i_nlink == 0) &&
|
||||
!REISERFS_SB(ip->i_reiserfs)->s_is_unlinked_ok ) {
|
||||
reiserfs_log(LOG_WARNING, "dead inode read from disk. This is "
|
||||
"likely to be race with knfsd. Ignore");
|
||||
reiserfs_make_bad_inode(ip);
|
||||
}
|
||||
|
||||
/* Init inode should be relsing */
|
||||
reiserfs_check_path(&path_to_sd);
|
||||
}
|
||||
|
||||
int
|
||||
reiserfs_iget(
|
||||
struct mount *mp, const struct cpu_key *key,
|
||||
struct vnode **vpp, struct thread *td)
|
||||
{
|
||||
int error, flags;
|
||||
struct cdev *dev;
|
||||
struct vnode *vp;
|
||||
struct reiserfs_node *ip;
|
||||
struct reiserfs_mount *rmp;
|
||||
|
||||
struct reiserfs_iget_args args;
|
||||
|
||||
//restart:
|
||||
/* Check if the inode cache contains it */
|
||||
// XXX LK_EXCLUSIVE ?
|
||||
flags = LK_EXCLUSIVE;
|
||||
error = vfs_hash_get(mp, key->on_disk_key.k_objectid, flags,
|
||||
td, vpp, NULL, NULL);
|
||||
if (error || *vpp != NULL)
|
||||
return (error);
|
||||
|
||||
rmp = VFSTOREISERFS(mp);
|
||||
dev = rmp->rm_dev;
|
||||
|
||||
/*
|
||||
* If this MALLOC() is performed after the getnewvnode() it might
|
||||
* block, leaving a vnode with a NULL v_data to be found by
|
||||
* reiserfs_sync() if a sync happens to fire right then, which
|
||||
* will cause a panic because reiserfs_sync() blindly dereferences
|
||||
* vp->v_data (as well it should).
|
||||
*/
|
||||
reiserfs_log(LOG_DEBUG, "malloc(struct reiserfs_node)\n");
|
||||
ip = malloc(sizeof(struct reiserfs_node), M_REISERFSNODE,
|
||||
M_WAITOK | M_ZERO);
|
||||
|
||||
/* Allocate a new vnode/inode. */
|
||||
reiserfs_log(LOG_DEBUG, "getnewvnode\n");
|
||||
if ((error =
|
||||
getnewvnode("reiserfs", mp, &reiserfs_vnodeops, &vp)) != 0) {
|
||||
*vpp = NULL;
|
||||
free(ip, M_REISERFSNODE);
|
||||
reiserfs_log(LOG_DEBUG, "getnewvnode FAILED\n");
|
||||
return (error);
|
||||
}
|
||||
|
||||
args.dirid = key->on_disk_key.k_dir_id;
|
||||
args.objectid = key->on_disk_key.k_objectid;
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "filling *ip\n");
|
||||
vp->v_data = ip;
|
||||
ip->i_vnode = vp;
|
||||
ip->i_dev = dev;
|
||||
ip->i_number = args.objectid;
|
||||
ip->i_ino = args.dirid;
|
||||
ip->i_reiserfs = rmp->rm_reiserfs;
|
||||
|
||||
vp->v_bufobj.bo_ops = &reiserfs_vnbufops;
|
||||
vp->v_bufobj.bo_private = vp;
|
||||
|
||||
/* If this is the root node, set the VV_ROOT flag */
|
||||
if (ip->i_number == REISERFS_ROOT_OBJECTID &&
|
||||
ip->i_ino == REISERFS_ROOT_PARENT_OBJECTID)
|
||||
vp->v_vflag |= VV_ROOT;
|
||||
|
||||
#if 0
|
||||
if (VOP_LOCK(vp, LK_EXCLUSIVE, td) != 0)
|
||||
panic("reiserfs/iget: unexpected lock failure");
|
||||
|
||||
/*
|
||||
* Exclusively lock the vnode before adding to hash. Note, that we
|
||||
* must not release nor downgrade the lock (despite flags argument
|
||||
* says) till it is fully initialized.
|
||||
*/
|
||||
lockmgr(vp->v_vnlock, LK_EXCLUSIVE, (struct mtx *)0, td);
|
||||
#endif
|
||||
|
||||
error = vfs_hash_insert(vp, key->on_disk_key.k_objectid, flags,
|
||||
td, vpp, NULL, NULL);
|
||||
if (error || *vpp != NULL)
|
||||
return (error);
|
||||
|
||||
/* Read the inode */
|
||||
reiserfs_log(LOG_DEBUG, "call reiserfs_read_locked_inode ("
|
||||
"objectid=%d,dirid=%d)\n", args.objectid, args.dirid);
|
||||
reiserfs_read_locked_inode(ip, &args);
|
||||
|
||||
ip->i_devvp = rmp->rm_devvp;
|
||||
VREF(ip->i_devvp);
|
||||
|
||||
switch(vp->v_type = IFTOVT(ip->i_mode)) {
|
||||
case VBLK:
|
||||
reiserfs_log(LOG_DEBUG, "vnode type VBLK\n");
|
||||
vp->v_op = &reiserfs_specops;
|
||||
break;
|
||||
#if 0
|
||||
case VCHR:
|
||||
reiserfs_log(LOG_DEBUG, "vnode type VCHR\n");
|
||||
vp->v_op = &reiserfs_specops;
|
||||
vp = addaliasu(vp, ip->i_rdev);
|
||||
ip->i_vnode = vp;
|
||||
break;
|
||||
case VFIFO:
|
||||
reiserfs_log(LOG_DEBUG, "vnode type VFIFO\n");
|
||||
vp->v_op = reiserfs_fifoop_p;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
*vpp = vp;
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
sd_attrs_to_i_attrs(uint16_t sd_attrs, struct reiserfs_node *ip)
|
||||
{
|
||||
|
||||
if (reiserfs_attrs(ip->i_reiserfs)) {
|
||||
#if 0
|
||||
if (sd_attrs & REISERFS_SYNC_FL)
|
||||
ip->i_flags |= S_SYNC;
|
||||
else
|
||||
ip->i_flags &= ~S_SYNC;
|
||||
#endif
|
||||
if (sd_attrs & REISERFS_IMMUTABLE_FL)
|
||||
ip->i_flags |= IMMUTABLE;
|
||||
else
|
||||
ip->i_flags &= ~IMMUTABLE;
|
||||
if (sd_attrs & REISERFS_APPEND_FL)
|
||||
ip->i_flags |= APPEND;
|
||||
else
|
||||
ip->i_flags &= ~APPEND;
|
||||
#if 0
|
||||
if (sd_attrs & REISERFS_NOATIME_FL)
|
||||
ip->i_flags |= S_NOATIME;
|
||||
else
|
||||
ip->i_flags &= ~S_NOATIME;
|
||||
if (sd_attrs & REISERFS_NOTAIL_FL)
|
||||
REISERFS_I(ip)->i_flags |= i_nopack_mask;
|
||||
else
|
||||
REISERFS_I(ip)->i_flags &= ~i_nopack_mask;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
i_attrs_to_sd_attrs(struct reiserfs_node *ip, uint16_t *sd_attrs)
|
||||
{
|
||||
|
||||
if (reiserfs_attrs(ip->i_reiserfs)) {
|
||||
#if 0
|
||||
if (ip->i_flags & S_SYNC)
|
||||
*sd_attrs |= REISERFS_SYNC_FL;
|
||||
else
|
||||
*sd_attrs &= ~REISERFS_SYNC_FL;
|
||||
#endif
|
||||
if (ip->i_flags & IMMUTABLE)
|
||||
*sd_attrs |= REISERFS_IMMUTABLE_FL;
|
||||
else
|
||||
*sd_attrs &= ~REISERFS_IMMUTABLE_FL;
|
||||
if (ip->i_flags & APPEND)
|
||||
*sd_attrs |= REISERFS_APPEND_FL;
|
||||
else
|
||||
*sd_attrs &= ~REISERFS_APPEND_FL;
|
||||
#if 0
|
||||
if (ip->i_flags & S_NOATIME)
|
||||
*sd_attrs |= REISERFS_NOATIME_FL;
|
||||
else
|
||||
*sd_attrs &= ~REISERFS_NOATIME_FL;
|
||||
if (REISERFS_I(ip)->i_flags & i_nopack_mask)
|
||||
*sd_attrs |= REISERFS_NOTAIL_FL;
|
||||
else
|
||||
*sd_attrs &= ~REISERFS_NOTAIL_FL;
|
||||
#endif
|
||||
}
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
/*-
|
||||
* Copyright 2000 Hans Reiser
|
||||
* See README for licensing and copyright details
|
||||
*
|
||||
* Ported to FreeBSD by Jean-Sébastien Pédron <jspedron@club-internet.fr>
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <gnu/reiserfs/reiserfs_fs.h>
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* Stat data functions
|
||||
* -------------------------------------------------------------------*/
|
||||
|
||||
static int
|
||||
sd_bytes_number(struct item_head *ih, int block_size)
|
||||
{
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct item_operations stat_data_ops = {
|
||||
.bytes_number = sd_bytes_number,
|
||||
//.decrement_key = sd_decrement_key,
|
||||
//.is_left_mergeable = sd_is_left_mergeable,
|
||||
//.print_item = sd_print_item,
|
||||
//.check_item = sd_check_item,
|
||||
|
||||
//.create_vi = sd_create_vi,
|
||||
//.check_left = sd_check_left,
|
||||
//.check_right = sd_check_right,
|
||||
//.part_size = sd_part_size,
|
||||
//.unit_num = sd_unit_num,
|
||||
//.print_vi = sd_print_vi
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* Direct item functions
|
||||
* -------------------------------------------------------------------*/
|
||||
|
||||
static int
|
||||
direct_bytes_number(struct item_head *ih, int block_size)
|
||||
{
|
||||
|
||||
return (ih_item_len(ih));
|
||||
}
|
||||
|
||||
struct item_operations direct_ops = {
|
||||
.bytes_number = direct_bytes_number,
|
||||
//.decrement_key = direct_decrement_key,
|
||||
//.is_left_mergeable = direct_is_left_mergeable,
|
||||
//.print_item = direct_print_item,
|
||||
//.check_item = direct_check_item,
|
||||
|
||||
//.create_vi = direct_create_vi,
|
||||
//.check_left = direct_check_left,
|
||||
//.check_right = direct_check_right,
|
||||
//.part_size = direct_part_size,
|
||||
//.unit_num = direct_unit_num,
|
||||
//.print_vi = direct_print_vi
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* Indirect item functions
|
||||
* -------------------------------------------------------------------*/
|
||||
|
||||
static int
|
||||
indirect_bytes_number(struct item_head *ih, int block_size)
|
||||
{
|
||||
|
||||
return (ih_item_len(ih) / UNFM_P_SIZE * block_size);
|
||||
}
|
||||
|
||||
struct item_operations indirect_ops = {
|
||||
.bytes_number = indirect_bytes_number,
|
||||
//.decrement_key = indirect_decrement_key,
|
||||
//.is_left_mergeable = indirect_is_left_mergeable,
|
||||
//.print_item = indirect_print_item,
|
||||
//.check_item = indirect_check_item,
|
||||
|
||||
//.create_vi = indirect_create_vi,
|
||||
//.check_left = indirect_check_left,
|
||||
//.check_right = indirect_check_right,
|
||||
//.part_size = indirect_part_size,
|
||||
//.unit_num = indirect_unit_num,
|
||||
//.print_vi = indirect_print_vi
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* Direntry functions
|
||||
* -------------------------------------------------------------------*/
|
||||
|
||||
static int
|
||||
direntry_bytes_number(struct item_head *ih, int block_size)
|
||||
{
|
||||
|
||||
reiserfs_log(LOG_WARNING, "bytes number is asked for direntry\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct item_operations direntry_ops = {
|
||||
.bytes_number = direntry_bytes_number,
|
||||
//.decrement_key = direntry_decrement_key,
|
||||
//.is_left_mergeable = direntry_is_left_mergeable,
|
||||
//.print_item = direntry_print_item,
|
||||
//.check_item = direntry_check_item,
|
||||
|
||||
//.create_vi = direntry_create_vi,
|
||||
//.check_left = direntry_check_left,
|
||||
//.check_right = direntry_check_right,
|
||||
//.part_size = direntry_part_size,
|
||||
//.unit_num = direntry_unit_num,
|
||||
//.print_vi = direntry_print_vi
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* Error catching functions to catch errors caused by incorrect item
|
||||
* types.
|
||||
* -------------------------------------------------------------------*/
|
||||
|
||||
static int
|
||||
errcatch_bytes_number(struct item_head *ih, int block_size)
|
||||
{
|
||||
|
||||
reiserfs_log(LOG_WARNING, "invalid item type observed, run fsck ASAP");
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct item_operations errcatch_ops = {
|
||||
errcatch_bytes_number,
|
||||
//errcatch_decrement_key,
|
||||
//errcatch_is_left_mergeable,
|
||||
//errcatch_print_item,
|
||||
//errcatch_check_item,
|
||||
|
||||
//errcatch_create_vi,
|
||||
//errcatch_check_left,
|
||||
//errcatch_check_right,
|
||||
//errcatch_part_size,
|
||||
//errcatch_unit_num,
|
||||
//errcatch_print_vi
|
||||
};
|
||||
|
||||
#if !(TYPE_STAT_DATA == 0 && TYPE_INDIRECT == 1 && \
|
||||
TYPE_DIRECT == 2 && TYPE_DIRENTRY == 3)
|
||||
#error
|
||||
#endif
|
||||
|
||||
struct item_operations *item_ops[TYPE_ANY + 1] = {
|
||||
&stat_data_ops,
|
||||
&indirect_ops,
|
||||
&direct_ops,
|
||||
&direntry_ops,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
&errcatch_ops /* This is to catch errors with invalid type (15th
|
||||
entry for TYPE_ANY) */
|
||||
};
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000 Hans Reiser
|
||||
* See README for licensing and copyright details
|
||||
*
|
||||
* Ported to FreeBSD by Jean-Sébastien Pédron <jspedron@club-internet.fr>
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _GNU_REISERFS_REISERFS_MOUNT_H
|
||||
#define _GNU_REISERFS_REISERFS_MOUNT_H
|
||||
|
||||
#define REISERFS_FOR_FREEBSD_VERSION "0.1.6"
|
||||
|
||||
#if defined(_KERNEL)
|
||||
|
||||
#ifdef MALLOC_DECLARE
|
||||
MALLOC_DECLARE(M_REISERFSMNT);
|
||||
MALLOC_DECLARE(M_REISERFSPATH);
|
||||
MALLOC_DECLARE(M_REISERFSNODE);
|
||||
MALLOC_DECLARE(M_REISERFSCOOKIES);
|
||||
#endif
|
||||
|
||||
/* This structure describes the ReiserFS specific mount structure data. */
|
||||
struct reiserfs_mount {
|
||||
struct mount *rm_mountp;
|
||||
struct cdev *rm_dev;
|
||||
struct vnode *rm_devvp;
|
||||
|
||||
struct reiserfs_sb_info *rm_reiserfs;
|
||||
|
||||
struct g_consumer *rm_cp;
|
||||
struct bufobj *rm_bo;
|
||||
};
|
||||
|
||||
/* Convert mount ptr to reiserfs_mount ptr. */
|
||||
#define VFSTOREISERFS(mp) ((struct reiserfs_mount *)((mp)->mnt_data))
|
||||
|
||||
#endif /* defined(_KERNEL) */
|
||||
|
||||
/* Arguments to mount ReiserFS filesystems. */
|
||||
struct reiserfs_args {
|
||||
char *fspec; /* blocks special holding the fs to mount */
|
||||
struct export_args export; /* network export information */
|
||||
};
|
||||
|
||||
#endif /* !defined _GNU_REISERFS_REISERFS_MOUNT_H */
|
@ -1,699 +0,0 @@
|
||||
/*-
|
||||
* Copyright 2000 Hans Reiser
|
||||
* See README for licensing and copyright details
|
||||
*
|
||||
* Ported to FreeBSD by Jean-Sébastien Pédron <jspedron@club-internet.fr>
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <gnu/reiserfs/reiserfs_fs.h>
|
||||
|
||||
static int reiserfs_find_entry(struct reiserfs_node *dp,
|
||||
const char *name, int namelen,
|
||||
struct path * path_to_entry, struct reiserfs_dir_entry *de);
|
||||
|
||||
MALLOC_DEFINE(M_REISERFSCOOKIES, "ReiserFS cookies",
|
||||
"ReiserFS VOP_READDIR cookies");
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* Lookup functions
|
||||
* -------------------------------------------------------------------*/
|
||||
|
||||
int
|
||||
reiserfs_lookup(struct vop_cachedlookup_args *ap)
|
||||
{
|
||||
int error, retval;
|
||||
struct vnode *vdp = ap->a_dvp;
|
||||
struct vnode **vpp = ap->a_vpp;
|
||||
struct componentname *cnp = ap->a_cnp;
|
||||
|
||||
int flags = cnp->cn_flags;
|
||||
struct thread *td = cnp->cn_thread;
|
||||
|
||||
struct vnode *vp;
|
||||
struct vnode *pdp; /* Saved dp during symlink work */
|
||||
struct reiserfs_node *dp;
|
||||
struct reiserfs_dir_entry de;
|
||||
INITIALIZE_PATH(path_to_entry);
|
||||
|
||||
char c = cnp->cn_nameptr[cnp->cn_namelen];
|
||||
cnp->cn_nameptr[cnp->cn_namelen] = '\0';
|
||||
reiserfs_log(LOG_DEBUG, "looking for `%s', %ld (%s)\n",
|
||||
cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_pnbuf);
|
||||
cnp->cn_nameptr[cnp->cn_namelen] = c;
|
||||
|
||||
vp = NULL;
|
||||
dp = VTOI(vdp);
|
||||
|
||||
if (REISERFS_MAX_NAME(dp->i_reiserfs->s_blocksize) < cnp->cn_namelen)
|
||||
return (ENAMETOOLONG);
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "searching entry\n");
|
||||
de.de_gen_number_bit_string = 0;
|
||||
retval = reiserfs_find_entry(dp, cnp->cn_nameptr, cnp->cn_namelen,
|
||||
&path_to_entry, &de);
|
||||
pathrelse(&path_to_entry);
|
||||
|
||||
if (retval == NAME_FOUND) {
|
||||
reiserfs_log(LOG_DEBUG, "found\n");
|
||||
} else {
|
||||
reiserfs_log(LOG_DEBUG, "not found\n");
|
||||
}
|
||||
|
||||
if (retval == NAME_FOUND) {
|
||||
#if 0
|
||||
/* Hide the .reiserfs_priv directory */
|
||||
if (reiserfs_xattrs(dp->i_reiserfs) &&
|
||||
!old_format_only(dp->i_reiserfs) &&
|
||||
REISERFS_SB(dp->i_reiserfs)->priv_root &&
|
||||
REISERFS_SB(dp->i_reiserfs)->priv_root->d_inode &&
|
||||
de.de_objectid == le32toh(INODE_PKEY(REISERFS_SB(
|
||||
dp->i_reiserfs)->priv_root->d_inode)->k_objectid)) {
|
||||
return (EACCES);
|
||||
}
|
||||
#endif
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "reading vnode\n");
|
||||
pdp = vdp;
|
||||
if (flags & ISDOTDOT) {
|
||||
VOP_UNLOCK(pdp, 0, td);
|
||||
error = reiserfs_iget(vdp->v_mount,
|
||||
(struct cpu_key *)&(de.de_dir_id), &vp, td);
|
||||
vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, td);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
*vpp = vp;
|
||||
} else if (de.de_objectid == dp->i_number &&
|
||||
de.de_dir_id == dp->i_ino) {
|
||||
VREF(vdp); /* We want ourself, ie "." */
|
||||
*vpp = vdp;
|
||||
} else {
|
||||
if ((error = reiserfs_iget(vdp->v_mount,
|
||||
(struct cpu_key *)&(de.de_dir_id), &vp, td)) != 0)
|
||||
return (error);
|
||||
*vpp = vp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Propogate the priv_object flag so we know we're in the
|
||||
* priv tree
|
||||
*/
|
||||
/*if (is_reiserfs_priv_object(dir))
|
||||
REISERFS_I(inode)->i_flags |= i_priv_object;*/
|
||||
} else {
|
||||
if (retval == IO_ERROR) {
|
||||
reiserfs_log(LOG_DEBUG, "IO error\n");
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
/* Insert name into cache if appropriate. */
|
||||
if (cnp->cn_flags & MAKEENTRY)
|
||||
cache_enter(vdp, *vpp, cnp);
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "done\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
extern struct key MIN_KEY;
|
||||
|
||||
int
|
||||
reiserfs_readdir(struct vop_readdir_args /* {
|
||||
struct vnode *a_vp;
|
||||
struct uio *a_uio;
|
||||
struct ucred *a_cred;
|
||||
int *a_eofflag;
|
||||
int *a_ncookies;
|
||||
u_long **a_cookies;
|
||||
} */*ap)
|
||||
{
|
||||
int error = 0;
|
||||
struct dirent dstdp;
|
||||
struct uio *uio = ap->a_uio;
|
||||
|
||||
off_t next_pos;
|
||||
struct buf *bp;
|
||||
struct item_head *ih;
|
||||
struct cpu_key pos_key;
|
||||
const struct key *rkey;
|
||||
struct reiserfs_node *ip;
|
||||
struct reiserfs_dir_entry de;
|
||||
INITIALIZE_PATH(path_to_entry);
|
||||
int entry_num, item_num, search_res;
|
||||
|
||||
/* The NFS part */
|
||||
int ncookies = 0;
|
||||
u_long *cookies = NULL;
|
||||
|
||||
/*
|
||||
* Form key for search the next directory entry using f_pos field of
|
||||
* file structure
|
||||
*/
|
||||
ip = VTOI(ap->a_vp);
|
||||
make_cpu_key(&pos_key,
|
||||
ip, uio->uio_offset ? uio->uio_offset : DOT_OFFSET,
|
||||
TYPE_DIRENTRY, 3);
|
||||
next_pos = cpu_key_k_offset(&pos_key);
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "listing entries for "
|
||||
"(objectid=%d, dirid=%d)\n",
|
||||
pos_key.on_disk_key.k_objectid, pos_key.on_disk_key.k_dir_id);
|
||||
reiserfs_log(LOG_DEBUG, "uio_offset = %jd, uio_resid = %d\n",
|
||||
(intmax_t)uio->uio_offset, uio->uio_resid);
|
||||
|
||||
if (ap->a_ncookies && ap->a_cookies) {
|
||||
cookies = (u_long *)malloc(
|
||||
uio->uio_resid / 16 * sizeof(u_long),
|
||||
M_REISERFSCOOKIES, M_WAITOK);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
//research:
|
||||
/*
|
||||
* Search the directory item, containing entry with
|
||||
* specified key
|
||||
*/
|
||||
reiserfs_log(LOG_DEBUG, "search directory to read\n");
|
||||
search_res = search_by_entry_key(ip->i_reiserfs, &pos_key,
|
||||
&path_to_entry, &de);
|
||||
if (search_res == IO_ERROR) {
|
||||
error = EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
entry_num = de.de_entry_num;
|
||||
item_num = de.de_item_num;
|
||||
bp = de.de_bp;
|
||||
ih = de.de_ih;
|
||||
|
||||
if (search_res == POSITION_FOUND ||
|
||||
entry_num < I_ENTRY_COUNT(ih)) {
|
||||
/*
|
||||
* Go through all entries in the directory item
|
||||
* beginning from the entry, that has been found.
|
||||
*/
|
||||
struct reiserfs_de_head *deh = B_I_DEH(bp, ih) +
|
||||
entry_num;
|
||||
|
||||
if (ap->a_ncookies == NULL) {
|
||||
cookies = NULL;
|
||||
} else {
|
||||
//ncookies =
|
||||
}
|
||||
|
||||
reiserfs_log(LOG_DEBUG,
|
||||
"walking through directory entries\n");
|
||||
for (; entry_num < I_ENTRY_COUNT(ih);
|
||||
entry_num++, deh++) {
|
||||
int d_namlen;
|
||||
char *d_name;
|
||||
off_t d_off;
|
||||
ino_t d_ino;
|
||||
|
||||
if (!de_visible(deh)) {
|
||||
/* It is hidden entry */
|
||||
continue;
|
||||
}
|
||||
|
||||
d_namlen = entry_length(bp, ih, entry_num);
|
||||
d_name = B_I_DEH_ENTRY_FILE_NAME(bp, ih, deh);
|
||||
if (!d_name[d_namlen - 1])
|
||||
d_namlen = strlen(d_name);
|
||||
reiserfs_log(LOG_DEBUG, " - `%s' (len=%d)\n",
|
||||
d_name, d_namlen);
|
||||
|
||||
if (d_namlen > REISERFS_MAX_NAME(
|
||||
ip->i_reiserfs->s_blocksize)) {
|
||||
/* Too big to send back to VFS */
|
||||
continue;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Ignore the .reiserfs_priv entry */
|
||||
if (reiserfs_xattrs(ip->i_reiserfs) &&
|
||||
!old_format_only(ip->i_reiserfs) &&
|
||||
filp->f_dentry == ip->i_reiserfs->s_root &&
|
||||
REISERFS_SB(ip->i_reiserfs)->priv_root &&
|
||||
REISERFS_SB(ip->i_reiserfs)->priv_root->d_inode &&
|
||||
deh_objectid(deh) ==
|
||||
le32toh(INODE_PKEY(REISERFS_SB(
|
||||
ip->i_reiserfs)->priv_root->d_inode)->k_objectid)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
d_off = deh_offset(deh);
|
||||
d_ino = deh_objectid(deh);
|
||||
uio->uio_offset = d_off;
|
||||
|
||||
/* Copy to user land */
|
||||
dstdp.d_fileno = d_ino;
|
||||
dstdp.d_type = DT_UNKNOWN;
|
||||
dstdp.d_namlen = d_namlen;
|
||||
dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
|
||||
bcopy(d_name, dstdp.d_name, dstdp.d_namlen);
|
||||
bzero(dstdp.d_name + dstdp.d_namlen,
|
||||
dstdp.d_reclen -
|
||||
offsetof(struct dirent, d_name) -
|
||||
dstdp.d_namlen);
|
||||
|
||||
if (d_namlen > 0) {
|
||||
if (dstdp.d_reclen <= uio->uio_resid) {
|
||||
reiserfs_log(LOG_DEBUG, " copying to user land\n");
|
||||
error = uiomove(&dstdp,
|
||||
dstdp.d_reclen, uio);
|
||||
if (error)
|
||||
goto end;
|
||||
if (cookies != NULL) {
|
||||
cookies[ncookies] =
|
||||
d_off;
|
||||
ncookies++;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
} else {
|
||||
error = EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
next_pos = deh_offset(deh) + 1;
|
||||
}
|
||||
reiserfs_log(LOG_DEBUG, "...done\n");
|
||||
}
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "checking item num (%d == %d ?)\n",
|
||||
item_num, B_NR_ITEMS(bp) - 1);
|
||||
if (item_num != B_NR_ITEMS(bp) - 1) {
|
||||
/* End of directory has been reached */
|
||||
reiserfs_log(LOG_DEBUG, "end reached\n");
|
||||
if (ap->a_eofflag)
|
||||
*ap->a_eofflag = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/*
|
||||
* Item we went through is last item of node. Using right
|
||||
* delimiting key check is it directory end
|
||||
*/
|
||||
reiserfs_log(LOG_DEBUG, "get right key\n");
|
||||
rkey = get_rkey(&path_to_entry, ip->i_reiserfs);
|
||||
reiserfs_log(LOG_DEBUG, "right key = (objectid=%d, dirid=%d)\n",
|
||||
rkey->k_objectid, rkey->k_dir_id);
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "compare it to MIN_KEY\n");
|
||||
reiserfs_log(LOG_DEBUG, "MIN KEY = (objectid=%d, dirid=%d)\n",
|
||||
MIN_KEY.k_objectid, MIN_KEY.k_dir_id);
|
||||
if (comp_le_keys(rkey, &MIN_KEY) == 0) {
|
||||
/* Set pos_key to key, that is the smallest and greater
|
||||
* that key of the last entry in the item */
|
||||
reiserfs_log(LOG_DEBUG, "continuing on the right\n");
|
||||
set_cpu_key_k_offset(&pos_key, next_pos);
|
||||
continue;
|
||||
}
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "compare it to pos_key\n");
|
||||
reiserfs_log(LOG_DEBUG, "pos key = (objectid=%d, dirid=%d)\n",
|
||||
pos_key.on_disk_key.k_objectid,
|
||||
pos_key.on_disk_key.k_dir_id);
|
||||
if (COMP_SHORT_KEYS(rkey, &pos_key)) {
|
||||
/* End of directory has been reached */
|
||||
reiserfs_log(LOG_DEBUG, "end reached (right)\n");
|
||||
if (ap->a_eofflag)
|
||||
*ap->a_eofflag = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Directory continues in the right neighboring block */
|
||||
reiserfs_log(LOG_DEBUG, "continuing with a new offset\n");
|
||||
set_cpu_key_k_offset(&pos_key,
|
||||
le_key_k_offset(KEY_FORMAT_3_5, rkey));
|
||||
reiserfs_log(LOG_DEBUG,
|
||||
"new pos key = (objectid=%d, dirid=%d)\n",
|
||||
pos_key.on_disk_key.k_objectid,
|
||||
pos_key.on_disk_key.k_dir_id);
|
||||
}
|
||||
|
||||
end:
|
||||
uio->uio_offset = next_pos;
|
||||
pathrelse(&path_to_entry);
|
||||
reiserfs_check_path(&path_to_entry);
|
||||
out:
|
||||
if (error && cookies != NULL) {
|
||||
free(cookies, M_REISERFSCOOKIES);
|
||||
} else if (ap->a_ncookies != NULL && ap->a_cookies != NULL) {
|
||||
*ap->a_ncookies = ncookies;
|
||||
*ap->a_cookies = cookies;
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* Functions from linux/fs/reiserfs/namei.c
|
||||
* -------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/*
|
||||
* Directory item contains array of entry headers. This performs binary
|
||||
* search through that array.
|
||||
*/
|
||||
static int
|
||||
bin_search_in_dir_item(struct reiserfs_dir_entry *de, off_t off)
|
||||
{
|
||||
struct item_head *ih = de->de_ih;
|
||||
struct reiserfs_de_head *deh = de->de_deh;
|
||||
int rbound, lbound, j;
|
||||
|
||||
lbound = 0;
|
||||
rbound = I_ENTRY_COUNT(ih) - 1;
|
||||
|
||||
for (j = (rbound + lbound) / 2; lbound <= rbound;
|
||||
j = (rbound + lbound) / 2) {
|
||||
if (off < deh_offset(deh + j)) {
|
||||
rbound = j - 1;
|
||||
continue;
|
||||
}
|
||||
if (off > deh_offset(deh + j)) {
|
||||
lbound = j + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* This is not name found, but matched third key component */
|
||||
de->de_entry_num = j;
|
||||
return (NAME_FOUND);
|
||||
}
|
||||
|
||||
de->de_entry_num = lbound;
|
||||
return (NAME_NOT_FOUND);
|
||||
}
|
||||
|
||||
/*
|
||||
* Comment? Maybe something like set de to point to what the path
|
||||
* points to?
|
||||
*/
|
||||
static inline void
|
||||
set_de_item_location(struct reiserfs_dir_entry *de, struct path *path)
|
||||
{
|
||||
|
||||
de->de_bp = get_last_bp(path);
|
||||
de->de_ih = get_ih(path);
|
||||
de->de_deh = B_I_DEH(de->de_bp, de->de_ih);
|
||||
de->de_item_num = PATH_LAST_POSITION(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* de_bh, de_ih, de_deh (points to first element of array), de_item_num
|
||||
* is set
|
||||
*/
|
||||
inline void
|
||||
set_de_name_and_namelen(struct reiserfs_dir_entry *de)
|
||||
{
|
||||
struct reiserfs_de_head *deh = de->de_deh + de->de_entry_num;
|
||||
|
||||
if (de->de_entry_num >= ih_entry_count(de->de_ih)) {
|
||||
reiserfs_log(LOG_DEBUG, "BUG\n");
|
||||
return;
|
||||
}
|
||||
|
||||
de->de_entrylen = entry_length(de->de_bp, de->de_ih, de->de_entry_num);
|
||||
de->de_namelen = de->de_entrylen - (de_with_sd(deh) ? SD_SIZE : 0);
|
||||
de->de_name = B_I_PITEM(de->de_bp, de->de_ih) + deh_location(deh);
|
||||
if (de->de_name[de->de_namelen - 1] == 0)
|
||||
de->de_namelen = strlen(de->de_name);
|
||||
}
|
||||
|
||||
/* What entry points to */
|
||||
static inline void
|
||||
set_de_object_key(struct reiserfs_dir_entry *de)
|
||||
{
|
||||
|
||||
if (de->de_entry_num >= ih_entry_count(de->de_ih)) {
|
||||
reiserfs_log(LOG_DEBUG, "BUG\n");
|
||||
return;
|
||||
}
|
||||
de->de_dir_id = deh_dir_id(&(de->de_deh[de->de_entry_num]));
|
||||
de->de_objectid = deh_objectid(&(de->de_deh[de->de_entry_num]));
|
||||
}
|
||||
|
||||
static inline void
|
||||
store_de_entry_key(struct reiserfs_dir_entry *de)
|
||||
{
|
||||
struct reiserfs_de_head *deh = de->de_deh + de->de_entry_num;
|
||||
|
||||
if (de->de_entry_num >= ih_entry_count(de->de_ih)) {
|
||||
reiserfs_log(LOG_DEBUG, "BUG\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store key of the found entry */
|
||||
de->de_entry_key.version = KEY_FORMAT_3_5;
|
||||
de->de_entry_key.on_disk_key.k_dir_id =
|
||||
le32toh(de->de_ih->ih_key.k_dir_id);
|
||||
de->de_entry_key.on_disk_key.k_objectid =
|
||||
le32toh(de->de_ih->ih_key.k_objectid);
|
||||
set_cpu_key_k_offset(&(de->de_entry_key), deh_offset(deh));
|
||||
set_cpu_key_k_type(&(de->de_entry_key), TYPE_DIRENTRY);
|
||||
}
|
||||
|
||||
/*
|
||||
* We assign a key to each directory item, and place multiple entries in
|
||||
* a single directory item. A directory item has a key equal to the key
|
||||
* of the first directory entry in it.
|
||||
*
|
||||
* This function first calls search_by_key, then, if item whose first
|
||||
* entry matches is not found it looks for the entry inside directory
|
||||
* item found by search_by_key. Fills the path to the entry, and to the
|
||||
* entry position in the item
|
||||
*/
|
||||
int
|
||||
search_by_entry_key(struct reiserfs_sb_info *sbi,
|
||||
const struct cpu_key *key, struct path *path,
|
||||
struct reiserfs_dir_entry *de)
|
||||
{
|
||||
int retval;
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "searching in (objectid=%d,dirid=%d)\n",
|
||||
key->on_disk_key.k_objectid, key->on_disk_key.k_dir_id);
|
||||
retval = search_item(sbi, key, path);
|
||||
switch (retval) {
|
||||
case ITEM_NOT_FOUND:
|
||||
if (!PATH_LAST_POSITION(path)) {
|
||||
reiserfs_log(LOG_DEBUG,
|
||||
"search_by_key returned item position == 0");
|
||||
pathrelse(path);
|
||||
return (IO_ERROR);
|
||||
}
|
||||
PATH_LAST_POSITION(path)--;
|
||||
reiserfs_log(LOG_DEBUG, "search_by_key did not found it\n");
|
||||
break;
|
||||
case ITEM_FOUND:
|
||||
reiserfs_log(LOG_DEBUG, "search_by_key found it\n");
|
||||
break;
|
||||
case IO_ERROR:
|
||||
return (retval);
|
||||
default:
|
||||
pathrelse(path);
|
||||
reiserfs_log(LOG_DEBUG, "no path to here");
|
||||
return (IO_ERROR);
|
||||
}
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "set item location\n");
|
||||
set_de_item_location(de, path);
|
||||
|
||||
/*
|
||||
* Binary search in directory item by third component of the
|
||||
* key. Sets de->de_entry_num of de
|
||||
*/
|
||||
reiserfs_log(LOG_DEBUG, "bin_search_in_dir_item\n");
|
||||
retval = bin_search_in_dir_item(de, cpu_key_k_offset(key));
|
||||
path->pos_in_item = de->de_entry_num;
|
||||
if (retval != NAME_NOT_FOUND) {
|
||||
/*
|
||||
* Ugly, but rename needs de_bp, de_deh, de_name, de_namelen,
|
||||
* de_objectid set
|
||||
*/
|
||||
set_de_name_and_namelen(de);
|
||||
set_de_object_key(de);
|
||||
reiserfs_log(LOG_DEBUG, "set (objectid=%d,dirid=%d)\n",
|
||||
de->de_objectid, de->de_dir_id);
|
||||
}
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
get_third_component(struct reiserfs_sb_info *sbi, const char *name, int len)
|
||||
{
|
||||
uint32_t res;
|
||||
|
||||
if (!len || (len == 1 && name[0] == '.'))
|
||||
return (DOT_OFFSET);
|
||||
|
||||
if (len == 2 && name[0] == '.' && name[1] == '.')
|
||||
return (DOT_DOT_OFFSET);
|
||||
|
||||
res = REISERFS_SB(sbi)->s_hash_function(name, len);
|
||||
|
||||
/* Take bits from 7-th to 30-th including both bounds */
|
||||
res = GET_HASH_VALUE(res);
|
||||
if (res == 0)
|
||||
/*
|
||||
* Needed to have no names before "." and ".." those have hash
|
||||
* value == 0 and generation counters 1 and 2 accordingly
|
||||
*/
|
||||
res = 128;
|
||||
|
||||
return (res + MAX_GENERATION_NUMBER);
|
||||
}
|
||||
|
||||
static int
|
||||
reiserfs_match(struct reiserfs_dir_entry *de, const char *name, int namelen)
|
||||
{
|
||||
int retval = NAME_NOT_FOUND;
|
||||
|
||||
if ((namelen == de->de_namelen) &&
|
||||
!memcmp(de->de_name, name, de->de_namelen))
|
||||
retval = (de_visible(de->de_deh + de->de_entry_num) ?
|
||||
NAME_FOUND : NAME_FOUND_INVISIBLE);
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* de's de_bh, de_ih, de_deh, de_item_num, de_entry_num are set already
|
||||
* Used when hash collisions exist
|
||||
*/
|
||||
static int
|
||||
linear_search_in_dir_item(struct cpu_key *key, struct reiserfs_dir_entry *de,
|
||||
const char *name, int namelen)
|
||||
{
|
||||
int i;
|
||||
int retval;
|
||||
struct reiserfs_de_head * deh = de->de_deh;
|
||||
|
||||
i = de->de_entry_num;
|
||||
|
||||
if (i == I_ENTRY_COUNT(de->de_ih) ||
|
||||
GET_HASH_VALUE(deh_offset(deh + i)) !=
|
||||
GET_HASH_VALUE(cpu_key_k_offset(key))) {
|
||||
i--;
|
||||
}
|
||||
|
||||
/*RFALSE( de->de_deh != B_I_DEH (de->de_bh, de->de_ih),
|
||||
"vs-7010: array of entry headers not found");*/
|
||||
|
||||
deh += i;
|
||||
|
||||
for (; i >= 0; i--, deh--) {
|
||||
if (GET_HASH_VALUE(deh_offset(deh)) !=
|
||||
GET_HASH_VALUE(cpu_key_k_offset(key))) {
|
||||
/*
|
||||
* Hash value does not match, no need to check
|
||||
* whole name
|
||||
*/
|
||||
reiserfs_log(LOG_DEBUG, "name `%s' not found\n", name);
|
||||
return (NAME_NOT_FOUND);
|
||||
}
|
||||
|
||||
/* Mark that this generation number is used */
|
||||
if (de->de_gen_number_bit_string)
|
||||
set_bit(GET_GENERATION_NUMBER(deh_offset(deh)),
|
||||
(unsigned long *)de->de_gen_number_bit_string);
|
||||
|
||||
/* Calculate pointer to name and namelen */
|
||||
de->de_entry_num = i;
|
||||
set_de_name_and_namelen(de);
|
||||
|
||||
if ((retval = reiserfs_match(de, name, namelen)) !=
|
||||
NAME_NOT_FOUND) {
|
||||
/*
|
||||
* de's de_name, de_namelen, de_recordlen are set.
|
||||
* Fill the rest:
|
||||
*/
|
||||
/* key of pointed object */
|
||||
set_de_object_key(de);
|
||||
store_de_entry_key(de);
|
||||
|
||||
/* retval can be NAME_FOUND or NAME_FOUND_INVISIBLE */
|
||||
reiserfs_log(LOG_DEBUG,
|
||||
"reiserfs_match answered `%d'\n",
|
||||
retval);
|
||||
return (retval);
|
||||
}
|
||||
}
|
||||
|
||||
if (GET_GENERATION_NUMBER(le_ih_k_offset(de->de_ih)) == 0)
|
||||
/*
|
||||
* We have reached left most entry in the node. In common
|
||||
* we have to go to the left neighbor, but if generation
|
||||
* counter is 0 already, we know for sure, that there is
|
||||
* no name with the same hash value
|
||||
*/
|
||||
/* FIXME: this work correctly only because hash value can
|
||||
* not be 0. Btw, in case of Yura's hash it is probably
|
||||
* possible, so, this is a bug
|
||||
*/
|
||||
return (NAME_NOT_FOUND);
|
||||
|
||||
/*RFALSE(de->de_item_num,
|
||||
"vs-7015: two diritems of the same directory in one node?");*/
|
||||
|
||||
return (GOTO_PREVIOUS_ITEM);
|
||||
}
|
||||
|
||||
/*
|
||||
* May return NAME_FOUND, NAME_FOUND_INVISIBLE, NAME_NOT_FOUND
|
||||
* FIXME: should add something like IOERROR
|
||||
*/
|
||||
static int
|
||||
reiserfs_find_entry(struct reiserfs_node *dp, const char *name, int namelen,
|
||||
struct path * path_to_entry, struct reiserfs_dir_entry *de)
|
||||
{
|
||||
struct cpu_key key_to_search;
|
||||
int retval;
|
||||
|
||||
if (namelen > REISERFS_MAX_NAME(dp->i_reiserfs->s_blocksize))
|
||||
return NAME_NOT_FOUND;
|
||||
|
||||
/* We will search for this key in the tree */
|
||||
make_cpu_key(&key_to_search, dp,
|
||||
get_third_component(dp->i_reiserfs, name, namelen),
|
||||
TYPE_DIRENTRY, 3);
|
||||
|
||||
while (1) {
|
||||
reiserfs_log(LOG_DEBUG, "search by entry key\n");
|
||||
retval = search_by_entry_key(dp->i_reiserfs, &key_to_search,
|
||||
path_to_entry, de);
|
||||
if (retval == IO_ERROR) {
|
||||
reiserfs_log(LOG_DEBUG, "IO error in %s\n",
|
||||
__FUNCTION__);
|
||||
return IO_ERROR;
|
||||
}
|
||||
|
||||
/* Compare names for all entries having given hash value */
|
||||
reiserfs_log(LOG_DEBUG, "linear search for `%s'\n", name);
|
||||
retval = linear_search_in_dir_item(&key_to_search, de,
|
||||
name, namelen);
|
||||
if (retval != GOTO_PREVIOUS_ITEM) {
|
||||
/*
|
||||
* There is no need to scan directory anymore.
|
||||
* Given entry found or does not exist
|
||||
*/
|
||||
reiserfs_log(LOG_DEBUG, "linear search returned "
|
||||
"(objectid=%d,dirid=%d)\n",
|
||||
de->de_objectid, de->de_dir_id);
|
||||
path_to_entry->pos_in_item = de->de_entry_num;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* There is left neighboring item of this directory and
|
||||
* given entry can be there
|
||||
*/
|
||||
set_cpu_key_k_offset(&key_to_search,
|
||||
le_ih_k_offset(de->de_ih) - 1);
|
||||
pathrelse(path_to_entry);
|
||||
} /* while (1) */
|
||||
}
|
@ -1,307 +0,0 @@
|
||||
/*-
|
||||
* Copyright 2000 Hans Reiser
|
||||
* See README for licensing and copyright details
|
||||
*
|
||||
* Ported to FreeBSD by Jean-Sébastien Pédron <jspedron@club-internet.fr>
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <gnu/reiserfs/reiserfs_fs.h>
|
||||
|
||||
#if 0
|
||||
static char error_buf[1024];
|
||||
static char fmt_buf[1024];
|
||||
static char off_buf[80];
|
||||
|
||||
static char *
|
||||
reiserfs_cpu_offset(struct cpu_key *key)
|
||||
{
|
||||
|
||||
if (cpu_key_k_type(key) == TYPE_DIRENTRY)
|
||||
sprintf(off_buf, "%Lu(%Lu)",
|
||||
(unsigned long long)GET_HASH_VALUE(cpu_key_k_offset(key)),
|
||||
(unsigned long long)GET_GENERATION_NUMBER(
|
||||
cpu_key_k_offset(key)));
|
||||
else
|
||||
sprintf(off_buf, "0x%Lx",
|
||||
(unsigned long long)cpu_key_k_offset(key));
|
||||
|
||||
return (off_buf);
|
||||
}
|
||||
|
||||
static char *
|
||||
le_offset(struct key *key)
|
||||
{
|
||||
int version;
|
||||
|
||||
version = le_key_version(key);
|
||||
if (le_key_k_type(version, key) == TYPE_DIRENTRY)
|
||||
sprintf(off_buf, "%Lu(%Lu)",
|
||||
(unsigned long long)GET_HASH_VALUE(
|
||||
le_key_k_offset(version, key)),
|
||||
(unsigned long long)GET_GENERATION_NUMBER(
|
||||
le_key_k_offset(version, key)));
|
||||
else
|
||||
sprintf(off_buf, "0x%Lx",
|
||||
(unsigned long long)le_key_k_offset(version, key));
|
||||
|
||||
return (off_buf);
|
||||
}
|
||||
|
||||
static char *
|
||||
cpu_type(struct cpu_key *key)
|
||||
{
|
||||
|
||||
if (cpu_key_k_type(key) == TYPE_STAT_DATA)
|
||||
return ("SD");
|
||||
if (cpu_key_k_type(key) == TYPE_DIRENTRY)
|
||||
return ("DIR");
|
||||
if (cpu_key_k_type(key) == TYPE_DIRECT)
|
||||
return ("DIRECT");
|
||||
if (cpu_key_k_type(key) == TYPE_INDIRECT)
|
||||
return ("IND");
|
||||
|
||||
return ("UNKNOWN");
|
||||
}
|
||||
|
||||
static char *
|
||||
le_type(struct key *key)
|
||||
{
|
||||
int version;
|
||||
|
||||
version = le_key_version(key);
|
||||
|
||||
if (le_key_k_type(version, key) == TYPE_STAT_DATA)
|
||||
return ("SD");
|
||||
if (le_key_k_type(version, key) == TYPE_DIRENTRY)
|
||||
return ("DIR");
|
||||
if (le_key_k_type(version, key) == TYPE_DIRECT)
|
||||
return ("DIRECT");
|
||||
if (le_key_k_type(version, key) == TYPE_INDIRECT)
|
||||
return ("IND");
|
||||
|
||||
return ("UNKNOWN");
|
||||
}
|
||||
|
||||
/* %k */
|
||||
static void
|
||||
sprintf_le_key(char *buf, struct key *key)
|
||||
{
|
||||
|
||||
if (key)
|
||||
sprintf(buf, "[%d %d %s %s]", le32toh(key->k_dir_id),
|
||||
le32toh(key->k_objectid), le_offset(key), le_type(key));
|
||||
else
|
||||
sprintf(buf, "[NULL]");
|
||||
}
|
||||
|
||||
/* %K */
|
||||
static void
|
||||
sprintf_cpu_key(char *buf, struct cpu_key *key)
|
||||
{
|
||||
|
||||
if (key)
|
||||
sprintf(buf, "[%d %d %s %s]", key->on_disk_key.k_dir_id,
|
||||
key->on_disk_key.k_objectid, reiserfs_cpu_offset (key),
|
||||
cpu_type (key));
|
||||
else
|
||||
sprintf(buf, "[NULL]");
|
||||
}
|
||||
|
||||
static void sprintf_de_head(char *buf, struct reiserfs_de_head *deh)
|
||||
{
|
||||
|
||||
if (deh)
|
||||
sprintf(buf,
|
||||
"[offset=%d dir_id=%d objectid=%d location=%d state=%04x]",
|
||||
deh_offset(deh), deh_dir_id(deh),
|
||||
deh_objectid(deh), deh_location(deh), deh_state(deh));
|
||||
else
|
||||
sprintf(buf, "[NULL]");
|
||||
}
|
||||
|
||||
static void
|
||||
sprintf_item_head(char *buf, struct item_head *ih)
|
||||
{
|
||||
|
||||
if (ih) {
|
||||
strcpy(buf, (ih_version(ih) == KEY_FORMAT_3_6) ?
|
||||
"*3.6* " : "*3.5*");
|
||||
sprintf_le_key(buf + strlen(buf), &(ih->ih_key));
|
||||
sprintf(buf + strlen(buf), ", item_len %d, item_location %d, "
|
||||
"free_space(entry_count) %d",
|
||||
ih_item_len(ih), ih_location(ih), ih_free_space(ih));
|
||||
} else
|
||||
sprintf(buf, "[NULL]");
|
||||
}
|
||||
|
||||
static void
|
||||
sprintf_direntry(char *buf, struct reiserfs_dir_entry *de)
|
||||
{
|
||||
char name[20];
|
||||
|
||||
memcpy(name, de->de_name, de->de_namelen > 19 ? 19 : de->de_namelen);
|
||||
name [de->de_namelen > 19 ? 19 : de->de_namelen] = 0;
|
||||
sprintf(buf, "\"%s\" ==> [%d %d]",
|
||||
name, de->de_dir_id, de->de_objectid);
|
||||
}
|
||||
|
||||
static void
|
||||
sprintf_block_head(char *buf, struct buf *bp)
|
||||
{
|
||||
|
||||
sprintf(buf, "level=%d, nr_items=%d, free_space=%d rdkey ",
|
||||
B_LEVEL(bp), B_NR_ITEMS(bp), B_FREE_SPACE(bp));
|
||||
}
|
||||
|
||||
static void
|
||||
sprintf_disk_child(char *buf, struct disk_child *dc)
|
||||
{
|
||||
|
||||
sprintf (buf, "[dc_number=%d, dc_size=%u]",
|
||||
dc_block_number(dc), dc_size(dc));
|
||||
}
|
||||
|
||||
static char *
|
||||
is_there_reiserfs_struct (char *fmt, int *what, int *skip)
|
||||
{
|
||||
char *k;
|
||||
|
||||
k = fmt;
|
||||
*skip = 0;
|
||||
|
||||
while ((k = strchr(k, '%')) != NULL) {
|
||||
if (k[1] == 'k' || k[1] == 'K' || k[1] == 'h' || k[1] == 't' ||
|
||||
k[1] == 'z' || k[1] == 'b' || k[1] == 'y' || k[1] == 'a' ) {
|
||||
*what = k[1];
|
||||
break;
|
||||
}
|
||||
(*skip)++;
|
||||
k++;
|
||||
}
|
||||
|
||||
return (k);
|
||||
}
|
||||
|
||||
static void
|
||||
prepare_error_buf(const char *fmt, va_list args)
|
||||
{
|
||||
char *fmt1, *k, *p;
|
||||
int i, j, what, skip;
|
||||
|
||||
fmt1 = fmt_buf;
|
||||
p = error_buf;
|
||||
strcpy (fmt1, fmt);
|
||||
|
||||
while ((k = is_there_reiserfs_struct(fmt1, &what, &skip)) != NULL) {
|
||||
*k = 0;
|
||||
|
||||
p += vsprintf (p, fmt1, args);
|
||||
|
||||
for (i = 0; i < skip; i ++)
|
||||
j = va_arg(args, int);
|
||||
|
||||
switch (what) {
|
||||
case 'k':
|
||||
sprintf_le_key(p, va_arg(args, struct key *));
|
||||
break;
|
||||
case 'K':
|
||||
sprintf_cpu_key(p, va_arg(args, struct cpu_key *));
|
||||
break;
|
||||
case 'h':
|
||||
sprintf_item_head(p, va_arg(args, struct item_head *));
|
||||
break;
|
||||
case 't':
|
||||
sprintf_direntry(p,
|
||||
va_arg(args, struct reiserfs_dir_entry *));
|
||||
break;
|
||||
case 'y':
|
||||
sprintf_disk_child(p,
|
||||
va_arg(args, struct disk_child *));
|
||||
break;
|
||||
case 'z':
|
||||
sprintf_block_head(p,
|
||||
va_arg(args, struct buffer_head *));
|
||||
break;
|
||||
case 'a':
|
||||
sprintf_de_head(p,
|
||||
va_arg(args, struct reiserfs_de_head *));
|
||||
break;
|
||||
}
|
||||
|
||||
p += strlen(p);
|
||||
fmt1 = k + 2;
|
||||
}
|
||||
|
||||
vsprintf(p, fmt1, args);
|
||||
}
|
||||
|
||||
/*
|
||||
* In addition to usual conversion specifiers this accepts reiserfs
|
||||
* specific conversion specifiers:
|
||||
* %k to print little endian key,
|
||||
* %K to print cpu key,
|
||||
* %h to print item_head,
|
||||
* %t to print directory entry,
|
||||
* %z to print block head (arg must be struct buf *)
|
||||
*/
|
||||
|
||||
#define do_reiserfs_warning(fmt) \
|
||||
{ \
|
||||
va_list args; \
|
||||
va_start(args, fmt); \
|
||||
prepare_error_buf(fmt, args); \
|
||||
va_end(args); \
|
||||
}
|
||||
|
||||
void
|
||||
__reiserfs_log(int level, const char * fmt, ...)
|
||||
{
|
||||
|
||||
do_reiserfs_warning(fmt);
|
||||
log(level, "ReiserFS/%s: %s\n", __FUNCTION__, error_buf);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
char *
|
||||
reiserfs_hashname(int code)
|
||||
{
|
||||
|
||||
if (code == YURA_HASH)
|
||||
return ("rupasov");
|
||||
if (code == TEA_HASH)
|
||||
return ("tea");
|
||||
if (code == R5_HASH)
|
||||
return ("r5");
|
||||
|
||||
return ("unknown");
|
||||
}
|
||||
|
||||
void
|
||||
reiserfs_dump_buffer(caddr_t buf, off_t len)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
log(LOG_DEBUG, "reiserfs: dumping a buffer of %jd bytes\n",
|
||||
(intmax_t)len);
|
||||
for (i = 0; i < len; i += 16) {
|
||||
log(LOG_DEBUG, "%08x: ", i);
|
||||
for (j = 0; j < 16; j += 2) {
|
||||
if (i + j >= len)
|
||||
log(LOG_DEBUG, " ");
|
||||
else
|
||||
log(LOG_DEBUG, "%02x%02x ",
|
||||
buf[i + j] & 0xff,
|
||||
buf[i + j + 1] & 0xff);
|
||||
}
|
||||
for (j = 0; j < 16; ++j) {
|
||||
if (i + j >= len)
|
||||
break;
|
||||
log(LOG_DEBUG, "%c",
|
||||
isprint(buf[i + j]) ? buf[i + j] : '.');
|
||||
}
|
||||
log(LOG_DEBUG, "\n");
|
||||
}
|
||||
}
|
@ -1,760 +0,0 @@
|
||||
/*-
|
||||
* Copyright 2000 Hans Reiser
|
||||
* See README for licensing and copyright details
|
||||
*
|
||||
* Ported to FreeBSD by Jean-Sébastien Pédron <jspedron@club-internet.fr>
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <gnu/reiserfs/reiserfs_fs.h>
|
||||
|
||||
/* Minimal possible key. It is never in the tree. */
|
||||
const struct key MIN_KEY = {
|
||||
0,
|
||||
0,
|
||||
{ {0, 0}, }
|
||||
};
|
||||
|
||||
/* Maximal possible key. It is never in the tree. */
|
||||
const struct key MAX_KEY = {
|
||||
0xffffffff,
|
||||
0xffffffff,
|
||||
{ {0xffffffff, 0xffffffff }, }
|
||||
};
|
||||
|
||||
/* Does the buffer contain a disk block which is in the tree. */
|
||||
inline int
|
||||
B_IS_IN_TREE(const struct buf *p_s_bp)
|
||||
{
|
||||
|
||||
return (B_LEVEL(p_s_bp) != FREE_LEVEL);
|
||||
}
|
||||
|
||||
/* To gets item head in le form */
|
||||
inline void
|
||||
copy_item_head(struct item_head *p_v_to, const struct item_head *p_v_from)
|
||||
{
|
||||
|
||||
memcpy(p_v_to, p_v_from, IH_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* k1 is pointer to on-disk structure which is stored in little-endian
|
||||
* form. k2 is pointer to cpu variable. For key of items of the same
|
||||
* object this returns 0.
|
||||
* Returns: -1 if key1 < key2, 0 if key1 == key2 or 1 if key1 > key2
|
||||
*/
|
||||
/*inline*/ int
|
||||
comp_short_keys(const struct key *le_key, const struct cpu_key *cpu_key)
|
||||
{
|
||||
const uint32_t *p_s_le_u32, *p_s_cpu_u32;
|
||||
int n_key_length = REISERFS_SHORT_KEY_LEN;
|
||||
|
||||
p_s_le_u32 = (const uint32_t *)le_key;
|
||||
p_s_cpu_u32 = (const uint32_t *)&cpu_key->on_disk_key;
|
||||
for(; n_key_length--; ++p_s_le_u32, ++p_s_cpu_u32) {
|
||||
if (le32toh(*p_s_le_u32) < *p_s_cpu_u32)
|
||||
return (-1);
|
||||
if (le32toh(*p_s_le_u32) > *p_s_cpu_u32)
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* k1 is pointer to on-disk structure which is stored in little-endian
|
||||
* form. k2 is pointer to cpu variable. Compare keys using all 4 key
|
||||
* fields.
|
||||
* Returns: -1 if key1 < key2, 0 if key1 = key2 or 1 if key1 > key2
|
||||
*/
|
||||
/*inline*/ int
|
||||
comp_keys(const struct key *le_key, const struct cpu_key *cpu_key)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = comp_short_keys(le_key, cpu_key);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (le_key_k_offset(le_key_version(le_key), le_key) <
|
||||
cpu_key_k_offset(cpu_key))
|
||||
return (-1);
|
||||
if (le_key_k_offset(le_key_version(le_key), le_key) >
|
||||
cpu_key_k_offset(cpu_key))
|
||||
return (1);
|
||||
|
||||
if (cpu_key->key_length == 3)
|
||||
return (0);
|
||||
|
||||
/* This part is needed only when tail conversion is in progress */
|
||||
if (le_key_k_type(le_key_version(le_key), le_key) <
|
||||
cpu_key_k_type(cpu_key))
|
||||
return (-1);
|
||||
|
||||
if (le_key_k_type(le_key_version(le_key), le_key) >
|
||||
cpu_key_k_type(cpu_key))
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Release all buffers in the path. */
|
||||
void
|
||||
pathrelse(struct path *p_s_search_path)
|
||||
{
|
||||
struct buf *bp;
|
||||
int n_path_offset = p_s_search_path->path_length;
|
||||
|
||||
while (n_path_offset > ILLEGAL_PATH_ELEMENT_OFFSET) {
|
||||
bp = PATH_OFFSET_PBUFFER(p_s_search_path, n_path_offset--);
|
||||
free(bp->b_data, M_REISERFSPATH);
|
||||
free(bp, M_REISERFSPATH);
|
||||
}
|
||||
|
||||
p_s_search_path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET;
|
||||
}
|
||||
|
||||
/*
|
||||
* This does not say which one is bigger, it only returns 1 if keys
|
||||
* are not equal, 0 otherwise
|
||||
*/
|
||||
inline int
|
||||
comp_le_keys(const struct key *k1, const struct key *k2)
|
||||
{
|
||||
|
||||
return (memcmp(k1, k2, sizeof(struct key)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Binary search toolkit function. Search for an item in the array by
|
||||
* the item key.
|
||||
* Returns: 1 if found, 0 if not found;
|
||||
* *p_n_pos = number of the searched element if found, else the
|
||||
* number of the first element that is larger than p_v_key.
|
||||
*/
|
||||
/*
|
||||
* For those not familiar with binary search: n_lbound is the leftmost
|
||||
* item that it could be, n_rbound the rightmost item that it could be.
|
||||
* We examine the item halfway between n_lbound and n_rbound, and that
|
||||
* tells us either that we can increase n_lbound, or decrease n_rbound,
|
||||
* or that we have found it, or if n_lbound <= n_rbound that there are
|
||||
* no possible items, and we have not found it. With each examination we
|
||||
* cut the number of possible items it could be by one more than half
|
||||
* rounded down, or we find it.
|
||||
*/
|
||||
inline int
|
||||
bin_search(const void *p_v_key, /* Key to search for. */
|
||||
const void *p_v_base, /* First item in the array. */
|
||||
int p_n_num, /* Number of items in the array. */
|
||||
int p_n_width, /* Item size in the array. searched. Lest the
|
||||
reader be confused, note that this is crafted
|
||||
as a general function, and when it is applied
|
||||
specifically to the array of item headers in
|
||||
a node, p_n_width is actually the item header
|
||||
size not the item size. */
|
||||
int *p_n_pos) /* Number of the searched for element. */
|
||||
{
|
||||
int n_rbound, n_lbound, n_j;
|
||||
|
||||
for (n_j = ((n_rbound = p_n_num - 1) + (n_lbound = 0)) / 2;
|
||||
n_lbound <= n_rbound; n_j = (n_rbound + n_lbound) / 2) {
|
||||
switch (COMP_KEYS((const struct key *)
|
||||
((const char *)p_v_base + n_j * p_n_width),
|
||||
(const struct cpu_key *)p_v_key)) {
|
||||
case -1:
|
||||
n_lbound = n_j + 1;
|
||||
continue;
|
||||
case 1:
|
||||
n_rbound = n_j - 1;
|
||||
continue;
|
||||
case 0:
|
||||
*p_n_pos = n_j;
|
||||
return (ITEM_FOUND); /* Key found in the array. */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* bin_search did not find given key, it returns position of key,
|
||||
* that is minimal and greater than the given one.
|
||||
*/
|
||||
*p_n_pos = n_lbound;
|
||||
return (ITEM_NOT_FOUND);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get delimiting key of the buffer by looking for it in the buffers in
|
||||
* the path, starting from the bottom of the path, and going upwards. We
|
||||
* must check the path's validity at each step. If the key is not in the
|
||||
* path, there is no delimiting key in the tree (buffer is first or last
|
||||
* buffer in tree), and in this case we return a special key, either
|
||||
* MIN_KEY or MAX_KEY.
|
||||
*/
|
||||
inline const struct key *
|
||||
get_lkey(const struct path *p_s_chk_path,
|
||||
const struct reiserfs_sb_info *p_s_sbi)
|
||||
{
|
||||
struct buf *p_s_parent;
|
||||
int n_position, n_path_offset = p_s_chk_path->path_length;
|
||||
|
||||
/* While not higher in path than first element. */
|
||||
while (n_path_offset-- > FIRST_PATH_ELEMENT_OFFSET) {
|
||||
/* Parent at the path is not in the tree now. */
|
||||
if (!B_IS_IN_TREE(p_s_parent =
|
||||
PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset)))
|
||||
return (&MAX_KEY);
|
||||
|
||||
/* Check whether position in the parent is correct. */
|
||||
if ((n_position = PATH_OFFSET_POSITION(p_s_chk_path,
|
||||
n_path_offset)) > B_NR_ITEMS(p_s_parent))
|
||||
return (&MAX_KEY);
|
||||
|
||||
/*
|
||||
* Check whether parent at the path really points to
|
||||
* the child.
|
||||
*/
|
||||
if (B_N_CHILD_NUM(p_s_parent, n_position) !=
|
||||
(PATH_OFFSET_PBUFFER(p_s_chk_path,
|
||||
n_path_offset + 1)->b_blkno
|
||||
/ btodb(p_s_sbi->s_blocksize)))
|
||||
return (&MAX_KEY);
|
||||
|
||||
/*
|
||||
* Return delimiting key if position in the parent is not
|
||||
* equal to zero.
|
||||
*/
|
||||
if (n_position)
|
||||
return (B_N_PDELIM_KEY(p_s_parent, n_position - 1));
|
||||
}
|
||||
|
||||
/* Return MIN_KEY if we are in the root of the buffer tree. */
|
||||
if ((PATH_OFFSET_PBUFFER(p_s_chk_path,
|
||||
FIRST_PATH_ELEMENT_OFFSET)->b_blkno
|
||||
/ btodb(p_s_sbi->s_blocksize)) == SB_ROOT_BLOCK(p_s_sbi))
|
||||
return (&MIN_KEY);
|
||||
|
||||
return (&MAX_KEY);
|
||||
}
|
||||
|
||||
/* Get delimiting key of the buffer at the path and its right neighbor. */
|
||||
inline const struct key *
|
||||
get_rkey(const struct path *p_s_chk_path,
|
||||
const struct reiserfs_sb_info *p_s_sbi)
|
||||
{
|
||||
struct buf *p_s_parent;
|
||||
int n_position, n_path_offset = p_s_chk_path->path_length;
|
||||
|
||||
while (n_path_offset-- > FIRST_PATH_ELEMENT_OFFSET) {
|
||||
/* Parent at the path is not in the tree now. */
|
||||
if (!B_IS_IN_TREE(p_s_parent =
|
||||
PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset)))
|
||||
return (&MIN_KEY);
|
||||
|
||||
/* Check whether position in the parent is correct. */
|
||||
if ((n_position = PATH_OFFSET_POSITION(p_s_chk_path,
|
||||
n_path_offset)) >
|
||||
B_NR_ITEMS(p_s_parent))
|
||||
return (&MIN_KEY);
|
||||
|
||||
/*
|
||||
* Check whether parent at the path really points to the
|
||||
* child.
|
||||
*/
|
||||
if (B_N_CHILD_NUM(p_s_parent, n_position) !=
|
||||
(PATH_OFFSET_PBUFFER(p_s_chk_path,
|
||||
n_path_offset + 1)->b_blkno
|
||||
/ btodb(p_s_sbi->s_blocksize)))
|
||||
return (&MIN_KEY);
|
||||
|
||||
/*
|
||||
* Return delimiting key if position in the parent is not
|
||||
* the last one.
|
||||
*/
|
||||
if (n_position != B_NR_ITEMS(p_s_parent))
|
||||
return (B_N_PDELIM_KEY(p_s_parent, n_position));
|
||||
}
|
||||
|
||||
/* Return MAX_KEY if we are in the root of the buffer tree. */
|
||||
if ((PATH_OFFSET_PBUFFER(p_s_chk_path,
|
||||
FIRST_PATH_ELEMENT_OFFSET)->b_blkno
|
||||
/ btodb(p_s_sbi->s_blocksize)) == SB_ROOT_BLOCK(p_s_sbi))
|
||||
return (&MAX_KEY);
|
||||
|
||||
return (&MIN_KEY);
|
||||
}
|
||||
|
||||
int
|
||||
reiserfs_check_path(struct path *p)
|
||||
{
|
||||
|
||||
if (p->path_length != ILLEGAL_PATH_ELEMENT_OFFSET)
|
||||
reiserfs_log(LOG_WARNING, "path not properly relsed\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a key is contained in the tree rooted from a buffer at
|
||||
* a path. This works by looking at the left and right delimiting keys
|
||||
* for the buffer in the last path_element in the path. These delimiting
|
||||
* keys are stored at least one level above that buffer in the tree.
|
||||
* If the buffer is the first or last node in the tree order then one
|
||||
* of the delimiting keys may be absent, and in this case get_lkey and
|
||||
* get_rkey return a special key which is MIN_KEY or MAX_KEY.
|
||||
*/
|
||||
static inline int
|
||||
key_in_buffer(
|
||||
struct path *p_s_chk_path, /* Path which should be checked. */
|
||||
const struct cpu_key *p_s_key, /* Key which should be checked. */
|
||||
struct reiserfs_sb_info *p_s_sbi) /* Super block pointer. */
|
||||
{
|
||||
|
||||
if (COMP_KEYS(get_lkey(p_s_chk_path, p_s_sbi), p_s_key) == 1)
|
||||
/* left delimiting key is bigger, that the key we look for */
|
||||
return (0);
|
||||
|
||||
if (COMP_KEYS(get_rkey(p_s_chk_path, p_s_sbi), p_s_key) != 1)
|
||||
/* p_s_key must be less than right delimitiing key */
|
||||
return (0);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* XXX Il ne semble pas y avoir de compteur de référence dans struct buf */
|
||||
inline void
|
||||
decrement_bcount(struct buf *p_s_bp)
|
||||
{
|
||||
|
||||
if (p_s_bp) {
|
||||
if (atomic_read(&(p_s_bp->b_count))) {
|
||||
put_bh(p_s_bp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Decrement b_count field of the all buffers in the path. */
|
||||
void
|
||||
decrement_counters_in_path(struct path *p_s_search_path)
|
||||
{
|
||||
|
||||
pathrelse(p_s_search_path);
|
||||
#if 0
|
||||
int n_path_offset = p_s_search_path->path_length;
|
||||
|
||||
while (n_path_offset > ILLEGAL_PATH_ELEMENT_OFFSET) {
|
||||
struct buf *bp;
|
||||
|
||||
bp = PATH_OFFSET_PBUFFER(p_s_search_path, n_path_offset--);
|
||||
decrement_bcount(bp);
|
||||
}
|
||||
|
||||
p_s_search_path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
is_leaf(char *buf, int blocksize, struct buf *bp)
|
||||
{
|
||||
struct item_head *ih;
|
||||
struct block_head *blkh;
|
||||
int used_space, prev_location, i, nr;
|
||||
|
||||
blkh = (struct block_head *)buf;
|
||||
if (blkh_level(blkh) != DISK_LEAF_NODE_LEVEL) {
|
||||
reiserfs_log(LOG_WARNING, "this should be caught earlier");
|
||||
return (0);
|
||||
}
|
||||
|
||||
nr = blkh_nr_item(blkh);
|
||||
if (nr < 1 || nr >
|
||||
((blocksize - BLKH_SIZE) / (IH_SIZE + MIN_ITEM_LEN))) {
|
||||
/* Item number is too big or too small */
|
||||
reiserfs_log(LOG_WARNING, "nr_item seems wrong\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
ih = (struct item_head *)(buf + BLKH_SIZE) + nr - 1;
|
||||
used_space = BLKH_SIZE + IH_SIZE * nr + (blocksize - ih_location(ih));
|
||||
if (used_space != blocksize - blkh_free_space(blkh)) {
|
||||
/*
|
||||
* Free space does not match to calculated amount of
|
||||
* use space
|
||||
*/
|
||||
reiserfs_log(LOG_WARNING, "free space seems wrong\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* FIXME: it is_leaf will hit performance too much - we may have
|
||||
* return 1 here */
|
||||
|
||||
/* Check tables of item heads */
|
||||
ih = (struct item_head *)(buf + BLKH_SIZE);
|
||||
prev_location = blocksize;
|
||||
for (i = 0; i < nr; i++, ih++) {
|
||||
if (le_ih_k_type(ih) == TYPE_ANY) {
|
||||
reiserfs_log(LOG_WARNING,
|
||||
"wrong item type for item\n");
|
||||
return (0);
|
||||
}
|
||||
if (ih_location(ih) >= blocksize ||
|
||||
ih_location(ih) < IH_SIZE * nr) {
|
||||
reiserfs_log(LOG_WARNING,
|
||||
"item location seems wrong\n");
|
||||
return (0);
|
||||
}
|
||||
if (ih_item_len(ih) < 1 ||
|
||||
ih_item_len(ih) > MAX_ITEM_LEN(blocksize)) {
|
||||
reiserfs_log(LOG_WARNING, "item length seems wrong\n");
|
||||
return (0);
|
||||
}
|
||||
if (prev_location - ih_location(ih) != ih_item_len(ih)) {
|
||||
reiserfs_log(LOG_WARNING,
|
||||
"item location seems wrong (second one)\n");
|
||||
return (0);
|
||||
}
|
||||
prev_location = ih_location(ih);
|
||||
}
|
||||
|
||||
/* One may imagine much more checks */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Returns 1 if buf looks like an internal node, 0 otherwise */
|
||||
static int
|
||||
is_internal(char *buf, int blocksize, struct buf *bp)
|
||||
{
|
||||
int nr, used_space;
|
||||
struct block_head *blkh;
|
||||
|
||||
blkh = (struct block_head *)buf;
|
||||
nr = blkh_level(blkh);
|
||||
if (nr <= DISK_LEAF_NODE_LEVEL || nr > MAX_HEIGHT) {
|
||||
/* This level is not possible for internal nodes */
|
||||
reiserfs_log(LOG_WARNING, "this should be caught earlier\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
nr = blkh_nr_item(blkh);
|
||||
if (nr > (blocksize - BLKH_SIZE - DC_SIZE) / (KEY_SIZE + DC_SIZE)) {
|
||||
/*
|
||||
* For internal which is not root we might check min
|
||||
* number of keys
|
||||
*/
|
||||
reiserfs_log(LOG_WARNING, "number of key seems wrong\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
used_space = BLKH_SIZE + KEY_SIZE * nr + DC_SIZE * (nr + 1);
|
||||
if (used_space != blocksize - blkh_free_space(blkh)) {
|
||||
reiserfs_log(LOG_WARNING,
|
||||
"is_internal: free space seems wrong\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* One may imagine much more checks */
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure that bh contains formatted node of reiserfs tree of
|
||||
* 'level'-th level
|
||||
*/
|
||||
static int
|
||||
is_tree_node(struct buf *bp, int level)
|
||||
{
|
||||
if (B_LEVEL(bp) != level) {
|
||||
reiserfs_log(LOG_WARNING,
|
||||
"node level (%d) doesn't match to the "
|
||||
"expected one (%d)\n", B_LEVEL (bp), level);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (level == DISK_LEAF_NODE_LEVEL)
|
||||
return (is_leaf(bp->b_data, bp->b_bcount, bp));
|
||||
|
||||
return (is_internal(bp->b_data, bp->b_bcount, bp));
|
||||
}
|
||||
|
||||
int
|
||||
search_by_key(struct reiserfs_sb_info *p_s_sbi,
|
||||
const struct cpu_key * p_s_key, /* Key to search. */
|
||||
struct path * p_s_search_path, /* This structure was allocated and
|
||||
initialized by the calling function.
|
||||
It is filled up by this function. */
|
||||
int n_stop_level) /* How far down the tree to search. To
|
||||
stop at leaf level - set to
|
||||
DISK_LEAF_NODE_LEVEL */
|
||||
{
|
||||
int error;
|
||||
int n_node_level, n_retval;
|
||||
int n_block_number, expected_level, fs_gen;
|
||||
struct path_element *p_s_last_element;
|
||||
struct buf *p_s_bp, *tmp_bp;
|
||||
|
||||
/*
|
||||
* As we add each node to a path we increase its count. This means that
|
||||
* we must be careful to release all nodes in a path before we either
|
||||
* discard the path struct or re-use the path struct, as we do here.
|
||||
*/
|
||||
decrement_counters_in_path(p_s_search_path);
|
||||
|
||||
/*
|
||||
* With each iteration of this loop we search through the items in the
|
||||
* current node, and calculate the next current node(next path element)
|
||||
* for the next iteration of this loop...
|
||||
*/
|
||||
n_block_number = SB_ROOT_BLOCK(p_s_sbi);
|
||||
expected_level = -1;
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "root block: #%d\n", n_block_number);
|
||||
|
||||
while (1) {
|
||||
/* Prep path to have another element added to it. */
|
||||
reiserfs_log(LOG_DEBUG, "path element #%d\n",
|
||||
p_s_search_path->path_length);
|
||||
p_s_last_element = PATH_OFFSET_PELEMENT(p_s_search_path,
|
||||
++p_s_search_path->path_length);
|
||||
fs_gen = get_generation(p_s_sbi);
|
||||
|
||||
/*
|
||||
* Read the next tree node, and set the last element in the
|
||||
* path to have a pointer to it.
|
||||
*/
|
||||
reiserfs_log(LOG_DEBUG, "reading block #%d\n",
|
||||
n_block_number);
|
||||
if ((error = bread(p_s_sbi->s_devvp,
|
||||
n_block_number * btodb(p_s_sbi->s_blocksize),
|
||||
p_s_sbi->s_blocksize, NOCRED, &tmp_bp)) != 0) {
|
||||
reiserfs_log(LOG_DEBUG, "error reading block\n");
|
||||
p_s_search_path->path_length--;
|
||||
pathrelse(p_s_search_path);
|
||||
return (IO_ERROR);
|
||||
}
|
||||
reiserfs_log(LOG_DEBUG, "blkno = %ju, lblkno = %ju\n",
|
||||
(intmax_t)tmp_bp->b_blkno, (intmax_t)tmp_bp->b_lblkno);
|
||||
|
||||
/*
|
||||
* As i didn't found a way to handle the lock correctly,
|
||||
* i copy the data into a fake buffer
|
||||
*/
|
||||
reiserfs_log(LOG_DEBUG, "allocating p_s_bp\n");
|
||||
p_s_bp = malloc(sizeof *p_s_bp, M_REISERFSPATH, M_WAITOK);
|
||||
if (!p_s_bp) {
|
||||
reiserfs_log(LOG_DEBUG, "error allocating memory\n");
|
||||
p_s_search_path->path_length--;
|
||||
pathrelse(p_s_search_path);
|
||||
brelse(tmp_bp);
|
||||
return (IO_ERROR);
|
||||
}
|
||||
reiserfs_log(LOG_DEBUG, "copying struct buf\n");
|
||||
bcopy(tmp_bp, p_s_bp, sizeof(struct buf));
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "allocating p_s_bp->b_data\n");
|
||||
p_s_bp->b_data = malloc(p_s_sbi->s_blocksize,
|
||||
M_REISERFSPATH, M_WAITOK);
|
||||
if (!p_s_bp->b_data) {
|
||||
reiserfs_log(LOG_DEBUG, "error allocating memory\n");
|
||||
p_s_search_path->path_length--;
|
||||
pathrelse(p_s_search_path);
|
||||
free(p_s_bp, M_REISERFSPATH);
|
||||
brelse(tmp_bp);
|
||||
return (IO_ERROR);
|
||||
}
|
||||
reiserfs_log(LOG_DEBUG, "copying buffer data\n");
|
||||
bcopy(tmp_bp->b_data, p_s_bp->b_data, p_s_sbi->s_blocksize);
|
||||
brelse(tmp_bp);
|
||||
tmp_bp = NULL;
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "...done\n");
|
||||
p_s_last_element->pe_buffer = p_s_bp;
|
||||
|
||||
if (expected_level == -1)
|
||||
expected_level = SB_TREE_HEIGHT(p_s_sbi);
|
||||
expected_level--;
|
||||
reiserfs_log(LOG_DEBUG, "expected level: %d (%d)\n",
|
||||
expected_level, SB_TREE_HEIGHT(p_s_sbi));
|
||||
|
||||
/* XXX */
|
||||
/*
|
||||
* It is possible that schedule occurred. We must check
|
||||
* whether the key to search is still in the tree rooted
|
||||
* from the current buffer. If not then repeat search
|
||||
* from the root.
|
||||
*/
|
||||
if (fs_changed(fs_gen, p_s_sbi) &&
|
||||
(!B_IS_IN_TREE(p_s_bp) ||
|
||||
B_LEVEL(p_s_bp) != expected_level ||
|
||||
!key_in_buffer(p_s_search_path, p_s_key, p_s_sbi))) {
|
||||
reiserfs_log(LOG_DEBUG,
|
||||
"the key isn't in the tree anymore\n");
|
||||
decrement_counters_in_path(p_s_search_path);
|
||||
|
||||
/*
|
||||
* Get the root block number so that we can repeat
|
||||
* the search starting from the root.
|
||||
*/
|
||||
n_block_number = SB_ROOT_BLOCK(p_s_sbi);
|
||||
expected_level = -1;
|
||||
|
||||
/* Repeat search from the root */
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure, that the node contents look like a node of
|
||||
* certain level
|
||||
*/
|
||||
if (!is_tree_node(p_s_bp, expected_level)) {
|
||||
reiserfs_log(LOG_WARNING,
|
||||
"invalid format found in block %ju. Fsck?",
|
||||
(intmax_t)p_s_bp->b_blkno);
|
||||
pathrelse (p_s_search_path);
|
||||
return (IO_ERROR);
|
||||
}
|
||||
|
||||
/* Ok, we have acquired next formatted node in the tree */
|
||||
n_node_level = B_LEVEL(p_s_bp);
|
||||
reiserfs_log(LOG_DEBUG, "block info:\n");
|
||||
reiserfs_log(LOG_DEBUG, " node level: %d\n",
|
||||
n_node_level);
|
||||
reiserfs_log(LOG_DEBUG, " nb of items: %d\n",
|
||||
B_NR_ITEMS(p_s_bp));
|
||||
reiserfs_log(LOG_DEBUG, " free space: %d bytes\n",
|
||||
B_FREE_SPACE(p_s_bp));
|
||||
reiserfs_log(LOG_DEBUG, "bin_search with :\n"
|
||||
" p_s_key = (objectid=%d, dirid=%d)\n"
|
||||
" B_NR_ITEMS(p_s_bp) = %d\n"
|
||||
" p_s_last_element->pe_position = %d (path_length = %d)\n",
|
||||
p_s_key->on_disk_key.k_objectid,
|
||||
p_s_key->on_disk_key.k_dir_id,
|
||||
B_NR_ITEMS(p_s_bp),
|
||||
p_s_last_element->pe_position,
|
||||
p_s_search_path->path_length);
|
||||
n_retval = bin_search(p_s_key, B_N_PITEM_HEAD(p_s_bp, 0),
|
||||
B_NR_ITEMS(p_s_bp),
|
||||
(n_node_level == DISK_LEAF_NODE_LEVEL) ? IH_SIZE : KEY_SIZE,
|
||||
&(p_s_last_element->pe_position));
|
||||
reiserfs_log(LOG_DEBUG, "bin_search result: %d\n",
|
||||
n_retval);
|
||||
if (n_node_level == n_stop_level) {
|
||||
reiserfs_log(LOG_DEBUG, "stop level reached (%s)\n",
|
||||
n_retval == ITEM_FOUND ? "found" : "not found");
|
||||
return (n_retval);
|
||||
}
|
||||
|
||||
/* We are not in the stop level */
|
||||
if (n_retval == ITEM_FOUND)
|
||||
/*
|
||||
* Item has been found, so we choose the pointer
|
||||
* which is to the right of the found one
|
||||
*/
|
||||
p_s_last_element->pe_position++;
|
||||
|
||||
/*
|
||||
* If item was not found we choose the position which is
|
||||
* to the left of the found item. This requires no code,
|
||||
* bin_search did it already.
|
||||
*/
|
||||
|
||||
/*
|
||||
* So we have chosen a position in the current node which
|
||||
* is an internal node. Now we calculate child block number
|
||||
* by position in the node.
|
||||
*/
|
||||
n_block_number = B_N_CHILD_NUM(p_s_bp,
|
||||
p_s_last_element->pe_position);
|
||||
}
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "done\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Form the path to an item and position in this item which contains
|
||||
* file byte defined by p_s_key. If there is no such item corresponding
|
||||
* to the key, we point the path to the item with maximal key less than
|
||||
* p_s_key, and *p_n_pos_in_item is set to one past the last entry/byte
|
||||
* in the item. If searching for entry in a directory item, and it is
|
||||
* not found, *p_n_pos_in_item is set to one entry more than the entry
|
||||
* with maximal key which is less than the sought key.
|
||||
*
|
||||
* Note that if there is no entry in this same node which is one more,
|
||||
* then we point to an imaginary entry. For direct items, the position
|
||||
* is in units of bytes, for indirect items the position is in units
|
||||
* of blocknr entries, for directory items the position is in units of
|
||||
* directory entries.
|
||||
*/
|
||||
|
||||
/* The function is NOT SCHEDULE-SAFE! */
|
||||
int
|
||||
search_for_position_by_key(struct reiserfs_sb_info *p_s_sbi,
|
||||
const struct cpu_key *p_cpu_key, /* Key to search (cpu variable) */
|
||||
struct path *p_s_search_path) /* Filled up by this function. */
|
||||
{
|
||||
int retval, n_blk_size;
|
||||
off_t item_offset, offset;
|
||||
struct item_head *p_le_ih; /* Pointer to on-disk structure */
|
||||
struct reiserfs_dir_entry de;
|
||||
|
||||
/* If searching for directory entry. */
|
||||
if (is_direntry_cpu_key(p_cpu_key))
|
||||
return (search_by_entry_key(p_s_sbi, p_cpu_key,
|
||||
p_s_search_path, &de));
|
||||
|
||||
/* If not searching for directory entry. */
|
||||
|
||||
/* If item is found. */
|
||||
retval = search_item(p_s_sbi, p_cpu_key, p_s_search_path);
|
||||
if (retval == IO_ERROR)
|
||||
return (retval);
|
||||
if (retval == ITEM_FOUND) {
|
||||
if (ih_item_len(B_N_PITEM_HEAD(
|
||||
PATH_PLAST_BUFFER(p_s_search_path),
|
||||
PATH_LAST_POSITION(p_s_search_path))) == 0) {
|
||||
reiserfs_log(LOG_WARNING, "item length equals zero\n");
|
||||
}
|
||||
|
||||
pos_in_item(p_s_search_path) = 0;
|
||||
return (POSITION_FOUND);
|
||||
}
|
||||
|
||||
if (PATH_LAST_POSITION(p_s_search_path) == 0) {
|
||||
reiserfs_log(LOG_WARNING, "position equals zero\n");
|
||||
}
|
||||
|
||||
/* Item is not found. Set path to the previous item. */
|
||||
p_le_ih = B_N_PITEM_HEAD(PATH_PLAST_BUFFER(p_s_search_path),
|
||||
--PATH_LAST_POSITION(p_s_search_path));
|
||||
n_blk_size = p_s_sbi->s_blocksize;
|
||||
|
||||
if (comp_short_keys(&(p_le_ih->ih_key), p_cpu_key)) {
|
||||
return (FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
item_offset = le_ih_k_offset(p_le_ih);
|
||||
offset = cpu_key_k_offset(p_cpu_key);
|
||||
|
||||
/* Needed byte is contained in the item pointed to by the path.*/
|
||||
if (item_offset <= offset &&
|
||||
item_offset + op_bytes_number(p_le_ih, n_blk_size) > offset) {
|
||||
pos_in_item(p_s_search_path) = offset - item_offset;
|
||||
if (is_indirect_le_ih(p_le_ih)) {
|
||||
pos_in_item(p_s_search_path) /= n_blk_size;
|
||||
}
|
||||
return (POSITION_FOUND);
|
||||
}
|
||||
|
||||
/* Needed byte is not contained in the item pointed to by the
|
||||
* path. Set pos_in_item out of the item. */
|
||||
if (is_indirect_le_ih(p_le_ih))
|
||||
pos_in_item(p_s_search_path) =
|
||||
ih_item_len(p_le_ih) / UNFM_P_SIZE;
|
||||
else
|
||||
pos_in_item(p_s_search_path) =
|
||||
ih_item_len(p_le_ih);
|
||||
|
||||
return (POSITION_NOT_FOUND);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,353 +0,0 @@
|
||||
/*-
|
||||
* Copyright 2000 Hans Reiser
|
||||
* See README for licensing and copyright details
|
||||
*
|
||||
* Ported to FreeBSD by Jean-Sébastien Pédron <jspedron@club-internet.fr>
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <gnu/reiserfs/reiserfs_fs.h>
|
||||
|
||||
static vop_access_t reiserfs_access;
|
||||
static vop_bmap_t reiserfs_bmap;
|
||||
static vop_getattr_t reiserfs_getattr;
|
||||
static vop_open_t reiserfs_open;
|
||||
static vop_pathconf_t reiserfs_pathconf;
|
||||
static vop_readlink_t reiserfs_readlink;
|
||||
static vop_strategy_t reiserfs_strategy;
|
||||
|
||||
/* Global vfs data structures for ReiserFS */
|
||||
struct vop_vector reiserfs_vnodeops = {
|
||||
.vop_default = &default_vnodeops,
|
||||
|
||||
.vop_access = reiserfs_access,
|
||||
.vop_bmap = reiserfs_bmap,
|
||||
.vop_cachedlookup = reiserfs_lookup,
|
||||
.vop_getattr = reiserfs_getattr,
|
||||
.vop_inactive = reiserfs_inactive,
|
||||
.vop_lookup = vfs_cache_lookup,
|
||||
.vop_open = reiserfs_open,
|
||||
.vop_reclaim = reiserfs_reclaim,
|
||||
.vop_read = reiserfs_read,
|
||||
.vop_readdir = reiserfs_readdir,
|
||||
.vop_readlink = reiserfs_readlink,
|
||||
.vop_pathconf = reiserfs_pathconf,
|
||||
.vop_strategy = reiserfs_strategy,
|
||||
};
|
||||
|
||||
struct vop_vector reiserfs_specops = {
|
||||
.vop_default = &default_vnodeops,
|
||||
|
||||
.vop_access = reiserfs_access,
|
||||
.vop_getattr = reiserfs_getattr,
|
||||
.vop_inactive = reiserfs_inactive,
|
||||
.vop_reclaim = reiserfs_reclaim,
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* vnode operations
|
||||
* -------------------------------------------------------------------*/
|
||||
|
||||
static int
|
||||
reiserfs_access(struct vop_access_args *ap)
|
||||
{
|
||||
int error;
|
||||
struct vnode *vp = ap->a_vp;
|
||||
struct reiserfs_node *ip = VTOI(vp);
|
||||
mode_t mode = ap->a_mode;
|
||||
|
||||
/*
|
||||
* Disallow write attempts on read-only file systems; unless the file
|
||||
* is a socket, fifo, or a block or character device resident on the
|
||||
* file system.
|
||||
*/
|
||||
if (mode & VWRITE) {
|
||||
switch (vp->v_type) {
|
||||
case VDIR:
|
||||
case VLNK:
|
||||
case VREG:
|
||||
if (vp->v_mount->mnt_flag & MNT_RDONLY) {
|
||||
reiserfs_log(LOG_DEBUG,
|
||||
"no write access (read-only fs)\n");
|
||||
return (EROFS);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If immutable bit set, nobody gets to write it. */
|
||||
if ((mode & VWRITE) && (ip->i_flags & (IMMUTABLE | SF_SNAPSHOT))) {
|
||||
reiserfs_log(LOG_DEBUG, "no write access (immutable)\n");
|
||||
return (EPERM);
|
||||
}
|
||||
|
||||
error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid,
|
||||
ap->a_mode, ap->a_cred, NULL);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
reiserfs_getattr(struct vop_getattr_args *ap)
|
||||
{
|
||||
struct vnode *vp = ap->a_vp;
|
||||
struct vattr *vap = ap->a_vap;
|
||||
struct reiserfs_node *ip = VTOI(vp);
|
||||
|
||||
vap->va_fsid = dev2udev(ip->i_dev);
|
||||
vap->va_fileid = ip->i_number;
|
||||
vap->va_mode = ip->i_mode & ~S_IFMT;
|
||||
vap->va_nlink = ip->i_nlink;
|
||||
vap->va_uid = ip->i_uid;
|
||||
vap->va_gid = ip->i_gid;
|
||||
//XXX vap->va_rdev = ip->i_rdev;
|
||||
vap->va_size = ip->i_size;
|
||||
vap->va_atime = ip->i_atime;
|
||||
vap->va_mtime = ip->i_mtime;
|
||||
vap->va_ctime = ip->i_ctime;
|
||||
vap->va_flags = ip->i_flags;
|
||||
vap->va_gen = ip->i_generation;
|
||||
vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
|
||||
vap->va_bytes = dbtob((u_quad_t)ip->i_blocks);
|
||||
vap->va_type = vp->v_type;
|
||||
//XXX vap->va_filerev = ip->i_modrev;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Return POSIX pathconf information applicable to ReiserFS filesystems */
|
||||
static int
|
||||
reiserfs_pathconf(struct vop_pathconf_args *ap)
|
||||
{
|
||||
switch (ap->a_name) {
|
||||
case _PC_LINK_MAX:
|
||||
*ap->a_retval = REISERFS_LINK_MAX;
|
||||
return (0);
|
||||
case _PC_NAME_MAX:
|
||||
*ap->a_retval =
|
||||
REISERFS_MAX_NAME(VTOI(ap->a_vp)->i_reiserfs->s_blocksize);
|
||||
return (0);
|
||||
case _PC_PATH_MAX:
|
||||
*ap->a_retval = PATH_MAX;
|
||||
return (0);
|
||||
case _PC_PIPE_BUF:
|
||||
*ap->a_retval = PIPE_BUF;
|
||||
return (0);
|
||||
case _PC_CHOWN_RESTRICTED:
|
||||
*ap->a_retval = 1;
|
||||
return (0);
|
||||
case _PC_NO_TRUNC:
|
||||
*ap->a_retval = 1;
|
||||
return (0);
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
reiserfs_open(struct vop_open_args *ap)
|
||||
{
|
||||
/* Files marked append-only must be opened for appending. */
|
||||
if ((VTOI(ap->a_vp)->i_flags & APPEND) &&
|
||||
(ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
|
||||
return (EPERM);
|
||||
|
||||
vnode_create_vobject(ap->a_vp, VTOI(ap->a_vp)->i_size, ap->a_td);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Return target name of a symbolic link */
|
||||
static int
|
||||
reiserfs_readlink(struct vop_readlink_args *ap)
|
||||
{
|
||||
struct vnode *vp = ap->a_vp;
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "redirect to VOP_READ()\n");
|
||||
return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
|
||||
}
|
||||
|
||||
/* Bmap converts the logical block number of a file to its physical
|
||||
* block number on the disk. */
|
||||
static int
|
||||
reiserfs_bmap(ap)
|
||||
struct vop_bmap_args /* {
|
||||
struct vnode *a_vp;
|
||||
daddr_t a_bn;
|
||||
struct bufobj **a_bop;
|
||||
daddr_t *a_bnp;
|
||||
int *a_runp;
|
||||
int *a_runb;
|
||||
} */ *ap;
|
||||
{
|
||||
daddr_t blkno;
|
||||
struct buf *bp;
|
||||
struct cpu_key key;
|
||||
struct item_head *ih;
|
||||
|
||||
struct vnode *vp = ap->a_vp;
|
||||
struct reiserfs_node *ip = VTOI(vp);
|
||||
struct reiserfs_sb_info *sbi = ip->i_reiserfs;
|
||||
INITIALIZE_PATH(path);
|
||||
|
||||
/* Prepare the key to look for the 'block'-th block of file
|
||||
* (XXX we suppose that statfs.f_iosize == sbi->s_blocksize) */
|
||||
make_cpu_key(&key, ip, (off_t)ap->a_bn * sbi->s_blocksize + 1,
|
||||
TYPE_ANY, 3);
|
||||
|
||||
/* Search item */
|
||||
if (search_for_position_by_key(sbi, &key, &path) != POSITION_FOUND) {
|
||||
reiserfs_log(LOG_DEBUG, "position not found\n");
|
||||
pathrelse(&path);
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
bp = get_last_bp(&path);
|
||||
ih = get_ih(&path);
|
||||
|
||||
if (is_indirect_le_ih(ih)) {
|
||||
/* Indirect item can be read by the underlying layer, instead of
|
||||
* VOP_STRATEGY. */
|
||||
int i;
|
||||
uint32_t *ind_item = (uint32_t *)B_I_PITEM(bp, ih);
|
||||
reiserfs_log(LOG_DEBUG, "found an INDIRECT item\n");
|
||||
blkno = get_block_num(ind_item, path.pos_in_item);
|
||||
|
||||
/* Read-ahead */
|
||||
if (ap->a_runb) {
|
||||
uint32_t count = 0;
|
||||
for (i = path.pos_in_item - 1; i >= 0; --i) {
|
||||
if ((blkno - get_block_num(ind_item, i)) !=
|
||||
count + 1)
|
||||
break;
|
||||
++count;
|
||||
}
|
||||
|
||||
/*
|
||||
* This count isn't expressed in DEV_BSIZE base but
|
||||
* in fs' own block base
|
||||
* (see sys/vm/vnode_pager.c:vnode_pager_addr())
|
||||
*/
|
||||
*ap->a_runb = count;
|
||||
reiserfs_log(LOG_DEBUG,
|
||||
" read-ahead: %d blocks before\n", *ap->a_runb);
|
||||
}
|
||||
if (ap->a_runp) {
|
||||
uint32_t count = 0;
|
||||
/*
|
||||
* ih is an uint32_t array, that's why we use
|
||||
* its length (in bytes) divided by 4 to know
|
||||
* the number of items
|
||||
*/
|
||||
for (i = path.pos_in_item + 1;
|
||||
i < ih_item_len(ih) / 4; ++i) {
|
||||
if ((get_block_num(ind_item, i) - blkno) !=
|
||||
count + 1)
|
||||
break;
|
||||
++count;
|
||||
}
|
||||
|
||||
/*
|
||||
* This count isn't expressed in DEV_BSIZE base but
|
||||
* in fs' own block base
|
||||
* (see sys/vm/vnode_pager.c:vnode_pager_addr()) */
|
||||
*ap->a_runp = count;
|
||||
reiserfs_log(LOG_DEBUG,
|
||||
" read-ahead: %d blocks after\n", *ap->a_runp);
|
||||
}
|
||||
|
||||
/* Indirect items can be read using the device VOP_STRATEGY */
|
||||
if (ap->a_bop)
|
||||
*ap->a_bop = &VTOI(ap->a_vp)->i_devvp->v_bufobj;
|
||||
|
||||
/* Convert the block number into DEV_BSIZE base */
|
||||
blkno *= btodb(sbi->s_blocksize);
|
||||
} else {
|
||||
/*
|
||||
* Direct item are not DEV_BSIZE aligned, VOP_STRATEGY will
|
||||
* have to handle this case specifically
|
||||
*/
|
||||
reiserfs_log(LOG_DEBUG, "found a DIRECT item\n");
|
||||
blkno = ap->a_bn;
|
||||
|
||||
if (ap->a_runp)
|
||||
*ap->a_runp = 0;
|
||||
if (ap->a_runb)
|
||||
*ap->a_runb = 0;
|
||||
|
||||
/* Direct item must be read by reiserfs_strategy */
|
||||
if (ap->a_bop)
|
||||
*ap->a_bop = &vp->v_bufobj;
|
||||
}
|
||||
|
||||
if (ap->a_bnp)
|
||||
*ap->a_bnp = blkno;
|
||||
|
||||
pathrelse(&path);
|
||||
|
||||
if (ap->a_bnp) {
|
||||
reiserfs_log(LOG_DEBUG, "logical block: %ju (%ju),"
|
||||
" physical block: %ju (%ju)\n",
|
||||
(intmax_t)ap->a_bn,
|
||||
(intmax_t)(ap->a_bn / btodb(sbi->s_blocksize)),
|
||||
(intmax_t)*ap->a_bnp,
|
||||
(intmax_t)(*ap->a_bnp / btodb(sbi->s_blocksize)));
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Does simply the same as reiserfs_read. It's called when reiserfs_bmap find
|
||||
* an direct item. */
|
||||
static int
|
||||
reiserfs_strategy(struct vop_strategy_args /* {
|
||||
struct vnode *a_vp;
|
||||
struct buf *a_bp;
|
||||
} */ *ap)
|
||||
{
|
||||
int error;
|
||||
struct uio auio;
|
||||
struct iovec aiov;
|
||||
struct reiserfs_node *ip;
|
||||
struct buf *bp = ap->a_bp;
|
||||
struct vnode *vp = ap->a_vp;
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "logical block: %ju,"
|
||||
" physical block: %ju\n", (intmax_t)bp->b_lblkno,
|
||||
(intmax_t)bp->b_blkno);
|
||||
|
||||
ip = VTOI(vp);
|
||||
|
||||
if (bp->b_iocmd == BIO_READ) {
|
||||
/* Prepare the uio structure */
|
||||
reiserfs_log(LOG_DEBUG, "prepare uio structure\n");
|
||||
aiov.iov_base = bp->b_data;
|
||||
aiov.iov_len = MIN(bp->b_bcount, ip->i_size);
|
||||
reiserfs_log(LOG_DEBUG, " vector length: %ju\n",
|
||||
(intmax_t)aiov.iov_len);
|
||||
|
||||
auio.uio_iov = &aiov;
|
||||
auio.uio_iovcnt = 1;
|
||||
auio.uio_offset = 0;
|
||||
auio.uio_rw = UIO_READ;
|
||||
auio.uio_segflg = UIO_SYSSPACE;
|
||||
auio.uio_td = curthread;
|
||||
auio.uio_resid = bp->b_bcount;
|
||||
reiserfs_log(LOG_DEBUG, " buffer length: %u\n",
|
||||
auio.uio_resid);
|
||||
|
||||
reiserfs_log(LOG_DEBUG, "reading block #%ju\n",
|
||||
(intmax_t)bp->b_blkno);
|
||||
error = reiserfs_get_block(ip, bp->b_blkno, 0, &auio);
|
||||
} else {
|
||||
/* No write support yet */
|
||||
error = (EOPNOTSUPP);
|
||||
bp->b_error = error;
|
||||
bp->b_ioflags |= BIO_ERROR;
|
||||
}
|
||||
|
||||
bufdone(bp);
|
||||
return (error);
|
||||
}
|
Loading…
Reference in New Issue
Block a user