543 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * directory.c4 *5 * PURPOSE6 * Directory related functions7 *8 */9 10#include "udfdecl.h"11#include "udf_i.h"12 13#include <linux/fs.h>14#include <linux/string.h>15#include <linux/bio.h>16#include <linux/crc-itu-t.h>17#include <linux/iversion.h>18 19static int udf_verify_fi(struct udf_fileident_iter *iter)20{21 unsigned int len;22 23 if (iter->fi.descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) {24 udf_err(iter->dir->i_sb,25 "directory (ino %lu) has entry at pos %llu with incorrect tag %x\n",26 iter->dir->i_ino, (unsigned long long)iter->pos,27 le16_to_cpu(iter->fi.descTag.tagIdent));28 return -EFSCORRUPTED;29 }30 len = udf_dir_entry_len(&iter->fi);31 if (le16_to_cpu(iter->fi.lengthOfImpUse) & 3) {32 udf_err(iter->dir->i_sb,33 "directory (ino %lu) has entry at pos %llu with unaligned length of impUse field\n",34 iter->dir->i_ino, (unsigned long long)iter->pos);35 return -EFSCORRUPTED;36 }37 /*38 * This is in fact allowed by the spec due to long impUse field but39 * we don't support it. If there is real media with this large impUse40 * field, support can be added.41 */42 if (len > 1 << iter->dir->i_blkbits) {43 udf_err(iter->dir->i_sb,44 "directory (ino %lu) has too big (%u) entry at pos %llu\n",45 iter->dir->i_ino, len, (unsigned long long)iter->pos);46 return -EFSCORRUPTED;47 }48 if (iter->pos + len > iter->dir->i_size) {49 udf_err(iter->dir->i_sb,50 "directory (ino %lu) has entry past directory size at pos %llu\n",51 iter->dir->i_ino, (unsigned long long)iter->pos);52 return -EFSCORRUPTED;53 }54 if (udf_dir_entry_len(&iter->fi) !=55 sizeof(struct tag) + le16_to_cpu(iter->fi.descTag.descCRCLength)) {56 udf_err(iter->dir->i_sb,57 "directory (ino %lu) has entry where CRC length (%u) does not match entry length (%u)\n",58 iter->dir->i_ino,59 (unsigned)le16_to_cpu(iter->fi.descTag.descCRCLength),60 (unsigned)(udf_dir_entry_len(&iter->fi) -61 sizeof(struct tag)));62 return -EFSCORRUPTED;63 }64 return 0;65}66 67static int udf_copy_fi(struct udf_fileident_iter *iter)68{69 struct udf_inode_info *iinfo = UDF_I(iter->dir);70 u32 blksize = 1 << iter->dir->i_blkbits;71 u32 off, len, nameoff;72 int err;73 74 /* Skip copying when we are at EOF */75 if (iter->pos >= iter->dir->i_size) {76 iter->name = NULL;77 return 0;78 }79 if (iter->dir->i_size < iter->pos + sizeof(struct fileIdentDesc)) {80 udf_err(iter->dir->i_sb,81 "directory (ino %lu) has entry straddling EOF\n",82 iter->dir->i_ino);83 return -EFSCORRUPTED;84 }85 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {86 memcpy(&iter->fi, iinfo->i_data + iinfo->i_lenEAttr + iter->pos,87 sizeof(struct fileIdentDesc));88 err = udf_verify_fi(iter);89 if (err < 0)90 return err;91 iter->name = iinfo->i_data + iinfo->i_lenEAttr + iter->pos +92 sizeof(struct fileIdentDesc) +93 le16_to_cpu(iter->fi.lengthOfImpUse);94 return 0;95 }96 97 off = iter->pos & (blksize - 1);98 len = min_t(u32, sizeof(struct fileIdentDesc), blksize - off);99 memcpy(&iter->fi, iter->bh[0]->b_data + off, len);100 if (len < sizeof(struct fileIdentDesc))101 memcpy((char *)(&iter->fi) + len, iter->bh[1]->b_data,102 sizeof(struct fileIdentDesc) - len);103 err = udf_verify_fi(iter);104 if (err < 0)105 return err;106 107 /* Handle directory entry name */108 nameoff = off + sizeof(struct fileIdentDesc) +109 le16_to_cpu(iter->fi.lengthOfImpUse);110 if (off + udf_dir_entry_len(&iter->fi) <= blksize) {111 iter->name = iter->bh[0]->b_data + nameoff;112 } else if (nameoff >= blksize) {113 iter->name = iter->bh[1]->b_data + (nameoff - blksize);114 } else {115 iter->name = iter->namebuf;116 len = blksize - nameoff;117 memcpy(iter->name, iter->bh[0]->b_data + nameoff, len);118 memcpy(iter->name + len, iter->bh[1]->b_data,119 iter->fi.lengthFileIdent - len);120 }121 return 0;122}123 124/* Readahead 8k once we are at 8k boundary */125static void udf_readahead_dir(struct udf_fileident_iter *iter)126{127 unsigned int ralen = 16 >> (iter->dir->i_blkbits - 9);128 struct buffer_head *tmp, *bha[16];129 int i, num;130 udf_pblk_t blk;131 132 if (iter->loffset & (ralen - 1))133 return;134 135 if (iter->loffset + ralen > (iter->elen >> iter->dir->i_blkbits))136 ralen = (iter->elen >> iter->dir->i_blkbits) - iter->loffset;137 num = 0;138 for (i = 0; i < ralen; i++) {139 blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc,140 iter->loffset + i);141 tmp = sb_getblk(iter->dir->i_sb, blk);142 if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))143 bha[num++] = tmp;144 else145 brelse(tmp);146 }147 if (num) {148 bh_readahead_batch(num, bha, REQ_RAHEAD);149 for (i = 0; i < num; i++)150 brelse(bha[i]);151 }152}153 154static struct buffer_head *udf_fiiter_bread_blk(struct udf_fileident_iter *iter)155{156 udf_pblk_t blk;157 158 udf_readahead_dir(iter);159 blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc, iter->loffset);160 return sb_bread(iter->dir->i_sb, blk);161}162 163/*164 * Updates loffset to point to next directory block; eloc, elen & epos are165 * updated if we need to traverse to the next extent as well.166 */167static int udf_fiiter_advance_blk(struct udf_fileident_iter *iter)168{169 int8_t etype = -1;170 int err = 0;171 172 iter->loffset++;173 if (iter->loffset < DIV_ROUND_UP(iter->elen, 1<<iter->dir->i_blkbits))174 return 0;175 176 iter->loffset = 0;177 err = udf_next_aext(iter->dir, &iter->epos, &iter->eloc,178 &iter->elen, &etype, 1);179 if (err < 0)180 return err;181 else if (err == 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {182 if (iter->pos == iter->dir->i_size) {183 iter->elen = 0;184 return 0;185 }186 udf_err(iter->dir->i_sb,187 "extent after position %llu not allocated in directory (ino %lu)\n",188 (unsigned long long)iter->pos, iter->dir->i_ino);189 return -EFSCORRUPTED;190 }191 return 0;192}193 194static int udf_fiiter_load_bhs(struct udf_fileident_iter *iter)195{196 int blksize = 1 << iter->dir->i_blkbits;197 int off = iter->pos & (blksize - 1);198 int err;199 struct fileIdentDesc *fi;200 201 /* Is there any further extent we can map from? */202 if (!iter->bh[0] && iter->elen) {203 iter->bh[0] = udf_fiiter_bread_blk(iter);204 if (!iter->bh[0]) {205 err = -ENOMEM;206 goto out_brelse;207 }208 if (!buffer_uptodate(iter->bh[0])) {209 err = -EIO;210 goto out_brelse;211 }212 }213 /* There's no next block so we are done */214 if (iter->pos >= iter->dir->i_size)215 return 0;216 /* Need to fetch next block as well? */217 if (off + sizeof(struct fileIdentDesc) > blksize)218 goto fetch_next;219 fi = (struct fileIdentDesc *)(iter->bh[0]->b_data + off);220 /* Need to fetch next block to get name? */221 if (off + udf_dir_entry_len(fi) > blksize) {222fetch_next:223 err = udf_fiiter_advance_blk(iter);224 if (err)225 goto out_brelse;226 iter->bh[1] = udf_fiiter_bread_blk(iter);227 if (!iter->bh[1]) {228 err = -ENOMEM;229 goto out_brelse;230 }231 if (!buffer_uptodate(iter->bh[1])) {232 err = -EIO;233 goto out_brelse;234 }235 }236 return 0;237out_brelse:238 brelse(iter->bh[0]);239 brelse(iter->bh[1]);240 iter->bh[0] = iter->bh[1] = NULL;241 return err;242}243 244int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,245 loff_t pos)246{247 struct udf_inode_info *iinfo = UDF_I(dir);248 int err = 0;249 int8_t etype;250 251 iter->dir = dir;252 iter->bh[0] = iter->bh[1] = NULL;253 iter->pos = pos;254 iter->elen = 0;255 iter->epos.bh = NULL;256 iter->name = NULL;257 /*258 * When directory is verified, we don't expect directory iteration to259 * fail and it can be difficult to undo without corrupting filesystem.260 * So just do not allow memory allocation failures here.261 */262 iter->namebuf = kmalloc(UDF_NAME_LEN_CS0, GFP_KERNEL | __GFP_NOFAIL);263 264 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {265 err = udf_copy_fi(iter);266 goto out;267 }268 269 err = inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos,270 &iter->eloc, &iter->elen, &iter->loffset, &etype);271 if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {272 if (pos == dir->i_size)273 return 0;274 udf_err(dir->i_sb,275 "position %llu not allocated in directory (ino %lu)\n",276 (unsigned long long)pos, dir->i_ino);277 err = -EFSCORRUPTED;278 goto out;279 }280 err = udf_fiiter_load_bhs(iter);281 if (err < 0)282 goto out;283 err = udf_copy_fi(iter);284out:285 if (err < 0)286 udf_fiiter_release(iter);287 return err;288}289 290int udf_fiiter_advance(struct udf_fileident_iter *iter)291{292 unsigned int oldoff, len;293 int blksize = 1 << iter->dir->i_blkbits;294 int err;295 296 oldoff = iter->pos & (blksize - 1);297 len = udf_dir_entry_len(&iter->fi);298 iter->pos += len;299 if (UDF_I(iter->dir)->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {300 if (oldoff + len >= blksize) {301 brelse(iter->bh[0]);302 iter->bh[0] = NULL;303 /* Next block already loaded? */304 if (iter->bh[1]) {305 iter->bh[0] = iter->bh[1];306 iter->bh[1] = NULL;307 } else {308 err = udf_fiiter_advance_blk(iter);309 if (err < 0)310 return err;311 }312 }313 err = udf_fiiter_load_bhs(iter);314 if (err < 0)315 return err;316 }317 return udf_copy_fi(iter);318}319 320void udf_fiiter_release(struct udf_fileident_iter *iter)321{322 iter->dir = NULL;323 brelse(iter->bh[0]);324 brelse(iter->bh[1]);325 iter->bh[0] = iter->bh[1] = NULL;326 kfree(iter->namebuf);327 iter->namebuf = NULL;328}329 330static void udf_copy_to_bufs(void *buf1, int len1, void *buf2, int len2,331 int off, void *src, int len)332{333 int copy;334 335 if (off >= len1) {336 off -= len1;337 } else {338 copy = min(off + len, len1) - off;339 memcpy(buf1 + off, src, copy);340 src += copy;341 len -= copy;342 off = 0;343 }344 if (len > 0) {345 if (WARN_ON_ONCE(off + len > len2 || !buf2))346 return;347 memcpy(buf2 + off, src, len);348 }349}350 351static uint16_t udf_crc_fi_bufs(void *buf1, int len1, void *buf2, int len2,352 int off, int len)353{354 int copy;355 uint16_t crc = 0;356 357 if (off >= len1) {358 off -= len1;359 } else {360 copy = min(off + len, len1) - off;361 crc = crc_itu_t(crc, buf1 + off, copy);362 len -= copy;363 off = 0;364 }365 if (len > 0) {366 if (WARN_ON_ONCE(off + len > len2 || !buf2))367 return 0;368 crc = crc_itu_t(crc, buf2 + off, len);369 }370 return crc;371}372 373static void udf_copy_fi_to_bufs(char *buf1, int len1, char *buf2, int len2,374 int off, struct fileIdentDesc *fi,375 uint8_t *impuse, uint8_t *name)376{377 uint16_t crc;378 int fioff = off;379 int crcoff = off + sizeof(struct tag);380 unsigned int crclen = udf_dir_entry_len(fi) - sizeof(struct tag);381 char zeros[UDF_NAME_PAD] = {};382 int endoff = off + udf_dir_entry_len(fi);383 384 udf_copy_to_bufs(buf1, len1, buf2, len2, off, fi,385 sizeof(struct fileIdentDesc));386 off += sizeof(struct fileIdentDesc);387 if (impuse)388 udf_copy_to_bufs(buf1, len1, buf2, len2, off, impuse,389 le16_to_cpu(fi->lengthOfImpUse));390 off += le16_to_cpu(fi->lengthOfImpUse);391 if (name) {392 udf_copy_to_bufs(buf1, len1, buf2, len2, off, name,393 fi->lengthFileIdent);394 off += fi->lengthFileIdent;395 udf_copy_to_bufs(buf1, len1, buf2, len2, off, zeros,396 endoff - off);397 }398 399 crc = udf_crc_fi_bufs(buf1, len1, buf2, len2, crcoff, crclen);400 fi->descTag.descCRC = cpu_to_le16(crc);401 fi->descTag.descCRCLength = cpu_to_le16(crclen);402 fi->descTag.tagChecksum = udf_tag_checksum(&fi->descTag);403 404 udf_copy_to_bufs(buf1, len1, buf2, len2, fioff, fi, sizeof(struct tag));405}406 407void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse)408{409 struct udf_inode_info *iinfo = UDF_I(iter->dir);410 void *buf1, *buf2 = NULL;411 int len1, len2 = 0, off;412 int blksize = 1 << iter->dir->i_blkbits;413 414 off = iter->pos & (blksize - 1);415 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {416 buf1 = iinfo->i_data + iinfo->i_lenEAttr;417 len1 = iter->dir->i_size;418 } else {419 buf1 = iter->bh[0]->b_data;420 len1 = blksize;421 if (iter->bh[1]) {422 buf2 = iter->bh[1]->b_data;423 len2 = blksize;424 }425 }426 427 udf_copy_fi_to_bufs(buf1, len1, buf2, len2, off, &iter->fi, impuse,428 iter->name == iter->namebuf ? iter->name : NULL);429 430 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {431 mark_inode_dirty(iter->dir);432 } else {433 mark_buffer_dirty_inode(iter->bh[0], iter->dir);434 if (iter->bh[1])435 mark_buffer_dirty_inode(iter->bh[1], iter->dir);436 }437 inode_inc_iversion(iter->dir);438}439 440void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen)441{442 struct udf_inode_info *iinfo = UDF_I(iter->dir);443 int diff = new_elen - iter->elen;444 445 /* Skip update when we already went past the last extent */446 if (!iter->elen)447 return;448 iter->elen = new_elen;449 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)450 iter->epos.offset -= sizeof(struct short_ad);451 else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)452 iter->epos.offset -= sizeof(struct long_ad);453 udf_write_aext(iter->dir, &iter->epos, &iter->eloc, iter->elen, 1);454 iinfo->i_lenExtents += diff;455 mark_inode_dirty(iter->dir);456}457 458/* Append new block to directory. @iter is expected to point at EOF */459int udf_fiiter_append_blk(struct udf_fileident_iter *iter)460{461 struct udf_inode_info *iinfo = UDF_I(iter->dir);462 int blksize = 1 << iter->dir->i_blkbits;463 struct buffer_head *bh;464 sector_t block;465 uint32_t old_elen = iter->elen;466 int err;467 int8_t etype;468 469 if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))470 return -EINVAL;471 472 /* Round up last extent in the file */473 udf_fiiter_update_elen(iter, ALIGN(iter->elen, blksize));474 475 /* Allocate new block and refresh mapping information */476 block = iinfo->i_lenExtents >> iter->dir->i_blkbits;477 bh = udf_bread(iter->dir, block, 1, &err);478 if (!bh) {479 udf_fiiter_update_elen(iter, old_elen);480 return err;481 }482 err = inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen,483 &iter->loffset, &etype);484 if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {485 udf_err(iter->dir->i_sb,486 "block %llu not allocated in directory (ino %lu)\n",487 (unsigned long long)block, iter->dir->i_ino);488 return -EFSCORRUPTED;489 }490 if (!(iter->pos & (blksize - 1))) {491 brelse(iter->bh[0]);492 iter->bh[0] = bh;493 } else {494 iter->bh[1] = bh;495 }496 return 0;497}498 499struct short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, uint32_t *offset,500 int inc)501{502 struct short_ad *sa;503 504 if ((!ptr) || (!offset)) {505 pr_err("%s: invalidparms\n", __func__);506 return NULL;507 }508 509 if ((*offset + sizeof(struct short_ad)) > maxoffset)510 return NULL;511 else {512 sa = (struct short_ad *)ptr;513 if (sa->extLength == 0)514 return NULL;515 }516 517 if (inc)518 *offset += sizeof(struct short_ad);519 return sa;520}521 522struct long_ad *udf_get_filelongad(uint8_t *ptr, int maxoffset, uint32_t *offset, int inc)523{524 struct long_ad *la;525 526 if ((!ptr) || (!offset)) {527 pr_err("%s: invalidparms\n", __func__);528 return NULL;529 }530 531 if ((*offset + sizeof(struct long_ad)) > maxoffset)532 return NULL;533 else {534 la = (struct long_ad *)ptr;535 if (la->extLength == 0)536 return NULL;537 }538 539 if (inc)540 *offset += sizeof(struct long_ad);541 return la;542}543