162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * directory.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * PURPOSE 662306a36Sopenharmony_ci * Directory related functions 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "udfdecl.h" 1162306a36Sopenharmony_ci#include "udf_i.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/bio.h> 1662306a36Sopenharmony_ci#include <linux/crc-itu-t.h> 1762306a36Sopenharmony_ci#include <linux/iversion.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic int udf_verify_fi(struct udf_fileident_iter *iter) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci unsigned int len; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci if (iter->fi.descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) { 2462306a36Sopenharmony_ci udf_err(iter->dir->i_sb, 2562306a36Sopenharmony_ci "directory (ino %lu) has entry at pos %llu with incorrect tag %x\n", 2662306a36Sopenharmony_ci iter->dir->i_ino, (unsigned long long)iter->pos, 2762306a36Sopenharmony_ci le16_to_cpu(iter->fi.descTag.tagIdent)); 2862306a36Sopenharmony_ci return -EFSCORRUPTED; 2962306a36Sopenharmony_ci } 3062306a36Sopenharmony_ci len = udf_dir_entry_len(&iter->fi); 3162306a36Sopenharmony_ci if (le16_to_cpu(iter->fi.lengthOfImpUse) & 3) { 3262306a36Sopenharmony_ci udf_err(iter->dir->i_sb, 3362306a36Sopenharmony_ci "directory (ino %lu) has entry at pos %llu with unaligned length of impUse field\n", 3462306a36Sopenharmony_ci iter->dir->i_ino, (unsigned long long)iter->pos); 3562306a36Sopenharmony_ci return -EFSCORRUPTED; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci /* 3862306a36Sopenharmony_ci * This is in fact allowed by the spec due to long impUse field but 3962306a36Sopenharmony_ci * we don't support it. If there is real media with this large impUse 4062306a36Sopenharmony_ci * field, support can be added. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci if (len > 1 << iter->dir->i_blkbits) { 4362306a36Sopenharmony_ci udf_err(iter->dir->i_sb, 4462306a36Sopenharmony_ci "directory (ino %lu) has too big (%u) entry at pos %llu\n", 4562306a36Sopenharmony_ci iter->dir->i_ino, len, (unsigned long long)iter->pos); 4662306a36Sopenharmony_ci return -EFSCORRUPTED; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci if (iter->pos + len > iter->dir->i_size) { 4962306a36Sopenharmony_ci udf_err(iter->dir->i_sb, 5062306a36Sopenharmony_ci "directory (ino %lu) has entry past directory size at pos %llu\n", 5162306a36Sopenharmony_ci iter->dir->i_ino, (unsigned long long)iter->pos); 5262306a36Sopenharmony_ci return -EFSCORRUPTED; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci if (udf_dir_entry_len(&iter->fi) != 5562306a36Sopenharmony_ci sizeof(struct tag) + le16_to_cpu(iter->fi.descTag.descCRCLength)) { 5662306a36Sopenharmony_ci udf_err(iter->dir->i_sb, 5762306a36Sopenharmony_ci "directory (ino %lu) has entry where CRC length (%u) does not match entry length (%u)\n", 5862306a36Sopenharmony_ci iter->dir->i_ino, 5962306a36Sopenharmony_ci (unsigned)le16_to_cpu(iter->fi.descTag.descCRCLength), 6062306a36Sopenharmony_ci (unsigned)(udf_dir_entry_len(&iter->fi) - 6162306a36Sopenharmony_ci sizeof(struct tag))); 6262306a36Sopenharmony_ci return -EFSCORRUPTED; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci return 0; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int udf_copy_fi(struct udf_fileident_iter *iter) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct udf_inode_info *iinfo = UDF_I(iter->dir); 7062306a36Sopenharmony_ci u32 blksize = 1 << iter->dir->i_blkbits; 7162306a36Sopenharmony_ci u32 off, len, nameoff; 7262306a36Sopenharmony_ci int err; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Skip copying when we are at EOF */ 7562306a36Sopenharmony_ci if (iter->pos >= iter->dir->i_size) { 7662306a36Sopenharmony_ci iter->name = NULL; 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci if (iter->dir->i_size < iter->pos + sizeof(struct fileIdentDesc)) { 8062306a36Sopenharmony_ci udf_err(iter->dir->i_sb, 8162306a36Sopenharmony_ci "directory (ino %lu) has entry straddling EOF\n", 8262306a36Sopenharmony_ci iter->dir->i_ino); 8362306a36Sopenharmony_ci return -EFSCORRUPTED; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { 8662306a36Sopenharmony_ci memcpy(&iter->fi, iinfo->i_data + iinfo->i_lenEAttr + iter->pos, 8762306a36Sopenharmony_ci sizeof(struct fileIdentDesc)); 8862306a36Sopenharmony_ci err = udf_verify_fi(iter); 8962306a36Sopenharmony_ci if (err < 0) 9062306a36Sopenharmony_ci return err; 9162306a36Sopenharmony_ci iter->name = iinfo->i_data + iinfo->i_lenEAttr + iter->pos + 9262306a36Sopenharmony_ci sizeof(struct fileIdentDesc) + 9362306a36Sopenharmony_ci le16_to_cpu(iter->fi.lengthOfImpUse); 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci off = iter->pos & (blksize - 1); 9862306a36Sopenharmony_ci len = min_t(u32, sizeof(struct fileIdentDesc), blksize - off); 9962306a36Sopenharmony_ci memcpy(&iter->fi, iter->bh[0]->b_data + off, len); 10062306a36Sopenharmony_ci if (len < sizeof(struct fileIdentDesc)) 10162306a36Sopenharmony_ci memcpy((char *)(&iter->fi) + len, iter->bh[1]->b_data, 10262306a36Sopenharmony_ci sizeof(struct fileIdentDesc) - len); 10362306a36Sopenharmony_ci err = udf_verify_fi(iter); 10462306a36Sopenharmony_ci if (err < 0) 10562306a36Sopenharmony_ci return err; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Handle directory entry name */ 10862306a36Sopenharmony_ci nameoff = off + sizeof(struct fileIdentDesc) + 10962306a36Sopenharmony_ci le16_to_cpu(iter->fi.lengthOfImpUse); 11062306a36Sopenharmony_ci if (off + udf_dir_entry_len(&iter->fi) <= blksize) { 11162306a36Sopenharmony_ci iter->name = iter->bh[0]->b_data + nameoff; 11262306a36Sopenharmony_ci } else if (nameoff >= blksize) { 11362306a36Sopenharmony_ci iter->name = iter->bh[1]->b_data + (nameoff - blksize); 11462306a36Sopenharmony_ci } else { 11562306a36Sopenharmony_ci iter->name = iter->namebuf; 11662306a36Sopenharmony_ci len = blksize - nameoff; 11762306a36Sopenharmony_ci memcpy(iter->name, iter->bh[0]->b_data + nameoff, len); 11862306a36Sopenharmony_ci memcpy(iter->name + len, iter->bh[1]->b_data, 11962306a36Sopenharmony_ci iter->fi.lengthFileIdent - len); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* Readahead 8k once we are at 8k boundary */ 12562306a36Sopenharmony_cistatic void udf_readahead_dir(struct udf_fileident_iter *iter) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci unsigned int ralen = 16 >> (iter->dir->i_blkbits - 9); 12862306a36Sopenharmony_ci struct buffer_head *tmp, *bha[16]; 12962306a36Sopenharmony_ci int i, num; 13062306a36Sopenharmony_ci udf_pblk_t blk; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (iter->loffset & (ralen - 1)) 13362306a36Sopenharmony_ci return; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (iter->loffset + ralen > (iter->elen >> iter->dir->i_blkbits)) 13662306a36Sopenharmony_ci ralen = (iter->elen >> iter->dir->i_blkbits) - iter->loffset; 13762306a36Sopenharmony_ci num = 0; 13862306a36Sopenharmony_ci for (i = 0; i < ralen; i++) { 13962306a36Sopenharmony_ci blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc, 14062306a36Sopenharmony_ci iter->loffset + i); 14162306a36Sopenharmony_ci tmp = sb_getblk(iter->dir->i_sb, blk); 14262306a36Sopenharmony_ci if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) 14362306a36Sopenharmony_ci bha[num++] = tmp; 14462306a36Sopenharmony_ci else 14562306a36Sopenharmony_ci brelse(tmp); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci if (num) { 14862306a36Sopenharmony_ci bh_readahead_batch(num, bha, REQ_RAHEAD); 14962306a36Sopenharmony_ci for (i = 0; i < num; i++) 15062306a36Sopenharmony_ci brelse(bha[i]); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic struct buffer_head *udf_fiiter_bread_blk(struct udf_fileident_iter *iter) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci udf_pblk_t blk; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci udf_readahead_dir(iter); 15962306a36Sopenharmony_ci blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc, iter->loffset); 16062306a36Sopenharmony_ci return sb_bread(iter->dir->i_sb, blk); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * Updates loffset to point to next directory block; eloc, elen & epos are 16562306a36Sopenharmony_ci * updated if we need to traverse to the next extent as well. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_cistatic int udf_fiiter_advance_blk(struct udf_fileident_iter *iter) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci iter->loffset++; 17062306a36Sopenharmony_ci if (iter->loffset < DIV_ROUND_UP(iter->elen, 1<<iter->dir->i_blkbits)) 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci iter->loffset = 0; 17462306a36Sopenharmony_ci if (udf_next_aext(iter->dir, &iter->epos, &iter->eloc, &iter->elen, 1) 17562306a36Sopenharmony_ci != (EXT_RECORDED_ALLOCATED >> 30)) { 17662306a36Sopenharmony_ci if (iter->pos == iter->dir->i_size) { 17762306a36Sopenharmony_ci iter->elen = 0; 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci udf_err(iter->dir->i_sb, 18162306a36Sopenharmony_ci "extent after position %llu not allocated in directory (ino %lu)\n", 18262306a36Sopenharmony_ci (unsigned long long)iter->pos, iter->dir->i_ino); 18362306a36Sopenharmony_ci return -EFSCORRUPTED; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int udf_fiiter_load_bhs(struct udf_fileident_iter *iter) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci int blksize = 1 << iter->dir->i_blkbits; 19162306a36Sopenharmony_ci int off = iter->pos & (blksize - 1); 19262306a36Sopenharmony_ci int err; 19362306a36Sopenharmony_ci struct fileIdentDesc *fi; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Is there any further extent we can map from? */ 19662306a36Sopenharmony_ci if (!iter->bh[0] && iter->elen) { 19762306a36Sopenharmony_ci iter->bh[0] = udf_fiiter_bread_blk(iter); 19862306a36Sopenharmony_ci if (!iter->bh[0]) { 19962306a36Sopenharmony_ci err = -ENOMEM; 20062306a36Sopenharmony_ci goto out_brelse; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci if (!buffer_uptodate(iter->bh[0])) { 20362306a36Sopenharmony_ci err = -EIO; 20462306a36Sopenharmony_ci goto out_brelse; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci /* There's no next block so we are done */ 20862306a36Sopenharmony_ci if (iter->pos >= iter->dir->i_size) 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci /* Need to fetch next block as well? */ 21162306a36Sopenharmony_ci if (off + sizeof(struct fileIdentDesc) > blksize) 21262306a36Sopenharmony_ci goto fetch_next; 21362306a36Sopenharmony_ci fi = (struct fileIdentDesc *)(iter->bh[0]->b_data + off); 21462306a36Sopenharmony_ci /* Need to fetch next block to get name? */ 21562306a36Sopenharmony_ci if (off + udf_dir_entry_len(fi) > blksize) { 21662306a36Sopenharmony_cifetch_next: 21762306a36Sopenharmony_ci err = udf_fiiter_advance_blk(iter); 21862306a36Sopenharmony_ci if (err) 21962306a36Sopenharmony_ci goto out_brelse; 22062306a36Sopenharmony_ci iter->bh[1] = udf_fiiter_bread_blk(iter); 22162306a36Sopenharmony_ci if (!iter->bh[1]) { 22262306a36Sopenharmony_ci err = -ENOMEM; 22362306a36Sopenharmony_ci goto out_brelse; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci if (!buffer_uptodate(iter->bh[1])) { 22662306a36Sopenharmony_ci err = -EIO; 22762306a36Sopenharmony_ci goto out_brelse; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ciout_brelse: 23262306a36Sopenharmony_ci brelse(iter->bh[0]); 23362306a36Sopenharmony_ci brelse(iter->bh[1]); 23462306a36Sopenharmony_ci iter->bh[0] = iter->bh[1] = NULL; 23562306a36Sopenharmony_ci return err; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ciint udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir, 23962306a36Sopenharmony_ci loff_t pos) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct udf_inode_info *iinfo = UDF_I(dir); 24262306a36Sopenharmony_ci int err = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci iter->dir = dir; 24562306a36Sopenharmony_ci iter->bh[0] = iter->bh[1] = NULL; 24662306a36Sopenharmony_ci iter->pos = pos; 24762306a36Sopenharmony_ci iter->elen = 0; 24862306a36Sopenharmony_ci iter->epos.bh = NULL; 24962306a36Sopenharmony_ci iter->name = NULL; 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * When directory is verified, we don't expect directory iteration to 25262306a36Sopenharmony_ci * fail and it can be difficult to undo without corrupting filesystem. 25362306a36Sopenharmony_ci * So just do not allow memory allocation failures here. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci iter->namebuf = kmalloc(UDF_NAME_LEN_CS0, GFP_KERNEL | __GFP_NOFAIL); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { 25862306a36Sopenharmony_ci err = udf_copy_fi(iter); 25962306a36Sopenharmony_ci goto out; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos, 26362306a36Sopenharmony_ci &iter->eloc, &iter->elen, &iter->loffset) != 26462306a36Sopenharmony_ci (EXT_RECORDED_ALLOCATED >> 30)) { 26562306a36Sopenharmony_ci if (pos == dir->i_size) 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci udf_err(dir->i_sb, 26862306a36Sopenharmony_ci "position %llu not allocated in directory (ino %lu)\n", 26962306a36Sopenharmony_ci (unsigned long long)pos, dir->i_ino); 27062306a36Sopenharmony_ci err = -EFSCORRUPTED; 27162306a36Sopenharmony_ci goto out; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci err = udf_fiiter_load_bhs(iter); 27462306a36Sopenharmony_ci if (err < 0) 27562306a36Sopenharmony_ci goto out; 27662306a36Sopenharmony_ci err = udf_copy_fi(iter); 27762306a36Sopenharmony_ciout: 27862306a36Sopenharmony_ci if (err < 0) 27962306a36Sopenharmony_ci udf_fiiter_release(iter); 28062306a36Sopenharmony_ci return err; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ciint udf_fiiter_advance(struct udf_fileident_iter *iter) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci unsigned int oldoff, len; 28662306a36Sopenharmony_ci int blksize = 1 << iter->dir->i_blkbits; 28762306a36Sopenharmony_ci int err; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci oldoff = iter->pos & (blksize - 1); 29062306a36Sopenharmony_ci len = udf_dir_entry_len(&iter->fi); 29162306a36Sopenharmony_ci iter->pos += len; 29262306a36Sopenharmony_ci if (UDF_I(iter->dir)->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { 29362306a36Sopenharmony_ci if (oldoff + len >= blksize) { 29462306a36Sopenharmony_ci brelse(iter->bh[0]); 29562306a36Sopenharmony_ci iter->bh[0] = NULL; 29662306a36Sopenharmony_ci /* Next block already loaded? */ 29762306a36Sopenharmony_ci if (iter->bh[1]) { 29862306a36Sopenharmony_ci iter->bh[0] = iter->bh[1]; 29962306a36Sopenharmony_ci iter->bh[1] = NULL; 30062306a36Sopenharmony_ci } else { 30162306a36Sopenharmony_ci err = udf_fiiter_advance_blk(iter); 30262306a36Sopenharmony_ci if (err < 0) 30362306a36Sopenharmony_ci return err; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci err = udf_fiiter_load_bhs(iter); 30762306a36Sopenharmony_ci if (err < 0) 30862306a36Sopenharmony_ci return err; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci return udf_copy_fi(iter); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_civoid udf_fiiter_release(struct udf_fileident_iter *iter) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci iter->dir = NULL; 31662306a36Sopenharmony_ci brelse(iter->bh[0]); 31762306a36Sopenharmony_ci brelse(iter->bh[1]); 31862306a36Sopenharmony_ci iter->bh[0] = iter->bh[1] = NULL; 31962306a36Sopenharmony_ci kfree(iter->namebuf); 32062306a36Sopenharmony_ci iter->namebuf = NULL; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic void udf_copy_to_bufs(void *buf1, int len1, void *buf2, int len2, 32462306a36Sopenharmony_ci int off, void *src, int len) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci int copy; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (off >= len1) { 32962306a36Sopenharmony_ci off -= len1; 33062306a36Sopenharmony_ci } else { 33162306a36Sopenharmony_ci copy = min(off + len, len1) - off; 33262306a36Sopenharmony_ci memcpy(buf1 + off, src, copy); 33362306a36Sopenharmony_ci src += copy; 33462306a36Sopenharmony_ci len -= copy; 33562306a36Sopenharmony_ci off = 0; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci if (len > 0) { 33862306a36Sopenharmony_ci if (WARN_ON_ONCE(off + len > len2 || !buf2)) 33962306a36Sopenharmony_ci return; 34062306a36Sopenharmony_ci memcpy(buf2 + off, src, len); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic uint16_t udf_crc_fi_bufs(void *buf1, int len1, void *buf2, int len2, 34562306a36Sopenharmony_ci int off, int len) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci int copy; 34862306a36Sopenharmony_ci uint16_t crc = 0; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (off >= len1) { 35162306a36Sopenharmony_ci off -= len1; 35262306a36Sopenharmony_ci } else { 35362306a36Sopenharmony_ci copy = min(off + len, len1) - off; 35462306a36Sopenharmony_ci crc = crc_itu_t(crc, buf1 + off, copy); 35562306a36Sopenharmony_ci len -= copy; 35662306a36Sopenharmony_ci off = 0; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci if (len > 0) { 35962306a36Sopenharmony_ci if (WARN_ON_ONCE(off + len > len2 || !buf2)) 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci crc = crc_itu_t(crc, buf2 + off, len); 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci return crc; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void udf_copy_fi_to_bufs(char *buf1, int len1, char *buf2, int len2, 36762306a36Sopenharmony_ci int off, struct fileIdentDesc *fi, 36862306a36Sopenharmony_ci uint8_t *impuse, uint8_t *name) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci uint16_t crc; 37162306a36Sopenharmony_ci int fioff = off; 37262306a36Sopenharmony_ci int crcoff = off + sizeof(struct tag); 37362306a36Sopenharmony_ci unsigned int crclen = udf_dir_entry_len(fi) - sizeof(struct tag); 37462306a36Sopenharmony_ci char zeros[UDF_NAME_PAD] = {}; 37562306a36Sopenharmony_ci int endoff = off + udf_dir_entry_len(fi); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci udf_copy_to_bufs(buf1, len1, buf2, len2, off, fi, 37862306a36Sopenharmony_ci sizeof(struct fileIdentDesc)); 37962306a36Sopenharmony_ci off += sizeof(struct fileIdentDesc); 38062306a36Sopenharmony_ci if (impuse) 38162306a36Sopenharmony_ci udf_copy_to_bufs(buf1, len1, buf2, len2, off, impuse, 38262306a36Sopenharmony_ci le16_to_cpu(fi->lengthOfImpUse)); 38362306a36Sopenharmony_ci off += le16_to_cpu(fi->lengthOfImpUse); 38462306a36Sopenharmony_ci if (name) { 38562306a36Sopenharmony_ci udf_copy_to_bufs(buf1, len1, buf2, len2, off, name, 38662306a36Sopenharmony_ci fi->lengthFileIdent); 38762306a36Sopenharmony_ci off += fi->lengthFileIdent; 38862306a36Sopenharmony_ci udf_copy_to_bufs(buf1, len1, buf2, len2, off, zeros, 38962306a36Sopenharmony_ci endoff - off); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci crc = udf_crc_fi_bufs(buf1, len1, buf2, len2, crcoff, crclen); 39362306a36Sopenharmony_ci fi->descTag.descCRC = cpu_to_le16(crc); 39462306a36Sopenharmony_ci fi->descTag.descCRCLength = cpu_to_le16(crclen); 39562306a36Sopenharmony_ci fi->descTag.tagChecksum = udf_tag_checksum(&fi->descTag); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci udf_copy_to_bufs(buf1, len1, buf2, len2, fioff, fi, sizeof(struct tag)); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_civoid udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct udf_inode_info *iinfo = UDF_I(iter->dir); 40362306a36Sopenharmony_ci void *buf1, *buf2 = NULL; 40462306a36Sopenharmony_ci int len1, len2 = 0, off; 40562306a36Sopenharmony_ci int blksize = 1 << iter->dir->i_blkbits; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci off = iter->pos & (blksize - 1); 40862306a36Sopenharmony_ci if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { 40962306a36Sopenharmony_ci buf1 = iinfo->i_data + iinfo->i_lenEAttr; 41062306a36Sopenharmony_ci len1 = iter->dir->i_size; 41162306a36Sopenharmony_ci } else { 41262306a36Sopenharmony_ci buf1 = iter->bh[0]->b_data; 41362306a36Sopenharmony_ci len1 = blksize; 41462306a36Sopenharmony_ci if (iter->bh[1]) { 41562306a36Sopenharmony_ci buf2 = iter->bh[1]->b_data; 41662306a36Sopenharmony_ci len2 = blksize; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci udf_copy_fi_to_bufs(buf1, len1, buf2, len2, off, &iter->fi, impuse, 42162306a36Sopenharmony_ci iter->name == iter->namebuf ? iter->name : NULL); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { 42462306a36Sopenharmony_ci mark_inode_dirty(iter->dir); 42562306a36Sopenharmony_ci } else { 42662306a36Sopenharmony_ci mark_buffer_dirty_inode(iter->bh[0], iter->dir); 42762306a36Sopenharmony_ci if (iter->bh[1]) 42862306a36Sopenharmony_ci mark_buffer_dirty_inode(iter->bh[1], iter->dir); 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci inode_inc_iversion(iter->dir); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_civoid udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct udf_inode_info *iinfo = UDF_I(iter->dir); 43662306a36Sopenharmony_ci int diff = new_elen - iter->elen; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* Skip update when we already went past the last extent */ 43962306a36Sopenharmony_ci if (!iter->elen) 44062306a36Sopenharmony_ci return; 44162306a36Sopenharmony_ci iter->elen = new_elen; 44262306a36Sopenharmony_ci if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) 44362306a36Sopenharmony_ci iter->epos.offset -= sizeof(struct short_ad); 44462306a36Sopenharmony_ci else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) 44562306a36Sopenharmony_ci iter->epos.offset -= sizeof(struct long_ad); 44662306a36Sopenharmony_ci udf_write_aext(iter->dir, &iter->epos, &iter->eloc, iter->elen, 1); 44762306a36Sopenharmony_ci iinfo->i_lenExtents += diff; 44862306a36Sopenharmony_ci mark_inode_dirty(iter->dir); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci/* Append new block to directory. @iter is expected to point at EOF */ 45262306a36Sopenharmony_ciint udf_fiiter_append_blk(struct udf_fileident_iter *iter) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct udf_inode_info *iinfo = UDF_I(iter->dir); 45562306a36Sopenharmony_ci int blksize = 1 << iter->dir->i_blkbits; 45662306a36Sopenharmony_ci struct buffer_head *bh; 45762306a36Sopenharmony_ci sector_t block; 45862306a36Sopenharmony_ci uint32_t old_elen = iter->elen; 45962306a36Sopenharmony_ci int err; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)) 46262306a36Sopenharmony_ci return -EINVAL; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* Round up last extent in the file */ 46562306a36Sopenharmony_ci udf_fiiter_update_elen(iter, ALIGN(iter->elen, blksize)); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Allocate new block and refresh mapping information */ 46862306a36Sopenharmony_ci block = iinfo->i_lenExtents >> iter->dir->i_blkbits; 46962306a36Sopenharmony_ci bh = udf_bread(iter->dir, block, 1, &err); 47062306a36Sopenharmony_ci if (!bh) { 47162306a36Sopenharmony_ci udf_fiiter_update_elen(iter, old_elen); 47262306a36Sopenharmony_ci return err; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci if (inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen, 47562306a36Sopenharmony_ci &iter->loffset) != (EXT_RECORDED_ALLOCATED >> 30)) { 47662306a36Sopenharmony_ci udf_err(iter->dir->i_sb, 47762306a36Sopenharmony_ci "block %llu not allocated in directory (ino %lu)\n", 47862306a36Sopenharmony_ci (unsigned long long)block, iter->dir->i_ino); 47962306a36Sopenharmony_ci return -EFSCORRUPTED; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci if (!(iter->pos & (blksize - 1))) { 48262306a36Sopenharmony_ci brelse(iter->bh[0]); 48362306a36Sopenharmony_ci iter->bh[0] = bh; 48462306a36Sopenharmony_ci } else { 48562306a36Sopenharmony_ci iter->bh[1] = bh; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistruct short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, uint32_t *offset, 49162306a36Sopenharmony_ci int inc) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct short_ad *sa; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if ((!ptr) || (!offset)) { 49662306a36Sopenharmony_ci pr_err("%s: invalidparms\n", __func__); 49762306a36Sopenharmony_ci return NULL; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if ((*offset + sizeof(struct short_ad)) > maxoffset) 50162306a36Sopenharmony_ci return NULL; 50262306a36Sopenharmony_ci else { 50362306a36Sopenharmony_ci sa = (struct short_ad *)ptr; 50462306a36Sopenharmony_ci if (sa->extLength == 0) 50562306a36Sopenharmony_ci return NULL; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (inc) 50962306a36Sopenharmony_ci *offset += sizeof(struct short_ad); 51062306a36Sopenharmony_ci return sa; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistruct long_ad *udf_get_filelongad(uint8_t *ptr, int maxoffset, uint32_t *offset, int inc) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct long_ad *la; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if ((!ptr) || (!offset)) { 51862306a36Sopenharmony_ci pr_err("%s: invalidparms\n", __func__); 51962306a36Sopenharmony_ci return NULL; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if ((*offset + sizeof(struct long_ad)) > maxoffset) 52362306a36Sopenharmony_ci return NULL; 52462306a36Sopenharmony_ci else { 52562306a36Sopenharmony_ci la = (struct long_ad *)ptr; 52662306a36Sopenharmony_ci if (la->extLength == 0) 52762306a36Sopenharmony_ci return NULL; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (inc) 53162306a36Sopenharmony_ci *offset += sizeof(struct long_ad); 53262306a36Sopenharmony_ci return la; 53362306a36Sopenharmony_ci} 534