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.
341 lines
9.2 KiB
341 lines
9.2 KiB
/* |
|
* Copyright (c) 2000-2001 Christoph Hellwig. |
|
* Copyright (c) 2016 Krzysztof Blaszkowski |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions, and the following disclaimer, |
|
* without modification. |
|
* 2. The name of the author may not be used to endorse or promote products |
|
* derived from this software without specific prior written permission. |
|
* |
|
* Alternatively, this software may be distributed under the terms of the |
|
* GNU General Public License ("GPL"). |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR |
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
* SUCH DAMAGE. |
|
*/ |
|
|
|
/* |
|
* Veritas filesystem driver - inode routines. |
|
*/ |
|
#include <linux/fs.h> |
|
#include <linux/buffer_head.h> |
|
#include <linux/pagemap.h> |
|
#include <linux/kernel.h> |
|
#include <linux/slab.h> |
|
#include <linux/namei.h> |
|
|
|
#include "vxfs.h" |
|
#include "vxfs_inode.h" |
|
#include "vxfs_extern.h" |
|
|
|
|
|
#ifdef DIAGNOSTIC |
|
/* |
|
* Dump inode contents (partially). |
|
*/ |
|
void |
|
vxfs_dumpi(struct vxfs_inode_info *vip, ino_t ino) |
|
{ |
|
printk(KERN_DEBUG "\n\n"); |
|
if (ino) |
|
printk(KERN_DEBUG "dumping vxfs inode %ld\n", ino); |
|
else |
|
printk(KERN_DEBUG "dumping unknown vxfs inode\n"); |
|
|
|
printk(KERN_DEBUG "---------------------------\n"); |
|
printk(KERN_DEBUG "mode is %x\n", vip->vii_mode); |
|
printk(KERN_DEBUG "nlink:%u, uid:%u, gid:%u\n", |
|
vip->vii_nlink, vip->vii_uid, vip->vii_gid); |
|
printk(KERN_DEBUG "size:%Lx, blocks:%u\n", |
|
vip->vii_size, vip->vii_blocks); |
|
printk(KERN_DEBUG "orgtype:%u\n", vip->vii_orgtype); |
|
} |
|
#endif |
|
|
|
/** |
|
* vxfs_transmod - mode for a VxFS inode |
|
* @vip: VxFS inode |
|
* |
|
* Description: |
|
* vxfs_transmod returns a Linux mode_t for a given |
|
* VxFS inode structure. |
|
*/ |
|
static __inline__ umode_t |
|
vxfs_transmod(struct vxfs_inode_info *vip) |
|
{ |
|
umode_t ret = vip->vii_mode & ~VXFS_TYPE_MASK; |
|
|
|
if (VXFS_ISFIFO(vip)) |
|
ret |= S_IFIFO; |
|
if (VXFS_ISCHR(vip)) |
|
ret |= S_IFCHR; |
|
if (VXFS_ISDIR(vip)) |
|
ret |= S_IFDIR; |
|
if (VXFS_ISBLK(vip)) |
|
ret |= S_IFBLK; |
|
if (VXFS_ISLNK(vip)) |
|
ret |= S_IFLNK; |
|
if (VXFS_ISREG(vip)) |
|
ret |= S_IFREG; |
|
if (VXFS_ISSOC(vip)) |
|
ret |= S_IFSOCK; |
|
|
|
return (ret); |
|
} |
|
|
|
static inline void dip2vip_cpy(struct vxfs_sb_info *sbi, |
|
struct vxfs_inode_info *vip, struct vxfs_dinode *dip) |
|
{ |
|
struct inode *inode = &vip->vfs_inode; |
|
|
|
vip->vii_mode = fs32_to_cpu(sbi, dip->vdi_mode); |
|
vip->vii_nlink = fs32_to_cpu(sbi, dip->vdi_nlink); |
|
vip->vii_uid = fs32_to_cpu(sbi, dip->vdi_uid); |
|
vip->vii_gid = fs32_to_cpu(sbi, dip->vdi_gid); |
|
vip->vii_size = fs64_to_cpu(sbi, dip->vdi_size); |
|
vip->vii_atime = fs32_to_cpu(sbi, dip->vdi_atime); |
|
vip->vii_autime = fs32_to_cpu(sbi, dip->vdi_autime); |
|
vip->vii_mtime = fs32_to_cpu(sbi, dip->vdi_mtime); |
|
vip->vii_mutime = fs32_to_cpu(sbi, dip->vdi_mutime); |
|
vip->vii_ctime = fs32_to_cpu(sbi, dip->vdi_ctime); |
|
vip->vii_cutime = fs32_to_cpu(sbi, dip->vdi_cutime); |
|
vip->vii_orgtype = dip->vdi_orgtype; |
|
|
|
vip->vii_blocks = fs32_to_cpu(sbi, dip->vdi_blocks); |
|
vip->vii_gen = fs32_to_cpu(sbi, dip->vdi_gen); |
|
|
|
if (VXFS_ISDIR(vip)) |
|
vip->vii_dotdot = fs32_to_cpu(sbi, dip->vdi_dotdot); |
|
else if (!VXFS_ISREG(vip) && !VXFS_ISLNK(vip)) |
|
vip->vii_rdev = fs32_to_cpu(sbi, dip->vdi_rdev); |
|
|
|
/* don't endian swap the fields that differ by orgtype */ |
|
memcpy(&vip->vii_org, &dip->vdi_org, sizeof(vip->vii_org)); |
|
|
|
inode->i_mode = vxfs_transmod(vip); |
|
i_uid_write(inode, (uid_t)vip->vii_uid); |
|
i_gid_write(inode, (gid_t)vip->vii_gid); |
|
|
|
set_nlink(inode, vip->vii_nlink); |
|
inode->i_size = vip->vii_size; |
|
|
|
inode->i_atime.tv_sec = vip->vii_atime; |
|
inode->i_ctime.tv_sec = vip->vii_ctime; |
|
inode->i_mtime.tv_sec = vip->vii_mtime; |
|
inode->i_atime.tv_nsec = 0; |
|
inode->i_ctime.tv_nsec = 0; |
|
inode->i_mtime.tv_nsec = 0; |
|
|
|
inode->i_blocks = vip->vii_blocks; |
|
inode->i_generation = vip->vii_gen; |
|
} |
|
|
|
/** |
|
* vxfs_blkiget - find inode based on extent # |
|
* @sbp: superblock of the filesystem we search in |
|
* @extent: number of the extent to search |
|
* @ino: inode number to search |
|
* |
|
* Description: |
|
* vxfs_blkiget searches inode @ino in the filesystem described by |
|
* @sbp in the extent @extent. |
|
* Returns the matching VxFS inode on success, else a NULL pointer. |
|
* |
|
* NOTE: |
|
* While __vxfs_iget uses the pagecache vxfs_blkiget uses the |
|
* buffercache. This function should not be used outside the |
|
* read_super() method, otherwise the data may be incoherent. |
|
*/ |
|
struct inode * |
|
vxfs_blkiget(struct super_block *sbp, u_long extent, ino_t ino) |
|
{ |
|
struct buffer_head *bp; |
|
struct inode *inode; |
|
u_long block, offset; |
|
|
|
inode = new_inode(sbp); |
|
if (!inode) |
|
return NULL; |
|
inode->i_ino = get_next_ino(); |
|
|
|
block = extent + ((ino * VXFS_ISIZE) / sbp->s_blocksize); |
|
offset = ((ino % (sbp->s_blocksize / VXFS_ISIZE)) * VXFS_ISIZE); |
|
bp = sb_bread(sbp, block); |
|
|
|
if (bp && buffer_mapped(bp)) { |
|
struct vxfs_inode_info *vip = VXFS_INO(inode); |
|
struct vxfs_dinode *dip; |
|
|
|
dip = (struct vxfs_dinode *)(bp->b_data + offset); |
|
dip2vip_cpy(VXFS_SBI(sbp), vip, dip); |
|
vip->vfs_inode.i_mapping->a_ops = &vxfs_aops; |
|
#ifdef DIAGNOSTIC |
|
vxfs_dumpi(vip, ino); |
|
#endif |
|
brelse(bp); |
|
return inode; |
|
} |
|
|
|
printk(KERN_WARNING "vxfs: unable to read block %ld\n", block); |
|
brelse(bp); |
|
iput(inode); |
|
return NULL; |
|
} |
|
|
|
/** |
|
* __vxfs_iget - generic find inode facility |
|
* @ilistp: inode list |
|
* @vip: VxFS inode to fill in |
|
* @ino: inode number |
|
* |
|
* Description: |
|
* Search the for inode number @ino in the filesystem |
|
* described by @sbp. Use the specified inode table (@ilistp). |
|
* Returns the matching inode on success, else an error code. |
|
*/ |
|
static int |
|
__vxfs_iget(struct inode *ilistp, struct vxfs_inode_info *vip, ino_t ino) |
|
{ |
|
struct page *pp; |
|
u_long offset; |
|
|
|
offset = (ino % (PAGE_SIZE / VXFS_ISIZE)) * VXFS_ISIZE; |
|
pp = vxfs_get_page(ilistp->i_mapping, ino * VXFS_ISIZE / PAGE_SIZE); |
|
|
|
if (!IS_ERR(pp)) { |
|
struct vxfs_dinode *dip; |
|
caddr_t kaddr = (char *)page_address(pp); |
|
|
|
dip = (struct vxfs_dinode *)(kaddr + offset); |
|
dip2vip_cpy(VXFS_SBI(ilistp->i_sb), vip, dip); |
|
vip->vfs_inode.i_mapping->a_ops = &vxfs_aops; |
|
#ifdef DIAGNOSTIC |
|
vxfs_dumpi(vip, ino); |
|
#endif |
|
vxfs_put_page(pp); |
|
return 0; |
|
} |
|
|
|
printk(KERN_WARNING "vxfs: error on page 0x%p for inode %ld\n", |
|
pp, (unsigned long)ino); |
|
return PTR_ERR(pp); |
|
} |
|
|
|
/** |
|
* vxfs_stiget - find inode using the structural inode list |
|
* @sbp: VFS superblock |
|
* @ino: inode # |
|
* |
|
* Description: |
|
* Find inode @ino in the filesystem described by @sbp using |
|
* the structural inode list. |
|
* Returns the matching inode on success, else a NULL pointer. |
|
*/ |
|
struct inode * |
|
vxfs_stiget(struct super_block *sbp, ino_t ino) |
|
{ |
|
struct inode *inode; |
|
int error; |
|
|
|
inode = new_inode(sbp); |
|
if (!inode) |
|
return NULL; |
|
inode->i_ino = get_next_ino(); |
|
|
|
error = __vxfs_iget(VXFS_SBI(sbp)->vsi_stilist, VXFS_INO(inode), ino); |
|
if (error) { |
|
iput(inode); |
|
return NULL; |
|
} |
|
|
|
return inode; |
|
} |
|
|
|
/** |
|
* vxfs_iget - get an inode |
|
* @sbp: the superblock to get the inode for |
|
* @ino: the number of the inode to get |
|
* |
|
* Description: |
|
* vxfs_read_inode creates an inode, reads the disk inode for @ino and fills |
|
* in all relevant fields in the new inode. |
|
*/ |
|
struct inode * |
|
vxfs_iget(struct super_block *sbp, ino_t ino) |
|
{ |
|
struct vxfs_inode_info *vip; |
|
const struct address_space_operations *aops; |
|
struct inode *ip; |
|
int error; |
|
|
|
ip = iget_locked(sbp, ino); |
|
if (!ip) |
|
return ERR_PTR(-ENOMEM); |
|
if (!(ip->i_state & I_NEW)) |
|
return ip; |
|
|
|
vip = VXFS_INO(ip); |
|
error = __vxfs_iget(VXFS_SBI(sbp)->vsi_ilist, vip, ino); |
|
if (error) { |
|
iget_failed(ip); |
|
return ERR_PTR(error); |
|
} |
|
|
|
if (VXFS_ISIMMED(vip)) |
|
aops = &vxfs_immed_aops; |
|
else |
|
aops = &vxfs_aops; |
|
|
|
if (S_ISREG(ip->i_mode)) { |
|
ip->i_fop = &generic_ro_fops; |
|
ip->i_mapping->a_ops = aops; |
|
} else if (S_ISDIR(ip->i_mode)) { |
|
ip->i_op = &vxfs_dir_inode_ops; |
|
ip->i_fop = &vxfs_dir_operations; |
|
ip->i_mapping->a_ops = aops; |
|
} else if (S_ISLNK(ip->i_mode)) { |
|
if (!VXFS_ISIMMED(vip)) { |
|
ip->i_op = &page_symlink_inode_operations; |
|
inode_nohighmem(ip); |
|
ip->i_mapping->a_ops = &vxfs_aops; |
|
} else { |
|
ip->i_op = &simple_symlink_inode_operations; |
|
ip->i_link = vip->vii_immed.vi_immed; |
|
nd_terminate_link(ip->i_link, ip->i_size, |
|
sizeof(vip->vii_immed.vi_immed) - 1); |
|
} |
|
} else |
|
init_special_inode(ip, ip->i_mode, old_decode_dev(vip->vii_rdev)); |
|
|
|
unlock_new_inode(ip); |
|
return ip; |
|
} |
|
|
|
/** |
|
* vxfs_evict_inode - remove inode from main memory |
|
* @ip: inode to discard. |
|
* |
|
* Description: |
|
* vxfs_evict_inode() is called on the final iput and frees the private |
|
* inode area. |
|
*/ |
|
void |
|
vxfs_evict_inode(struct inode *ip) |
|
{ |
|
truncate_inode_pages_final(&ip->i_data); |
|
clear_inode(ip); |
|
}
|
|
|