/** * ntfs_extent_inode_open - load an extent inode and attach it to its base * @base_ni: base ntfs inode * @mref: mft reference of the extent inode to load (in little endian) * * First check if the extent inode @mref is already attached to the base ntfs * inode @base_ni, and if so, return a pointer to the attached extent inode. * * If the extent inode is not already attached to the base inode, allocate an * ntfs_inode structure and initialize it for the given inode @mref. @mref * specifies the inode number / mft record to read, including the sequence * number, which can be 0 if no sequence number checking is to be performed. * * Then, allocate a buffer for the mft record, read the mft record from the * volume @base_ni->vol, and attach it to the ntfs_inode structure (->mrec). * The mft record is mst deprotected and sanity checked for validity and we * abort if deprotection or checks fail. * * Finally attach the ntfs inode to its base inode @base_ni and return a * pointer to the ntfs_inode structure on success or NULL on error, with errno * set to the error code. * * Note, extent inodes are never closed directly. They are automatically * disposed off by the closing of the base inode. */ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) { u64 mft_no = MREF_LE(mref); ntfs_inode *ni = NULL; ntfs_inode **extent_nis; int i; if (!base_ni) { errno = EINVAL; ntfs_log_perror("%s", __FUNCTION__); return NULL; } ntfs_log_enter("Opening extent inode %lld (base mft record %lld).\n", (unsigned long long)mft_no, (unsigned long long)base_ni->mft_no); /* Is the extent inode already open and attached to the base inode? */ if (base_ni->nr_extents > 0) { extent_nis = base_ni->extent_nis; for (i = 0; i < base_ni->nr_extents; i++) { u16 seq_no; ni = extent_nis[i]; if (mft_no != ni->mft_no) continue; /* Verify the sequence number if given. */ seq_no = MSEQNO_LE(mref); if (seq_no && seq_no != le16_to_cpu( ni->mrec->sequence_number)) { errno = EIO; ntfs_log_perror("Found stale extent mft " "reference mft=%lld", (long long)ni->mft_no); goto out; } goto out; } } /* Wasn't there, we need to load the extent inode. */ ni = __ntfs_inode_allocate(base_ni->vol); if (!ni) goto out; if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec, NULL)) goto err_out; ni->mft_no = mft_no; ni->nr_extents = -1; ni->base_ni = base_ni; /* Attach extent inode to base inode, reallocating memory if needed. */ if (!(base_ni->nr_extents & 3)) { i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); extent_nis = ntfs_malloc(i); if (!extent_nis) goto err_out; if (base_ni->nr_extents) { memcpy(extent_nis, base_ni->extent_nis, i - 4 * sizeof(ntfs_inode *)); free(base_ni->extent_nis); } base_ni->extent_nis = extent_nis; } base_ni->extent_nis[base_ni->nr_extents++] = ni; out: ntfs_log_leave("\n"); return ni; err_out: __ntfs_inode_release(ni); ni = NULL; goto out; }
/** * map_extent_mft_record - load an extent inode and attach it to its base * @base_ni: base ntfs inode * @mref: mft reference of the extent inode to load (in little endian) * @ntfs_ino: on successful return, pointer to the ntfs_inode structure * * Load the extent mft record @mref and attach it to its base inode @base_ni. * Return the mapped extent mft record if IS_ERR(result) is false. Otherwise * PTR_ERR(result) gives the negative error code. * * On successful return, @ntfs_ino contains a pointer to the ntfs_inode * structure of the mapped extent inode. */ MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref, ntfs_inode **ntfs_ino) { MFT_RECORD *m; ntfs_inode *ni = NULL; ntfs_inode **extent_nis = NULL; int i; unsigned long mft_no = MREF_LE(mref); u16 seq_no = MSEQNO_LE(mref); BOOL destroy_ni = FALSE; ntfs_debug("Mapping extent mft record 0x%lx (base mft record 0x%lx).", mft_no, base_ni->mft_no); /* Make sure the base ntfs inode doesn't go away. */ atomic_inc(&base_ni->count); /* * Check if this extent inode has already been added to the base inode, * in which case just return it. If not found, add it to the base * inode before returning it. */ down(&base_ni->extent_lock); if (base_ni->nr_extents > 0) { extent_nis = base_ni->ext.extent_ntfs_inos; for (i = 0; i < base_ni->nr_extents; i++) { if (mft_no != extent_nis[i]->mft_no) continue; ni = extent_nis[i]; /* Make sure the ntfs inode doesn't go away. */ atomic_inc(&ni->count); break; } } if (likely(ni != NULL)) { up(&base_ni->extent_lock); atomic_dec(&base_ni->count); /* We found the record; just have to map and return it. */ m = map_mft_record(ni); /* map_mft_record() has incremented this on success. */ atomic_dec(&ni->count); if (likely(!IS_ERR(m))) { /* Verify the sequence number. */ if (likely(le16_to_cpu(m->sequence_number) == seq_no)) { ntfs_debug("Done 1."); *ntfs_ino = ni; return m; } unmap_mft_record(ni); ntfs_error(base_ni->vol->sb, "Found stale extent mft " "reference! Corrupt file system. " "Run chkdsk."); return ERR_PTR(-EIO); } map_err_out: ntfs_error(base_ni->vol->sb, "Failed to map extent " "mft record, error code %ld.", -PTR_ERR(m)); return m; } /* Record wasn't there. Get a new ntfs inode and initialize it. */ ni = ntfs_new_extent_inode(base_ni->vol->sb, mft_no); if (unlikely(!ni)) { up(&base_ni->extent_lock); atomic_dec(&base_ni->count); return ERR_PTR(-ENOMEM); } ni->vol = base_ni->vol; ni->seq_no = seq_no; ni->nr_extents = -1; ni->ext.base_ntfs_ino = base_ni; /* Now map the record. */ m = map_mft_record(ni); if (unlikely(IS_ERR(m))) { up(&base_ni->extent_lock); atomic_dec(&base_ni->count); ntfs_clear_extent_inode(ni); goto map_err_out; } /* Verify the sequence number. */ if (unlikely(le16_to_cpu(m->sequence_number) != seq_no)) { ntfs_error(base_ni->vol->sb, "Found stale extent mft " "reference! Corrupt file system. Run chkdsk."); destroy_ni = TRUE; m = ERR_PTR(-EIO); goto unm_err_out; } /* Attach extent inode to base inode, reallocating memory if needed. */ if (!(base_ni->nr_extents & 3)) { ntfs_inode **tmp; int new_size = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); tmp = (ntfs_inode **)kmalloc(new_size, GFP_NOFS); if (unlikely(!tmp)) { ntfs_error(base_ni->vol->sb, "Failed to allocate " "internal buffer."); destroy_ni = TRUE; m = ERR_PTR(-ENOMEM); goto unm_err_out; } if (base_ni->ext.extent_ntfs_inos) { memcpy(tmp, base_ni->ext.extent_ntfs_inos, new_size - 4 * sizeof(ntfs_inode *)); kfree(base_ni->ext.extent_ntfs_inos); } base_ni->ext.extent_ntfs_inos = tmp; } base_ni->ext.extent_ntfs_inos[base_ni->nr_extents++] = ni; up(&base_ni->extent_lock); atomic_dec(&base_ni->count); ntfs_debug("Done 2."); *ntfs_ino = ni; return m; unm_err_out: unmap_mft_record(ni); up(&base_ni->extent_lock); atomic_dec(&base_ni->count); /* * If the extent inode was not attached to the base inode we need to * release it or we will leak memory. */ if (destroy_ni) ntfs_clear_extent_inode(ni); return m; }