linux/fs/udf/directory.c
<<
>>
Prefs
   1/*
   2 * directory.c
   3 *
   4 * PURPOSE
   5 *      Directory related functions
   6 *
   7 * COPYRIGHT
   8 *      This file is distributed under the terms of the GNU General Public
   9 *      License (GPL). Copies of the GPL can be obtained from:
  10 *              ftp://prep.ai.mit.edu/pub/gnu/GPL
  11 *      Each contributing author retains all rights to their own work.
  12 */
  13
  14#include "udfdecl.h"
  15#include "udf_i.h"
  16
  17#include <linux/fs.h>
  18#include <linux/string.h>
  19#include <linux/bio.h>
  20
  21struct fileIdentDesc *udf_fileident_read(struct inode *dir, loff_t *nf_pos,
  22                                         struct udf_fileident_bh *fibh,
  23                                         struct fileIdentDesc *cfi,
  24                                         struct extent_position *epos,
  25                                         struct kernel_lb_addr *eloc, uint32_t *elen,
  26                                         sector_t *offset)
  27{
  28        struct fileIdentDesc *fi;
  29        int i, num;
  30        udf_pblk_t block;
  31        struct buffer_head *tmp, *bha[16];
  32        struct udf_inode_info *iinfo = UDF_I(dir);
  33
  34        fibh->soffset = fibh->eoffset;
  35
  36        if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
  37                fi = udf_get_fileident(iinfo->i_data -
  38                                       (iinfo->i_efe ?
  39                                        sizeof(struct extendedFileEntry) :
  40                                        sizeof(struct fileEntry)),
  41                                       dir->i_sb->s_blocksize,
  42                                       &(fibh->eoffset));
  43                if (!fi)
  44                        return NULL;
  45
  46                *nf_pos += fibh->eoffset - fibh->soffset;
  47
  48                memcpy((uint8_t *)cfi, (uint8_t *)fi,
  49                       sizeof(struct fileIdentDesc));
  50
  51                return fi;
  52        }
  53
  54        if (fibh->eoffset == dir->i_sb->s_blocksize) {
  55                uint32_t lextoffset = epos->offset;
  56                unsigned char blocksize_bits = dir->i_sb->s_blocksize_bits;
  57
  58                if (udf_next_aext(dir, epos, eloc, elen, 1) !=
  59                    (EXT_RECORDED_ALLOCATED >> 30))
  60                        return NULL;
  61
  62                block = udf_get_lb_pblock(dir->i_sb, eloc, *offset);
  63
  64                (*offset)++;
  65
  66                if ((*offset << blocksize_bits) >= *elen)
  67                        *offset = 0;
  68                else
  69                        epos->offset = lextoffset;
  70
  71                brelse(fibh->sbh);
  72                fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
  73                if (!fibh->sbh)
  74                        return NULL;
  75                fibh->soffset = fibh->eoffset = 0;
  76
  77                if (!(*offset & ((16 >> (blocksize_bits - 9)) - 1))) {
  78                        i = 16 >> (blocksize_bits - 9);
  79                        if (i + *offset > (*elen >> blocksize_bits))
  80                                i = (*elen >> blocksize_bits)-*offset;
  81                        for (num = 0; i > 0; i--) {
  82                                block = udf_get_lb_pblock(dir->i_sb, eloc,
  83                                                          *offset + i);
  84                                tmp = udf_tgetblk(dir->i_sb, block);
  85                                if (tmp && !buffer_uptodate(tmp) &&
  86                                                !buffer_locked(tmp))
  87                                        bha[num++] = tmp;
  88                                else
  89                                        brelse(tmp);
  90                        }
  91                        if (num) {
  92                                ll_rw_block(REQ_OP_READ, REQ_RAHEAD, num, bha);
  93                                for (i = 0; i < num; i++)
  94                                        brelse(bha[i]);
  95                        }
  96                }
  97        } else if (fibh->sbh != fibh->ebh) {
  98                brelse(fibh->sbh);
  99                fibh->sbh = fibh->ebh;
 100        }
 101
 102        fi = udf_get_fileident(fibh->sbh->b_data, dir->i_sb->s_blocksize,
 103                               &(fibh->eoffset));
 104
 105        if (!fi)
 106                return NULL;
 107
 108        *nf_pos += fibh->eoffset - fibh->soffset;
 109
 110        if (fibh->eoffset <= dir->i_sb->s_blocksize) {
 111                memcpy((uint8_t *)cfi, (uint8_t *)fi,
 112                       sizeof(struct fileIdentDesc));
 113        } else if (fibh->eoffset > dir->i_sb->s_blocksize) {
 114                uint32_t lextoffset = epos->offset;
 115
 116                if (udf_next_aext(dir, epos, eloc, elen, 1) !=
 117                    (EXT_RECORDED_ALLOCATED >> 30))
 118                        return NULL;
 119
 120                block = udf_get_lb_pblock(dir->i_sb, eloc, *offset);
 121
 122                (*offset)++;
 123
 124                if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen)
 125                        *offset = 0;
 126                else
 127                        epos->offset = lextoffset;
 128
 129                fibh->soffset -= dir->i_sb->s_blocksize;
 130                fibh->eoffset -= dir->i_sb->s_blocksize;
 131
 132                fibh->ebh = udf_tread(dir->i_sb, block);
 133                if (!fibh->ebh)
 134                        return NULL;
 135
 136                if (sizeof(struct fileIdentDesc) > -fibh->soffset) {
 137                        int fi_len;
 138
 139                        memcpy((uint8_t *)cfi, (uint8_t *)fi, -fibh->soffset);
 140                        memcpy((uint8_t *)cfi - fibh->soffset,
 141                               fibh->ebh->b_data,
 142                               sizeof(struct fileIdentDesc) + fibh->soffset);
 143
 144                        fi_len = udf_dir_entry_len(cfi);
 145                        *nf_pos += fi_len - (fibh->eoffset - fibh->soffset);
 146                        fibh->eoffset = fibh->soffset + fi_len;
 147                } else {
 148                        memcpy((uint8_t *)cfi, (uint8_t *)fi,
 149                               sizeof(struct fileIdentDesc));
 150                }
 151        }
 152        /* Got last entry outside of dir size - fs is corrupted! */
 153        if (*nf_pos > dir->i_size)
 154                return NULL;
 155        return fi;
 156}
 157
 158struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize, int *offset)
 159{
 160        struct fileIdentDesc *fi;
 161        int lengthThisIdent;
 162        uint8_t *ptr;
 163        int padlen;
 164
 165        if ((!buffer) || (!offset)) {
 166                udf_debug("invalidparms, buffer=%p, offset=%p\n",
 167                          buffer, offset);
 168                return NULL;
 169        }
 170
 171        ptr = buffer;
 172
 173        if ((*offset > 0) && (*offset < bufsize))
 174                ptr += *offset;
 175        fi = (struct fileIdentDesc *)ptr;
 176        if (fi->descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) {
 177                udf_debug("0x%x != TAG_IDENT_FID\n",
 178                          le16_to_cpu(fi->descTag.tagIdent));
 179                udf_debug("offset: %d sizeof: %lu bufsize: %d\n",
 180                          *offset, (unsigned long)sizeof(struct fileIdentDesc),
 181                          bufsize);
 182                return NULL;
 183        }
 184        if ((*offset + sizeof(struct fileIdentDesc)) > bufsize)
 185                lengthThisIdent = sizeof(struct fileIdentDesc);
 186        else
 187                lengthThisIdent = sizeof(struct fileIdentDesc) +
 188                        fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse);
 189
 190        /* we need to figure padding, too! */
 191        padlen = lengthThisIdent % UDF_NAME_PAD;
 192        if (padlen)
 193                lengthThisIdent += (UDF_NAME_PAD - padlen);
 194        *offset = *offset + lengthThisIdent;
 195
 196        return fi;
 197}
 198
 199struct short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, uint32_t *offset,
 200                              int inc)
 201{
 202        struct short_ad *sa;
 203
 204        if ((!ptr) || (!offset)) {
 205                pr_err("%s: invalidparms\n", __func__);
 206                return NULL;
 207        }
 208
 209        if ((*offset + sizeof(struct short_ad)) > maxoffset)
 210                return NULL;
 211        else {
 212                sa = (struct short_ad *)ptr;
 213                if (sa->extLength == 0)
 214                        return NULL;
 215        }
 216
 217        if (inc)
 218                *offset += sizeof(struct short_ad);
 219        return sa;
 220}
 221
 222struct long_ad *udf_get_filelongad(uint8_t *ptr, int maxoffset, uint32_t *offset, int inc)
 223{
 224        struct long_ad *la;
 225
 226        if ((!ptr) || (!offset)) {
 227                pr_err("%s: invalidparms\n", __func__);
 228                return NULL;
 229        }
 230
 231        if ((*offset + sizeof(struct long_ad)) > maxoffset)
 232                return NULL;
 233        else {
 234                la = (struct long_ad *)ptr;
 235                if (la->extLength == 0)
 236                        return NULL;
 237        }
 238
 239        if (inc)
 240                *offset += sizeof(struct long_ad);
 241        return la;
 242}
 243