📄 namei.c
字号:
/* * namei.c * * PURPOSE * Inode name handling routines for the OSTA-UDF(tm) filesystem. * * CONTACTS * E-mail regarding any portion of the Linux UDF file system should be * directed to the development team mailing list (run by majordomo): * linux_udf@hpesjro.fc.hp.com * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * * (C) 1998-2003 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY * * 12/12/98 blf Created. Split out the lookup code from dir.c * 04/19/99 blf link, mknod, symlink support */#include "udfdecl.h"#include "udf_i.h"#include "udf_sb.h"#include <linux/string.h>#include <linux/errno.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/quotaops.h>#include <linux/smp_lock.h>#include <linux/buffer_head.h>static inline int udf_match(int len1, const char *name1, int len2, const char *name2){ if (len1 != len2) return 0; return !memcmp(name1, name2, len1);}int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh, uint8_t *impuse, uint8_t *fileident){ uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(tag); uint16_t crc; uint8_t checksum = 0; int i; int offset; uint16_t liu = le16_to_cpu(cfi->lengthOfImpUse); uint8_t lfi = cfi->lengthFileIdent; int padlen = fibh->eoffset - fibh->soffset - liu - lfi - sizeof(struct fileIdentDesc); int adinicb = 0; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) adinicb = 1; offset = fibh->soffset + sizeof(struct fileIdentDesc); if (impuse) { if (adinicb || (offset + liu < 0)) memcpy((uint8_t *)sfi->impUse, impuse, liu); else if (offset >= 0) memcpy(fibh->ebh->b_data + offset, impuse, liu); else { memcpy((uint8_t *)sfi->impUse, impuse, -offset); memcpy(fibh->ebh->b_data, impuse - offset, liu + offset); } } offset += liu; if (fileident) { if (adinicb || (offset + lfi < 0)) memcpy((uint8_t *)sfi->fileIdent + liu, fileident, lfi); else if (offset >= 0) memcpy(fibh->ebh->b_data + offset, fileident, lfi); else { memcpy((uint8_t *)sfi->fileIdent + liu, fileident, -offset); memcpy(fibh->ebh->b_data, fileident - offset, lfi + offset); } } offset += lfi; if (adinicb || (offset + padlen < 0)) memset((uint8_t *)sfi->padding + liu + lfi, 0x00, padlen); else if (offset >= 0) memset(fibh->ebh->b_data + offset, 0x00, padlen); else { memset((uint8_t *)sfi->padding + liu + lfi, 0x00, -offset); memset(fibh->ebh->b_data, 0x00, padlen + offset); } crc = udf_crc((uint8_t *)cfi + sizeof(tag), sizeof(struct fileIdentDesc) - sizeof(tag), 0); if (fibh->sbh == fibh->ebh) crc = udf_crc((uint8_t *)sfi->impUse, crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc); else if (sizeof(struct fileIdentDesc) >= -fibh->soffset) crc = udf_crc(fibh->ebh->b_data + sizeof(struct fileIdentDesc) + fibh->soffset, crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc); else { crc = udf_crc((uint8_t *)sfi->impUse, -fibh->soffset - sizeof(struct fileIdentDesc), crc); crc = udf_crc(fibh->ebh->b_data, fibh->eoffset, crc); } cfi->descTag.descCRC = cpu_to_le32(crc); cfi->descTag.descCRCLength = cpu_to_le16(crclen); for (i=0; i<16; i++) if (i != 4) checksum += ((uint8_t *)&cfi->descTag)[i]; cfi->descTag.tagChecksum = checksum; if (adinicb || (sizeof(struct fileIdentDesc) <= -fibh->soffset)) memcpy((uint8_t *)sfi, (uint8_t *)cfi, sizeof(struct fileIdentDesc)); else { memcpy((uint8_t *)sfi, (uint8_t *)cfi, -fibh->soffset); memcpy(fibh->ebh->b_data, (uint8_t *)cfi - fibh->soffset, sizeof(struct fileIdentDesc) + fibh->soffset); } if (adinicb) mark_inode_dirty(inode); else { if (fibh->sbh != fibh->ebh) mark_buffer_dirty_inode(fibh->ebh, inode); mark_buffer_dirty_inode(fibh->sbh, inode); } return 0;}static struct fileIdentDesc *udf_find_entry(struct inode *dir, struct dentry *dentry, struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi){ struct fileIdentDesc *fi=NULL; loff_t f_pos; int block, namelen; char name[UDF_NAME_LEN], fname[UDF_NAME_LEN]; char *nameptr; uint8_t lfi; uint16_t liu; loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; lb_addr bloc, eloc; uint32_t extoffset, elen, offset; struct buffer_head *bh = NULL; if (!dir) return NULL; if ( !(namelen = udf_put_filename(dir->i_sb, dentry->d_name.name, name, dentry->d_name.len))) return NULL; f_pos = (udf_ext0_offset(dir) >> 2); fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) fibh->sbh = fibh->ebh = NULL; else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) { offset >>= dir->i_sb->s_blocksize_bits; block = udf_get_lb_pblock(dir->i_sb, eloc, offset); if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) extoffset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) extoffset -= sizeof(long_ad); } else offset = 0; if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) { udf_release_data(bh); return NULL; } } else { udf_release_data(bh); return NULL; } while ( (f_pos < size) ) { fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); if (!fi) { if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); udf_release_data(bh); return NULL; } liu = le16_to_cpu(cfi->lengthOfImpUse); lfi = cfi->lengthFileIdent; if (fibh->sbh == fibh->ebh) { nameptr = fi->fileIdent + liu; } else { int poffset; /* Unpaded ending offset */ poffset = fibh->soffset + sizeof(struct fileIdentDesc) + liu + lfi; if (poffset >= lfi) nameptr = (uint8_t *)(fibh->ebh->b_data + poffset - lfi); else { nameptr = fname; memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset); } } if ( (cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 ) { if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) ) continue; } if ( (cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 ) { if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) ) continue; } if (!lfi) continue; if (udf_match(namelen, name, lfi, nameptr)) { udf_release_data(bh); return fi; } } if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); udf_release_data(bh); return NULL;}/* * udf_lookup * * PURPOSE * Look-up the inode for a given name. * * DESCRIPTION * Required - lookup_dentry() will return -ENOTDIR if this routine is not * available for a directory. The filesystem is useless if this routine is * not available for at least the filesystem's root directory. * * This routine is passed an incomplete dentry - it must be completed by * calling d_add(dentry, inode). If the name does not exist, then the * specified inode must be set to null. An error should only be returned * when the lookup fails for a reason other than the name not existing. * Note that the directory inode semaphore is held during the call. * * Refer to lookup_dentry() in fs/namei.c * lookup_dentry() -> lookup() -> real_lookup() -> . * * PRE-CONDITIONS * dir Pointer to inode of parent directory. * dentry Pointer to dentry to complete. * nd Pointer to lookup nameidata * * POST-CONDITIONS * <return> Zero on success. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd){ struct inode *inode = NULL; struct fileIdentDesc cfi, *fi; struct udf_fileident_bh fibh; if (dentry->d_name.len > UDF_NAME_LEN-2) return ERR_PTR(-ENAMETOOLONG); lock_kernel();#ifdef UDF_RECOVERY /* temporary shorthand for specifying files by inode number */ if (!strncmp(dentry->d_name.name, ".B=", 3) ) { lb_addr lb = { 0, simple_strtoul(dentry->d_name.name+3, NULL, 0) }; inode = udf_iget(dir->i_sb, lb); if (!inode) { unlock_kernel(); return ERR_PTR(-EACCES); } } else#endif /* UDF_RECOVERY */ if ((fi = udf_find_entry(dir, dentry, &fibh, &cfi))) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); inode = udf_iget(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation)); if ( !inode ) { unlock_kernel(); return ERR_PTR(-EACCES); } } unlock_kernel(); d_add(dentry, inode); return NULL;}static struct fileIdentDesc *udf_add_entry(struct inode *dir, struct dentry *dentry, struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi, int *err){ struct super_block *sb; struct fileIdentDesc *fi=NULL; char name[UDF_NAME_LEN], fname[UDF_NAME_LEN]; int namelen; loff_t f_pos; char *nameptr; loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; int nfidlen; uint8_t lfi; uint16_t liu; int block; lb_addr bloc, eloc; uint32_t extoffset, elen, offset; struct buffer_head *bh = NULL; sb = dir->i_sb; if (dentry) { if (!dentry->d_name.len) { *err = -EINVAL; return NULL; } if ( !(namelen = udf_put_filename(sb, dentry->d_name.name, name, dentry->d_name.len))) { *err = -ENAMETOOLONG; return NULL; } } else namelen = 0; nfidlen = (sizeof(struct fileIdentDesc) + namelen + 3) & ~3; f_pos = (udf_ext0_offset(dir) >> 2); fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) fibh->sbh = fibh->ebh = NULL; else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) { offset >>= dir->i_sb->s_blocksize_bits; block = udf_get_lb_pblock(dir->i_sb, eloc, offset); if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) extoffset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) extoffset -= sizeof(long_ad); } else offset = 0; if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) { udf_release_data(bh); *err = -EIO; return NULL; } block = UDF_I_LOCATION(dir).logicalBlockNum; } else { block = udf_get_lb_pblock(dir->i_sb, UDF_I_LOCATION(dir), 0); fibh->sbh = fibh->ebh = NULL; fibh->soffset = fibh->eoffset = sb->s_blocksize; goto add; } while ( (f_pos < size) ) { fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); if (!fi) { if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); udf_release_data(bh); *err = -EIO; return NULL; } liu = le16_to_cpu(cfi->lengthOfImpUse); lfi = cfi->lengthFileIdent; if (fibh->sbh == fibh->ebh) nameptr = fi->fileIdent + liu; else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -