forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
888 lines
23 KiB
888 lines
23 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Copyright (c) 2000,2002,2005 Silicon Graphics, Inc. |
|
* Copyright (c) 2013 Red Hat, Inc. |
|
* All Rights Reserved. |
|
*/ |
|
#include "xfs.h" |
|
#include "xfs_fs.h" |
|
#include "xfs_shared.h" |
|
#include "xfs_format.h" |
|
#include "xfs_log_format.h" |
|
#include "xfs_trans_resv.h" |
|
#include "xfs_mount.h" |
|
#include "xfs_inode.h" |
|
#include "xfs_dir2.h" |
|
|
|
/* |
|
* Shortform directory ops |
|
*/ |
|
static int |
|
xfs_dir2_sf_entsize( |
|
struct xfs_dir2_sf_hdr *hdr, |
|
int len) |
|
{ |
|
int count = sizeof(struct xfs_dir2_sf_entry); /* namelen + offset */ |
|
|
|
count += len; /* name */ |
|
count += hdr->i8count ? XFS_INO64_SIZE : XFS_INO32_SIZE; /* ino # */ |
|
return count; |
|
} |
|
|
|
static int |
|
xfs_dir3_sf_entsize( |
|
struct xfs_dir2_sf_hdr *hdr, |
|
int len) |
|
{ |
|
return xfs_dir2_sf_entsize(hdr, len) + sizeof(uint8_t); |
|
} |
|
|
|
static struct xfs_dir2_sf_entry * |
|
xfs_dir2_sf_nextentry( |
|
struct xfs_dir2_sf_hdr *hdr, |
|
struct xfs_dir2_sf_entry *sfep) |
|
{ |
|
return (struct xfs_dir2_sf_entry *) |
|
((char *)sfep + xfs_dir2_sf_entsize(hdr, sfep->namelen)); |
|
} |
|
|
|
static struct xfs_dir2_sf_entry * |
|
xfs_dir3_sf_nextentry( |
|
struct xfs_dir2_sf_hdr *hdr, |
|
struct xfs_dir2_sf_entry *sfep) |
|
{ |
|
return (struct xfs_dir2_sf_entry *) |
|
((char *)sfep + xfs_dir3_sf_entsize(hdr, sfep->namelen)); |
|
} |
|
|
|
|
|
/* |
|
* For filetype enabled shortform directories, the file type field is stored at |
|
* the end of the name. Because it's only a single byte, endian conversion is |
|
* not necessary. For non-filetype enable directories, the type is always |
|
* unknown and we never store the value. |
|
*/ |
|
static uint8_t |
|
xfs_dir2_sfe_get_ftype( |
|
struct xfs_dir2_sf_entry *sfep) |
|
{ |
|
return XFS_DIR3_FT_UNKNOWN; |
|
} |
|
|
|
static void |
|
xfs_dir2_sfe_put_ftype( |
|
struct xfs_dir2_sf_entry *sfep, |
|
uint8_t ftype) |
|
{ |
|
ASSERT(ftype < XFS_DIR3_FT_MAX); |
|
} |
|
|
|
static uint8_t |
|
xfs_dir3_sfe_get_ftype( |
|
struct xfs_dir2_sf_entry *sfep) |
|
{ |
|
uint8_t ftype; |
|
|
|
ftype = sfep->name[sfep->namelen]; |
|
if (ftype >= XFS_DIR3_FT_MAX) |
|
return XFS_DIR3_FT_UNKNOWN; |
|
return ftype; |
|
} |
|
|
|
static void |
|
xfs_dir3_sfe_put_ftype( |
|
struct xfs_dir2_sf_entry *sfep, |
|
uint8_t ftype) |
|
{ |
|
ASSERT(ftype < XFS_DIR3_FT_MAX); |
|
|
|
sfep->name[sfep->namelen] = ftype; |
|
} |
|
|
|
/* |
|
* Inode numbers in short-form directories can come in two versions, |
|
* either 4 bytes or 8 bytes wide. These helpers deal with the |
|
* two forms transparently by looking at the headers i8count field. |
|
* |
|
* For 64-bit inode number the most significant byte must be zero. |
|
*/ |
|
static xfs_ino_t |
|
xfs_dir2_sf_get_ino( |
|
struct xfs_dir2_sf_hdr *hdr, |
|
uint8_t *from) |
|
{ |
|
if (hdr->i8count) |
|
return get_unaligned_be64(from) & 0x00ffffffffffffffULL; |
|
else |
|
return get_unaligned_be32(from); |
|
} |
|
|
|
static void |
|
xfs_dir2_sf_put_ino( |
|
struct xfs_dir2_sf_hdr *hdr, |
|
uint8_t *to, |
|
xfs_ino_t ino) |
|
{ |
|
ASSERT((ino & 0xff00000000000000ULL) == 0); |
|
|
|
if (hdr->i8count) |
|
put_unaligned_be64(ino, to); |
|
else |
|
put_unaligned_be32(ino, to); |
|
} |
|
|
|
static xfs_ino_t |
|
xfs_dir2_sf_get_parent_ino( |
|
struct xfs_dir2_sf_hdr *hdr) |
|
{ |
|
return xfs_dir2_sf_get_ino(hdr, hdr->parent); |
|
} |
|
|
|
static void |
|
xfs_dir2_sf_put_parent_ino( |
|
struct xfs_dir2_sf_hdr *hdr, |
|
xfs_ino_t ino) |
|
{ |
|
xfs_dir2_sf_put_ino(hdr, hdr->parent, ino); |
|
} |
|
|
|
/* |
|
* In short-form directory entries the inode numbers are stored at variable |
|
* offset behind the entry name. If the entry stores a filetype value, then it |
|
* sits between the name and the inode number. Hence the inode numbers may only |
|
* be accessed through the helpers below. |
|
*/ |
|
static xfs_ino_t |
|
xfs_dir2_sfe_get_ino( |
|
struct xfs_dir2_sf_hdr *hdr, |
|
struct xfs_dir2_sf_entry *sfep) |
|
{ |
|
return xfs_dir2_sf_get_ino(hdr, &sfep->name[sfep->namelen]); |
|
} |
|
|
|
static void |
|
xfs_dir2_sfe_put_ino( |
|
struct xfs_dir2_sf_hdr *hdr, |
|
struct xfs_dir2_sf_entry *sfep, |
|
xfs_ino_t ino) |
|
{ |
|
xfs_dir2_sf_put_ino(hdr, &sfep->name[sfep->namelen], ino); |
|
} |
|
|
|
static xfs_ino_t |
|
xfs_dir3_sfe_get_ino( |
|
struct xfs_dir2_sf_hdr *hdr, |
|
struct xfs_dir2_sf_entry *sfep) |
|
{ |
|
return xfs_dir2_sf_get_ino(hdr, &sfep->name[sfep->namelen + 1]); |
|
} |
|
|
|
static void |
|
xfs_dir3_sfe_put_ino( |
|
struct xfs_dir2_sf_hdr *hdr, |
|
struct xfs_dir2_sf_entry *sfep, |
|
xfs_ino_t ino) |
|
{ |
|
xfs_dir2_sf_put_ino(hdr, &sfep->name[sfep->namelen + 1], ino); |
|
} |
|
|
|
|
|
/* |
|
* Directory data block operations |
|
*/ |
|
|
|
/* |
|
* For special situations, the dirent size ends up fixed because we always know |
|
* what the size of the entry is. That's true for the "." and "..", and |
|
* therefore we know that they are a fixed size and hence their offsets are |
|
* constant, as is the first entry. |
|
* |
|
* Hence, this calculation is written as a macro to be able to be calculated at |
|
* compile time and so certain offsets can be calculated directly in the |
|
* structure initaliser via the macro. There are two macros - one for dirents |
|
* with ftype and without so there are no unresolvable conditionals in the |
|
* calculations. We also use round_up() as XFS_DIR2_DATA_ALIGN is always a power |
|
* of 2 and the compiler doesn't reject it (unlike roundup()). |
|
*/ |
|
#define XFS_DIR2_DATA_ENTSIZE(n) \ |
|
round_up((offsetof(struct xfs_dir2_data_entry, name[0]) + (n) + \ |
|
sizeof(xfs_dir2_data_off_t)), XFS_DIR2_DATA_ALIGN) |
|
|
|
#define XFS_DIR3_DATA_ENTSIZE(n) \ |
|
round_up((offsetof(struct xfs_dir2_data_entry, name[0]) + (n) + \ |
|
sizeof(xfs_dir2_data_off_t) + sizeof(uint8_t)), \ |
|
XFS_DIR2_DATA_ALIGN) |
|
|
|
static int |
|
xfs_dir2_data_entsize( |
|
int n) |
|
{ |
|
return XFS_DIR2_DATA_ENTSIZE(n); |
|
} |
|
|
|
static int |
|
xfs_dir3_data_entsize( |
|
int n) |
|
{ |
|
return XFS_DIR3_DATA_ENTSIZE(n); |
|
} |
|
|
|
static uint8_t |
|
xfs_dir2_data_get_ftype( |
|
struct xfs_dir2_data_entry *dep) |
|
{ |
|
return XFS_DIR3_FT_UNKNOWN; |
|
} |
|
|
|
static void |
|
xfs_dir2_data_put_ftype( |
|
struct xfs_dir2_data_entry *dep, |
|
uint8_t ftype) |
|
{ |
|
ASSERT(ftype < XFS_DIR3_FT_MAX); |
|
} |
|
|
|
static uint8_t |
|
xfs_dir3_data_get_ftype( |
|
struct xfs_dir2_data_entry *dep) |
|
{ |
|
uint8_t ftype = dep->name[dep->namelen]; |
|
|
|
if (ftype >= XFS_DIR3_FT_MAX) |
|
return XFS_DIR3_FT_UNKNOWN; |
|
return ftype; |
|
} |
|
|
|
static void |
|
xfs_dir3_data_put_ftype( |
|
struct xfs_dir2_data_entry *dep, |
|
uint8_t type) |
|
{ |
|
ASSERT(type < XFS_DIR3_FT_MAX); |
|
ASSERT(dep->namelen != 0); |
|
|
|
dep->name[dep->namelen] = type; |
|
} |
|
|
|
/* |
|
* Pointer to an entry's tag word. |
|
*/ |
|
static __be16 * |
|
xfs_dir2_data_entry_tag_p( |
|
struct xfs_dir2_data_entry *dep) |
|
{ |
|
return (__be16 *)((char *)dep + |
|
xfs_dir2_data_entsize(dep->namelen) - sizeof(__be16)); |
|
} |
|
|
|
static __be16 * |
|
xfs_dir3_data_entry_tag_p( |
|
struct xfs_dir2_data_entry *dep) |
|
{ |
|
return (__be16 *)((char *)dep + |
|
xfs_dir3_data_entsize(dep->namelen) - sizeof(__be16)); |
|
} |
|
|
|
/* |
|
* location of . and .. in data space (always block 0) |
|
*/ |
|
static struct xfs_dir2_data_entry * |
|
xfs_dir2_data_dot_entry_p( |
|
struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return (struct xfs_dir2_data_entry *) |
|
((char *)hdr + sizeof(struct xfs_dir2_data_hdr)); |
|
} |
|
|
|
static struct xfs_dir2_data_entry * |
|
xfs_dir2_data_dotdot_entry_p( |
|
struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return (struct xfs_dir2_data_entry *) |
|
((char *)hdr + sizeof(struct xfs_dir2_data_hdr) + |
|
XFS_DIR2_DATA_ENTSIZE(1)); |
|
} |
|
|
|
static struct xfs_dir2_data_entry * |
|
xfs_dir2_data_first_entry_p( |
|
struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return (struct xfs_dir2_data_entry *) |
|
((char *)hdr + sizeof(struct xfs_dir2_data_hdr) + |
|
XFS_DIR2_DATA_ENTSIZE(1) + |
|
XFS_DIR2_DATA_ENTSIZE(2)); |
|
} |
|
|
|
static struct xfs_dir2_data_entry * |
|
xfs_dir2_ftype_data_dotdot_entry_p( |
|
struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return (struct xfs_dir2_data_entry *) |
|
((char *)hdr + sizeof(struct xfs_dir2_data_hdr) + |
|
XFS_DIR3_DATA_ENTSIZE(1)); |
|
} |
|
|
|
static struct xfs_dir2_data_entry * |
|
xfs_dir2_ftype_data_first_entry_p( |
|
struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return (struct xfs_dir2_data_entry *) |
|
((char *)hdr + sizeof(struct xfs_dir2_data_hdr) + |
|
XFS_DIR3_DATA_ENTSIZE(1) + |
|
XFS_DIR3_DATA_ENTSIZE(2)); |
|
} |
|
|
|
static struct xfs_dir2_data_entry * |
|
xfs_dir3_data_dot_entry_p( |
|
struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return (struct xfs_dir2_data_entry *) |
|
((char *)hdr + sizeof(struct xfs_dir3_data_hdr)); |
|
} |
|
|
|
static struct xfs_dir2_data_entry * |
|
xfs_dir3_data_dotdot_entry_p( |
|
struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return (struct xfs_dir2_data_entry *) |
|
((char *)hdr + sizeof(struct xfs_dir3_data_hdr) + |
|
XFS_DIR3_DATA_ENTSIZE(1)); |
|
} |
|
|
|
static struct xfs_dir2_data_entry * |
|
xfs_dir3_data_first_entry_p( |
|
struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return (struct xfs_dir2_data_entry *) |
|
((char *)hdr + sizeof(struct xfs_dir3_data_hdr) + |
|
XFS_DIR3_DATA_ENTSIZE(1) + |
|
XFS_DIR3_DATA_ENTSIZE(2)); |
|
} |
|
|
|
static struct xfs_dir2_data_free * |
|
xfs_dir2_data_bestfree_p(struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return hdr->bestfree; |
|
} |
|
|
|
static struct xfs_dir2_data_free * |
|
xfs_dir3_data_bestfree_p(struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return ((struct xfs_dir3_data_hdr *)hdr)->best_free; |
|
} |
|
|
|
static struct xfs_dir2_data_entry * |
|
xfs_dir2_data_entry_p(struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return (struct xfs_dir2_data_entry *) |
|
((char *)hdr + sizeof(struct xfs_dir2_data_hdr)); |
|
} |
|
|
|
static struct xfs_dir2_data_unused * |
|
xfs_dir2_data_unused_p(struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return (struct xfs_dir2_data_unused *) |
|
((char *)hdr + sizeof(struct xfs_dir2_data_hdr)); |
|
} |
|
|
|
static struct xfs_dir2_data_entry * |
|
xfs_dir3_data_entry_p(struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return (struct xfs_dir2_data_entry *) |
|
((char *)hdr + sizeof(struct xfs_dir3_data_hdr)); |
|
} |
|
|
|
static struct xfs_dir2_data_unused * |
|
xfs_dir3_data_unused_p(struct xfs_dir2_data_hdr *hdr) |
|
{ |
|
return (struct xfs_dir2_data_unused *) |
|
((char *)hdr + sizeof(struct xfs_dir3_data_hdr)); |
|
} |
|
|
|
|
|
/* |
|
* Directory Leaf block operations |
|
*/ |
|
static int |
|
xfs_dir2_max_leaf_ents(struct xfs_da_geometry *geo) |
|
{ |
|
return (geo->blksize - sizeof(struct xfs_dir2_leaf_hdr)) / |
|
(uint)sizeof(struct xfs_dir2_leaf_entry); |
|
} |
|
|
|
static struct xfs_dir2_leaf_entry * |
|
xfs_dir2_leaf_ents_p(struct xfs_dir2_leaf *lp) |
|
{ |
|
return lp->__ents; |
|
} |
|
|
|
static int |
|
xfs_dir3_max_leaf_ents(struct xfs_da_geometry *geo) |
|
{ |
|
return (geo->blksize - sizeof(struct xfs_dir3_leaf_hdr)) / |
|
(uint)sizeof(struct xfs_dir2_leaf_entry); |
|
} |
|
|
|
static struct xfs_dir2_leaf_entry * |
|
xfs_dir3_leaf_ents_p(struct xfs_dir2_leaf *lp) |
|
{ |
|
return ((struct xfs_dir3_leaf *)lp)->__ents; |
|
} |
|
|
|
static void |
|
xfs_dir2_leaf_hdr_from_disk( |
|
struct xfs_dir3_icleaf_hdr *to, |
|
struct xfs_dir2_leaf *from) |
|
{ |
|
to->forw = be32_to_cpu(from->hdr.info.forw); |
|
to->back = be32_to_cpu(from->hdr.info.back); |
|
to->magic = be16_to_cpu(from->hdr.info.magic); |
|
to->count = be16_to_cpu(from->hdr.count); |
|
to->stale = be16_to_cpu(from->hdr.stale); |
|
|
|
ASSERT(to->magic == XFS_DIR2_LEAF1_MAGIC || |
|
to->magic == XFS_DIR2_LEAFN_MAGIC); |
|
} |
|
|
|
static void |
|
xfs_dir2_leaf_hdr_to_disk( |
|
struct xfs_dir2_leaf *to, |
|
struct xfs_dir3_icleaf_hdr *from) |
|
{ |
|
ASSERT(from->magic == XFS_DIR2_LEAF1_MAGIC || |
|
from->magic == XFS_DIR2_LEAFN_MAGIC); |
|
|
|
to->hdr.info.forw = cpu_to_be32(from->forw); |
|
to->hdr.info.back = cpu_to_be32(from->back); |
|
to->hdr.info.magic = cpu_to_be16(from->magic); |
|
to->hdr.count = cpu_to_be16(from->count); |
|
to->hdr.stale = cpu_to_be16(from->stale); |
|
} |
|
|
|
static void |
|
xfs_dir3_leaf_hdr_from_disk( |
|
struct xfs_dir3_icleaf_hdr *to, |
|
struct xfs_dir2_leaf *from) |
|
{ |
|
struct xfs_dir3_leaf_hdr *hdr3 = (struct xfs_dir3_leaf_hdr *)from; |
|
|
|
to->forw = be32_to_cpu(hdr3->info.hdr.forw); |
|
to->back = be32_to_cpu(hdr3->info.hdr.back); |
|
to->magic = be16_to_cpu(hdr3->info.hdr.magic); |
|
to->count = be16_to_cpu(hdr3->count); |
|
to->stale = be16_to_cpu(hdr3->stale); |
|
|
|
ASSERT(to->magic == XFS_DIR3_LEAF1_MAGIC || |
|
to->magic == XFS_DIR3_LEAFN_MAGIC); |
|
} |
|
|
|
static void |
|
xfs_dir3_leaf_hdr_to_disk( |
|
struct xfs_dir2_leaf *to, |
|
struct xfs_dir3_icleaf_hdr *from) |
|
{ |
|
struct xfs_dir3_leaf_hdr *hdr3 = (struct xfs_dir3_leaf_hdr *)to; |
|
|
|
ASSERT(from->magic == XFS_DIR3_LEAF1_MAGIC || |
|
from->magic == XFS_DIR3_LEAFN_MAGIC); |
|
|
|
hdr3->info.hdr.forw = cpu_to_be32(from->forw); |
|
hdr3->info.hdr.back = cpu_to_be32(from->back); |
|
hdr3->info.hdr.magic = cpu_to_be16(from->magic); |
|
hdr3->count = cpu_to_be16(from->count); |
|
hdr3->stale = cpu_to_be16(from->stale); |
|
} |
|
|
|
|
|
/* |
|
* Directory/Attribute Node block operations |
|
*/ |
|
static struct xfs_da_node_entry * |
|
xfs_da2_node_tree_p(struct xfs_da_intnode *dap) |
|
{ |
|
return dap->__btree; |
|
} |
|
|
|
static struct xfs_da_node_entry * |
|
xfs_da3_node_tree_p(struct xfs_da_intnode *dap) |
|
{ |
|
return ((struct xfs_da3_intnode *)dap)->__btree; |
|
} |
|
|
|
static void |
|
xfs_da2_node_hdr_from_disk( |
|
struct xfs_da3_icnode_hdr *to, |
|
struct xfs_da_intnode *from) |
|
{ |
|
ASSERT(from->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC)); |
|
to->forw = be32_to_cpu(from->hdr.info.forw); |
|
to->back = be32_to_cpu(from->hdr.info.back); |
|
to->magic = be16_to_cpu(from->hdr.info.magic); |
|
to->count = be16_to_cpu(from->hdr.__count); |
|
to->level = be16_to_cpu(from->hdr.__level); |
|
} |
|
|
|
static void |
|
xfs_da2_node_hdr_to_disk( |
|
struct xfs_da_intnode *to, |
|
struct xfs_da3_icnode_hdr *from) |
|
{ |
|
ASSERT(from->magic == XFS_DA_NODE_MAGIC); |
|
to->hdr.info.forw = cpu_to_be32(from->forw); |
|
to->hdr.info.back = cpu_to_be32(from->back); |
|
to->hdr.info.magic = cpu_to_be16(from->magic); |
|
to->hdr.__count = cpu_to_be16(from->count); |
|
to->hdr.__level = cpu_to_be16(from->level); |
|
} |
|
|
|
static void |
|
xfs_da3_node_hdr_from_disk( |
|
struct xfs_da3_icnode_hdr *to, |
|
struct xfs_da_intnode *from) |
|
{ |
|
struct xfs_da3_node_hdr *hdr3 = (struct xfs_da3_node_hdr *)from; |
|
|
|
ASSERT(from->hdr.info.magic == cpu_to_be16(XFS_DA3_NODE_MAGIC)); |
|
to->forw = be32_to_cpu(hdr3->info.hdr.forw); |
|
to->back = be32_to_cpu(hdr3->info.hdr.back); |
|
to->magic = be16_to_cpu(hdr3->info.hdr.magic); |
|
to->count = be16_to_cpu(hdr3->__count); |
|
to->level = be16_to_cpu(hdr3->__level); |
|
} |
|
|
|
static void |
|
xfs_da3_node_hdr_to_disk( |
|
struct xfs_da_intnode *to, |
|
struct xfs_da3_icnode_hdr *from) |
|
{ |
|
struct xfs_da3_node_hdr *hdr3 = (struct xfs_da3_node_hdr *)to; |
|
|
|
ASSERT(from->magic == XFS_DA3_NODE_MAGIC); |
|
hdr3->info.hdr.forw = cpu_to_be32(from->forw); |
|
hdr3->info.hdr.back = cpu_to_be32(from->back); |
|
hdr3->info.hdr.magic = cpu_to_be16(from->magic); |
|
hdr3->__count = cpu_to_be16(from->count); |
|
hdr3->__level = cpu_to_be16(from->level); |
|
} |
|
|
|
|
|
/* |
|
* Directory free space block operations |
|
*/ |
|
static int |
|
xfs_dir2_free_max_bests(struct xfs_da_geometry *geo) |
|
{ |
|
return (geo->blksize - sizeof(struct xfs_dir2_free_hdr)) / |
|
sizeof(xfs_dir2_data_off_t); |
|
} |
|
|
|
static __be16 * |
|
xfs_dir2_free_bests_p(struct xfs_dir2_free *free) |
|
{ |
|
return (__be16 *)((char *)free + sizeof(struct xfs_dir2_free_hdr)); |
|
} |
|
|
|
/* |
|
* Convert data space db to the corresponding free db. |
|
*/ |
|
static xfs_dir2_db_t |
|
xfs_dir2_db_to_fdb(struct xfs_da_geometry *geo, xfs_dir2_db_t db) |
|
{ |
|
return xfs_dir2_byte_to_db(geo, XFS_DIR2_FREE_OFFSET) + |
|
(db / xfs_dir2_free_max_bests(geo)); |
|
} |
|
|
|
/* |
|
* Convert data space db to the corresponding index in a free db. |
|
*/ |
|
static int |
|
xfs_dir2_db_to_fdindex(struct xfs_da_geometry *geo, xfs_dir2_db_t db) |
|
{ |
|
return db % xfs_dir2_free_max_bests(geo); |
|
} |
|
|
|
static int |
|
xfs_dir3_free_max_bests(struct xfs_da_geometry *geo) |
|
{ |
|
return (geo->blksize - sizeof(struct xfs_dir3_free_hdr)) / |
|
sizeof(xfs_dir2_data_off_t); |
|
} |
|
|
|
static __be16 * |
|
xfs_dir3_free_bests_p(struct xfs_dir2_free *free) |
|
{ |
|
return (__be16 *)((char *)free + sizeof(struct xfs_dir3_free_hdr)); |
|
} |
|
|
|
/* |
|
* Convert data space db to the corresponding free db. |
|
*/ |
|
static xfs_dir2_db_t |
|
xfs_dir3_db_to_fdb(struct xfs_da_geometry *geo, xfs_dir2_db_t db) |
|
{ |
|
return xfs_dir2_byte_to_db(geo, XFS_DIR2_FREE_OFFSET) + |
|
(db / xfs_dir3_free_max_bests(geo)); |
|
} |
|
|
|
/* |
|
* Convert data space db to the corresponding index in a free db. |
|
*/ |
|
static int |
|
xfs_dir3_db_to_fdindex(struct xfs_da_geometry *geo, xfs_dir2_db_t db) |
|
{ |
|
return db % xfs_dir3_free_max_bests(geo); |
|
} |
|
|
|
static void |
|
xfs_dir2_free_hdr_from_disk( |
|
struct xfs_dir3_icfree_hdr *to, |
|
struct xfs_dir2_free *from) |
|
{ |
|
to->magic = be32_to_cpu(from->hdr.magic); |
|
to->firstdb = be32_to_cpu(from->hdr.firstdb); |
|
to->nvalid = be32_to_cpu(from->hdr.nvalid); |
|
to->nused = be32_to_cpu(from->hdr.nused); |
|
ASSERT(to->magic == XFS_DIR2_FREE_MAGIC); |
|
} |
|
|
|
static void |
|
xfs_dir2_free_hdr_to_disk( |
|
struct xfs_dir2_free *to, |
|
struct xfs_dir3_icfree_hdr *from) |
|
{ |
|
ASSERT(from->magic == XFS_DIR2_FREE_MAGIC); |
|
|
|
to->hdr.magic = cpu_to_be32(from->magic); |
|
to->hdr.firstdb = cpu_to_be32(from->firstdb); |
|
to->hdr.nvalid = cpu_to_be32(from->nvalid); |
|
to->hdr.nused = cpu_to_be32(from->nused); |
|
} |
|
|
|
static void |
|
xfs_dir3_free_hdr_from_disk( |
|
struct xfs_dir3_icfree_hdr *to, |
|
struct xfs_dir2_free *from) |
|
{ |
|
struct xfs_dir3_free_hdr *hdr3 = (struct xfs_dir3_free_hdr *)from; |
|
|
|
to->magic = be32_to_cpu(hdr3->hdr.magic); |
|
to->firstdb = be32_to_cpu(hdr3->firstdb); |
|
to->nvalid = be32_to_cpu(hdr3->nvalid); |
|
to->nused = be32_to_cpu(hdr3->nused); |
|
|
|
ASSERT(to->magic == XFS_DIR3_FREE_MAGIC); |
|
} |
|
|
|
static void |
|
xfs_dir3_free_hdr_to_disk( |
|
struct xfs_dir2_free *to, |
|
struct xfs_dir3_icfree_hdr *from) |
|
{ |
|
struct xfs_dir3_free_hdr *hdr3 = (struct xfs_dir3_free_hdr *)to; |
|
|
|
ASSERT(from->magic == XFS_DIR3_FREE_MAGIC); |
|
|
|
hdr3->hdr.magic = cpu_to_be32(from->magic); |
|
hdr3->firstdb = cpu_to_be32(from->firstdb); |
|
hdr3->nvalid = cpu_to_be32(from->nvalid); |
|
hdr3->nused = cpu_to_be32(from->nused); |
|
} |
|
|
|
static const struct xfs_dir_ops xfs_dir2_ops = { |
|
.sf_entsize = xfs_dir2_sf_entsize, |
|
.sf_nextentry = xfs_dir2_sf_nextentry, |
|
.sf_get_ftype = xfs_dir2_sfe_get_ftype, |
|
.sf_put_ftype = xfs_dir2_sfe_put_ftype, |
|
.sf_get_ino = xfs_dir2_sfe_get_ino, |
|
.sf_put_ino = xfs_dir2_sfe_put_ino, |
|
.sf_get_parent_ino = xfs_dir2_sf_get_parent_ino, |
|
.sf_put_parent_ino = xfs_dir2_sf_put_parent_ino, |
|
|
|
.data_entsize = xfs_dir2_data_entsize, |
|
.data_get_ftype = xfs_dir2_data_get_ftype, |
|
.data_put_ftype = xfs_dir2_data_put_ftype, |
|
.data_entry_tag_p = xfs_dir2_data_entry_tag_p, |
|
.data_bestfree_p = xfs_dir2_data_bestfree_p, |
|
|
|
.data_dot_offset = sizeof(struct xfs_dir2_data_hdr), |
|
.data_dotdot_offset = sizeof(struct xfs_dir2_data_hdr) + |
|
XFS_DIR2_DATA_ENTSIZE(1), |
|
.data_first_offset = sizeof(struct xfs_dir2_data_hdr) + |
|
XFS_DIR2_DATA_ENTSIZE(1) + |
|
XFS_DIR2_DATA_ENTSIZE(2), |
|
.data_entry_offset = sizeof(struct xfs_dir2_data_hdr), |
|
|
|
.data_dot_entry_p = xfs_dir2_data_dot_entry_p, |
|
.data_dotdot_entry_p = xfs_dir2_data_dotdot_entry_p, |
|
.data_first_entry_p = xfs_dir2_data_first_entry_p, |
|
.data_entry_p = xfs_dir2_data_entry_p, |
|
.data_unused_p = xfs_dir2_data_unused_p, |
|
|
|
.leaf_hdr_size = sizeof(struct xfs_dir2_leaf_hdr), |
|
.leaf_hdr_to_disk = xfs_dir2_leaf_hdr_to_disk, |
|
.leaf_hdr_from_disk = xfs_dir2_leaf_hdr_from_disk, |
|
.leaf_max_ents = xfs_dir2_max_leaf_ents, |
|
.leaf_ents_p = xfs_dir2_leaf_ents_p, |
|
|
|
.node_hdr_size = sizeof(struct xfs_da_node_hdr), |
|
.node_hdr_to_disk = xfs_da2_node_hdr_to_disk, |
|
.node_hdr_from_disk = xfs_da2_node_hdr_from_disk, |
|
.node_tree_p = xfs_da2_node_tree_p, |
|
|
|
.free_hdr_size = sizeof(struct xfs_dir2_free_hdr), |
|
.free_hdr_to_disk = xfs_dir2_free_hdr_to_disk, |
|
.free_hdr_from_disk = xfs_dir2_free_hdr_from_disk, |
|
.free_max_bests = xfs_dir2_free_max_bests, |
|
.free_bests_p = xfs_dir2_free_bests_p, |
|
.db_to_fdb = xfs_dir2_db_to_fdb, |
|
.db_to_fdindex = xfs_dir2_db_to_fdindex, |
|
}; |
|
|
|
static const struct xfs_dir_ops xfs_dir2_ftype_ops = { |
|
.sf_entsize = xfs_dir3_sf_entsize, |
|
.sf_nextentry = xfs_dir3_sf_nextentry, |
|
.sf_get_ftype = xfs_dir3_sfe_get_ftype, |
|
.sf_put_ftype = xfs_dir3_sfe_put_ftype, |
|
.sf_get_ino = xfs_dir3_sfe_get_ino, |
|
.sf_put_ino = xfs_dir3_sfe_put_ino, |
|
.sf_get_parent_ino = xfs_dir2_sf_get_parent_ino, |
|
.sf_put_parent_ino = xfs_dir2_sf_put_parent_ino, |
|
|
|
.data_entsize = xfs_dir3_data_entsize, |
|
.data_get_ftype = xfs_dir3_data_get_ftype, |
|
.data_put_ftype = xfs_dir3_data_put_ftype, |
|
.data_entry_tag_p = xfs_dir3_data_entry_tag_p, |
|
.data_bestfree_p = xfs_dir2_data_bestfree_p, |
|
|
|
.data_dot_offset = sizeof(struct xfs_dir2_data_hdr), |
|
.data_dotdot_offset = sizeof(struct xfs_dir2_data_hdr) + |
|
XFS_DIR3_DATA_ENTSIZE(1), |
|
.data_first_offset = sizeof(struct xfs_dir2_data_hdr) + |
|
XFS_DIR3_DATA_ENTSIZE(1) + |
|
XFS_DIR3_DATA_ENTSIZE(2), |
|
.data_entry_offset = sizeof(struct xfs_dir2_data_hdr), |
|
|
|
.data_dot_entry_p = xfs_dir2_data_dot_entry_p, |
|
.data_dotdot_entry_p = xfs_dir2_ftype_data_dotdot_entry_p, |
|
.data_first_entry_p = xfs_dir2_ftype_data_first_entry_p, |
|
.data_entry_p = xfs_dir2_data_entry_p, |
|
.data_unused_p = xfs_dir2_data_unused_p, |
|
|
|
.leaf_hdr_size = sizeof(struct xfs_dir2_leaf_hdr), |
|
.leaf_hdr_to_disk = xfs_dir2_leaf_hdr_to_disk, |
|
.leaf_hdr_from_disk = xfs_dir2_leaf_hdr_from_disk, |
|
.leaf_max_ents = xfs_dir2_max_leaf_ents, |
|
.leaf_ents_p = xfs_dir2_leaf_ents_p, |
|
|
|
.node_hdr_size = sizeof(struct xfs_da_node_hdr), |
|
.node_hdr_to_disk = xfs_da2_node_hdr_to_disk, |
|
.node_hdr_from_disk = xfs_da2_node_hdr_from_disk, |
|
.node_tree_p = xfs_da2_node_tree_p, |
|
|
|
.free_hdr_size = sizeof(struct xfs_dir2_free_hdr), |
|
.free_hdr_to_disk = xfs_dir2_free_hdr_to_disk, |
|
.free_hdr_from_disk = xfs_dir2_free_hdr_from_disk, |
|
.free_max_bests = xfs_dir2_free_max_bests, |
|
.free_bests_p = xfs_dir2_free_bests_p, |
|
.db_to_fdb = xfs_dir2_db_to_fdb, |
|
.db_to_fdindex = xfs_dir2_db_to_fdindex, |
|
}; |
|
|
|
static const struct xfs_dir_ops xfs_dir3_ops = { |
|
.sf_entsize = xfs_dir3_sf_entsize, |
|
.sf_nextentry = xfs_dir3_sf_nextentry, |
|
.sf_get_ftype = xfs_dir3_sfe_get_ftype, |
|
.sf_put_ftype = xfs_dir3_sfe_put_ftype, |
|
.sf_get_ino = xfs_dir3_sfe_get_ino, |
|
.sf_put_ino = xfs_dir3_sfe_put_ino, |
|
.sf_get_parent_ino = xfs_dir2_sf_get_parent_ino, |
|
.sf_put_parent_ino = xfs_dir2_sf_put_parent_ino, |
|
|
|
.data_entsize = xfs_dir3_data_entsize, |
|
.data_get_ftype = xfs_dir3_data_get_ftype, |
|
.data_put_ftype = xfs_dir3_data_put_ftype, |
|
.data_entry_tag_p = xfs_dir3_data_entry_tag_p, |
|
.data_bestfree_p = xfs_dir3_data_bestfree_p, |
|
|
|
.data_dot_offset = sizeof(struct xfs_dir3_data_hdr), |
|
.data_dotdot_offset = sizeof(struct xfs_dir3_data_hdr) + |
|
XFS_DIR3_DATA_ENTSIZE(1), |
|
.data_first_offset = sizeof(struct xfs_dir3_data_hdr) + |
|
XFS_DIR3_DATA_ENTSIZE(1) + |
|
XFS_DIR3_DATA_ENTSIZE(2), |
|
.data_entry_offset = sizeof(struct xfs_dir3_data_hdr), |
|
|
|
.data_dot_entry_p = xfs_dir3_data_dot_entry_p, |
|
.data_dotdot_entry_p = xfs_dir3_data_dotdot_entry_p, |
|
.data_first_entry_p = xfs_dir3_data_first_entry_p, |
|
.data_entry_p = xfs_dir3_data_entry_p, |
|
.data_unused_p = xfs_dir3_data_unused_p, |
|
|
|
.leaf_hdr_size = sizeof(struct xfs_dir3_leaf_hdr), |
|
.leaf_hdr_to_disk = xfs_dir3_leaf_hdr_to_disk, |
|
.leaf_hdr_from_disk = xfs_dir3_leaf_hdr_from_disk, |
|
.leaf_max_ents = xfs_dir3_max_leaf_ents, |
|
.leaf_ents_p = xfs_dir3_leaf_ents_p, |
|
|
|
.node_hdr_size = sizeof(struct xfs_da3_node_hdr), |
|
.node_hdr_to_disk = xfs_da3_node_hdr_to_disk, |
|
.node_hdr_from_disk = xfs_da3_node_hdr_from_disk, |
|
.node_tree_p = xfs_da3_node_tree_p, |
|
|
|
.free_hdr_size = sizeof(struct xfs_dir3_free_hdr), |
|
.free_hdr_to_disk = xfs_dir3_free_hdr_to_disk, |
|
.free_hdr_from_disk = xfs_dir3_free_hdr_from_disk, |
|
.free_max_bests = xfs_dir3_free_max_bests, |
|
.free_bests_p = xfs_dir3_free_bests_p, |
|
.db_to_fdb = xfs_dir3_db_to_fdb, |
|
.db_to_fdindex = xfs_dir3_db_to_fdindex, |
|
}; |
|
|
|
static const struct xfs_dir_ops xfs_dir2_nondir_ops = { |
|
.node_hdr_size = sizeof(struct xfs_da_node_hdr), |
|
.node_hdr_to_disk = xfs_da2_node_hdr_to_disk, |
|
.node_hdr_from_disk = xfs_da2_node_hdr_from_disk, |
|
.node_tree_p = xfs_da2_node_tree_p, |
|
}; |
|
|
|
static const struct xfs_dir_ops xfs_dir3_nondir_ops = { |
|
.node_hdr_size = sizeof(struct xfs_da3_node_hdr), |
|
.node_hdr_to_disk = xfs_da3_node_hdr_to_disk, |
|
.node_hdr_from_disk = xfs_da3_node_hdr_from_disk, |
|
.node_tree_p = xfs_da3_node_tree_p, |
|
}; |
|
|
|
/* |
|
* Return the ops structure according to the current config. If we are passed |
|
* an inode, then that overrides the default config we use which is based on |
|
* feature bits. |
|
*/ |
|
const struct xfs_dir_ops * |
|
xfs_dir_get_ops( |
|
struct xfs_mount *mp, |
|
struct xfs_inode *dp) |
|
{ |
|
if (dp) |
|
return dp->d_ops; |
|
if (mp->m_dir_inode_ops) |
|
return mp->m_dir_inode_ops; |
|
if (xfs_sb_version_hascrc(&mp->m_sb)) |
|
return &xfs_dir3_ops; |
|
if (xfs_sb_version_hasftype(&mp->m_sb)) |
|
return &xfs_dir2_ftype_ops; |
|
return &xfs_dir2_ops; |
|
} |
|
|
|
const struct xfs_dir_ops * |
|
xfs_nondir_get_ops( |
|
struct xfs_mount *mp, |
|
struct xfs_inode *dp) |
|
{ |
|
if (dp) |
|
return dp->d_ops; |
|
if (mp->m_nondir_inode_ops) |
|
return mp->m_nondir_inode_ops; |
|
if (xfs_sb_version_hascrc(&mp->m_sb)) |
|
return &xfs_dir3_nondir_ops; |
|
return &xfs_dir2_nondir_ops; |
|
}
|
|
|