/*
* linux/fs/hpfs/dnode.c
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* handling directory dnode tree - adding, deleteing & searching for dirents
*/
#include "hpfs_fn.h"
static loff_t get_pos(struct dnode *d, struct hpfs_dirent *fde)
{
struct hpfs_dirent *de;
struct hpfs_dirent *de_end = dnode_end_de(d);
int i = 1;
for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
if (de == fde) return ((loff_t) le32_to_cpu(d->self) << 4) | (loff_t)i;
i++;
}
printk("HPFS: get_pos: not_found\n");
return ((loff_t)le32_to_cpu(d->self) << 4) | (loff_t)1;
}
void hpfs_add_pos(struct inode *inode, loff_t *pos)
{
struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
int i = 0;
loff_t **ppos;
if (hpfs_inode->i_rddir_off)
for (; hpfs_inode->i_rddir_off[i]; i++)
if (hpfs_inode->i_rddir_off[i] == pos) return;
if (!(i&0x0f)) {
if (!(ppos = kmalloc((i+0x11) * sizeof(loff_t*), GFP_NOFS))) {
printk("HPFS: out of memory for position list\n");
return;
}
if (hpfs_inode->i_rddir_off) {
memcpy(ppos, hpfs_inode->i_rddir_off, i * sizeof(loff_t));
kfree(hpfs_inode->i_rddir_off);
}
hpfs_inode->i_rddir_off = ppos;
}
hpfs_inode->i_rddir_off[i] = pos;
hpfs_inode->i_rddir_off[i + 1] = NULL;
}
void hpfs_del_pos(struct inode *inode, loff_t *pos)
{
struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
loff_t **i, **j;
if (!hpfs_inode->i_rddir_off) goto not_f;
for (i = hpfs_inode->i_rddir_off; *i; i++) if (*i == pos) goto fnd;
goto not_f;
fnd:
for (j = i + 1; *j; j++) ;
*i = *(j - 1);
*(j - 1) = NULL;
if (j - 1 == hpfs_inode->i_rddir_off) {
kfree(hpfs_inode->i_rddir_off);
hpfs_inode->i_rddir_off = NULL;
}
return;
not_f:
/*printk("HPFS: warning: position pointer %p->%08x not found\n", pos, (int)*pos);*/
return;
}
static void for_all_poss(struct inode *inode, void (*f)(loff_t *, loff_t, loff_t),
loff_t p1, loff_t p2)
{
struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
loff_t **i;
if (!hpfs_inode->i_rddir_off) return;
for (i = hpfs_inode->i_rddir_off; *i; i++) (*f)(*i, p1, p2);
return;
}
static void hpfs_pos_subst(loff_t *p, loff_t f, loff_t t)
{
if (*p == f) *p = t;
}
/*void hpfs_hpfs_pos_substd(loff_t *p, loff_t f, loff_t t)
{
if ((*p & ~0x3f) == (f & ~0x3f)) *p = (t & ~0x3f) | (*p & 0x3f);
}*/
static void hpfs_pos_ins(loff_t *p, loff_t d, loff_t c)
{
if ((*p & ~0x3f) == (d & ~0x3f) && (*p & 0x3f) >= (d & 0x3f)) {
int n = (*p & 0x3f) + c;
if (n > 0x3f) printk("HPFS: hpfs_pos_ins: %08x + %d\n", (int)*p, (int)c >> 8);
else *p = (*p & ~0x3f) | n;
}
}
static void hpfs_pos_del(loff_t *p, loff_t d, loff_t c)
{
if ((*p & ~0x3f) == (d & ~0x3f) && (*p & 0x3f) >= (d & 0x3f)) {
int n = (*p & 0x3f) - c;
if (n < 1) printk("HPFS: hpfs_pos_ins: %08x - %d\n", (int)*p, (int)c >> 8);
else *p = (*p & ~0x3f) | n;
}
}
static struct hpfs_dirent *dnode_pre_last_de(struct dnode *d)
{
struct hpfs_dirent *de, *de_end, *dee = NULL, *deee = NULL;
de_end = dnode_end_de(d);
for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
deee = dee; dee = de;
}
return deee;
}
static struct hpfs_dirent *dnode_last_de(struct dnode *d)
{
struct hpfs_dirent *de, *de_end, *dee = NULL;
de_end = dnode_end_de(d);
for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
dee = de;
}
return dee;
}
static void set_last_pointer(struct super_block *s, struct dnode *d, dnode_secno ptr)
{
struct hpfs_dirent *de;
if (!(de = dnode_last_de(d))) {
hpfs_error(s, "set_last_pointer: empty dnode %08x", le32_to_cpu(d->self));
return;
}
if (hpfs_sb(s)->sb_chk) {
if (de->down) {
hpfs_error(s, "set_last_pointer: dnode %08x has already last pointer %08x",
le32_to_cpu(d->self), de_down_pointer(de));
return;
}
if (le16_to_cpu(de->length) != 32) {
hpfs_error(s, "set_last_pointer: bad last dirent in dnode %08x", le32_to_cpu(d->self));
return;
}
}
if (ptr) {
d->first_free = cpu_to_le32(le32_to_cpu(d->first_free) + 4);
if (le32_to_cpu(d->first_free) > 2048) {
hpfs_error(s, "set_last_pointer: too long dnode %08x", le32_to_cpu(d->self));
d->first_free = cpu_to_le32(le32_to_cpu(d->first_free) - 4);
return;
}
de->length = cpu_to_le16(36);
de->down = 1;
*(__le32 *)((char *)de + 32) = cpu_to_le32(ptr);
}
}
/* Add an entry to dnode and don't care if it grows over 2048 bytes */
struct hpfs_dirent *hpfs_add_de(struct super_block *s, struct dnode *d,
const unsigned char *name,
unsigned namelen, secno down_ptr)
{
struct hpfs_dirent *de;
struct hpfs_dirent *de_end = dnode_end_de
|