static int befs_readdir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; befs_data_stream *ds = &BEFS_I(inode)->i_data.ds; befs_off_t value; int result; size_t keysize; unsigned char d_type; char keybuf[BEFS_NAME_LEN + 1]; befs_debug(sb, "---> %s name %pD, inode %ld, ctx->pos %lld", __func__, file, inode->i_ino, ctx->pos); more: result = befs_btree_read(sb, ds, ctx->pos, BEFS_NAME_LEN + 1, keybuf, &keysize, &value); if (result == BEFS_ERR) { befs_debug(sb, "<--- %s ERROR", __func__); befs_error(sb, "IO error reading %pD (inode %lu)", file, inode->i_ino); return -EIO; } else if (result == BEFS_BT_END) { befs_debug(sb, "<--- %s END", __func__); return 0; } else if (result == BEFS_BT_EMPTY) { befs_debug(sb, "<--- %s Empty directory", __func__); return 0; } d_type = DT_UNKNOWN; /* Convert to NLS */ if (BEFS_SB(sb)->nls) { char *nlsname; int nlsnamelen; result = befs_utf2nls(sb, keybuf, keysize, &nlsname, &nlsnamelen); if (result < 0) { befs_debug(sb, "<--- %s ERROR", __func__); return result; } if (!dir_emit(ctx, nlsname, nlsnamelen, (ino_t) value, d_type)) { kfree(nlsname); return 0; } kfree(nlsname); } else { if (!dir_emit(ctx, keybuf, keysize, (ino_t) value, d_type)) return 0; } ctx->pos++; goto more; }
/* * Get root directory contents. */ static int zpl_root_iterate(struct file *filp, struct dir_context *ctx) { zfs_sb_t *zsb = ITOZSB(filp->f_path.dentry->d_inode); int error = 0; ZFS_ENTER(zsb); if (!dir_emit_dots(filp, ctx)) goto out; if (ctx->pos == 2) { if (!dir_emit(ctx, ZFS_SNAPDIR_NAME, strlen(ZFS_SNAPDIR_NAME), ZFSCTL_INO_SNAPDIR, DT_DIR)) goto out; ctx->pos++; } if (ctx->pos == 3) { if (!dir_emit(ctx, ZFS_SHAREDIR_NAME, strlen(ZFS_SHAREDIR_NAME), ZFSCTL_INO_SHARES, DT_DIR)) goto out; ctx->pos++; } out: ZFS_EXIT(zsb); return (error); }
bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, unsigned int start_pos) { unsigned char d_type = DT_UNKNOWN; unsigned int bit_pos; struct f2fs_dir_entry *de = NULL; bit_pos = ((unsigned long)ctx->pos % d->max); while (bit_pos < d->max) { bit_pos = find_next_bit_le(d->bitmap, d->max, bit_pos); if (bit_pos >= d->max) break; de = &d->dentry[bit_pos]; if (de->file_type < F2FS_FT_MAX) d_type = f2fs_filetype_table[de->file_type]; else d_type = DT_UNKNOWN; if (!dir_emit(ctx, d->filename[bit_pos], le16_to_cpu(de->name_len), le32_to_cpu(de->ino), d_type)) return true; bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); ctx->pos = start_pos + bit_pos; } return false; }
static Bool HgfsReaddirFillEntry(filldir_t filldirCb, // IN: System filler callback void *filldirCtx, // IN/OUT: System filler context char *entryName, // IN: entry name uint32 entryNameLength, // IN: max name length loff_t entryPos, // IN: position = (ctx-pos) ino_t entryIno, // IN: inode entry number uint32 entryType) // IN: entry type { struct dir_context *ctx = filldirCtx; Bool result; ASSERT(filldirCb == NULL); /* Contained within the context structure. */ ASSERT(ctx != NULL); ASSERT(ctx->pos == entryPos); ASSERT(entryName != NULL); ASSERT(entryNameLength != 0); LOG(6, (KERN_DEBUG "VMware hgfs: %s: dir_emit(%s, %u, %Lu)\n", __func__, entryName, entryNameLength, ctx->pos)); result = dir_emit(ctx, /* filldir callback struct */ entryName, /* name of dirent */ entryNameLength, /* length of name */ entryIno, /* inode number (0 makes it not show) */ entryType); /* type of dirent */ return result; }
static int f2fs_readdir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); unsigned long npages = dir_blocks(inode); unsigned int bit_pos = 0; struct f2fs_dentry_block *dentry_blk = NULL; struct f2fs_dir_entry *de = NULL; struct page *dentry_page = NULL; struct file_ra_state *ra = &file->f_ra; unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK); unsigned char d_type = DT_UNKNOWN; bit_pos = ((unsigned long)ctx->pos % NR_DENTRY_IN_BLOCK); /* readahead for multi pages of dir */ if (npages - n > 1 && !ra_has_index(ra, n)) page_cache_sync_readahead(inode->i_mapping, ra, file, n, min(npages - n, (pgoff_t)MAX_DIR_RA_PAGES)); for (; n < npages; n++) { dentry_page = get_lock_data_page(inode, n); if (IS_ERR(dentry_page)) continue; dentry_blk = kmap(dentry_page); while (bit_pos < NR_DENTRY_IN_BLOCK) { bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, NR_DENTRY_IN_BLOCK, bit_pos); if (bit_pos >= NR_DENTRY_IN_BLOCK) break; de = &dentry_blk->dentry[bit_pos]; if (de->file_type < F2FS_FT_MAX) d_type = f2fs_filetype_table[de->file_type]; else d_type = DT_UNKNOWN; if (!dir_emit(ctx, dentry_blk->filename[bit_pos], le16_to_cpu(de->name_len), le32_to_cpu(de->ino), d_type)) goto stop; bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); ctx->pos = n * NR_DENTRY_IN_BLOCK + bit_pos; } bit_pos = 0; ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK; kunmap(dentry_page); f2fs_put_page(dentry_page, 1); dentry_page = NULL; } stop: if (dentry_page && !IS_ERR(dentry_page)) { kunmap(dentry_page); f2fs_put_page(dentry_page, 1); } return 0; }
/* Inspired by generic filldir in fs/readdir.c */ static int ecryptfs_filldir(void *dirent, const char *lower_name, int lower_namelen, loff_t offset, u64 ino, unsigned int d_type) { struct ecryptfs_getdents_callback *buf = (struct ecryptfs_getdents_callback *)dirent; size_t name_size; char *name; int rc; buf->filldir_called++; rc = ecryptfs_decode_and_decrypt_filename(&name, &name_size, buf->sb, lower_name, lower_namelen); if (rc) { printk(KERN_ERR "%s: Error attempting to decode and decrypt " "filename [%s]; rc = [%d]\n", __func__, lower_name, rc); goto out; } buf->caller->pos = buf->ctx.pos; rc = !dir_emit(buf->caller, name, name_size, ino, d_type); kfree(name); if (!rc) buf->entries_written++; out: return rc; }
static int sysfs_readdir(struct file *file, struct dir_context *ctx) { struct dentry *dentry = file->f_path.dentry; struct sysfs_dirent *parent_sd = dentry->d_fsdata; struct sysfs_dirent *pos = file->private_data; enum kobj_ns_type type; const void *ns; type = sysfs_ns_type(parent_sd); ns = sysfs_info(dentry->d_sb)->ns[type]; if (!dir_emit_dots(file, ctx)) return 0; mutex_lock(&sysfs_mutex); for (pos = sysfs_dir_pos(ns, parent_sd, ctx->pos, pos); pos; pos = sysfs_dir_next_pos(ns, parent_sd, ctx->pos, pos)) { const char *name = pos->s_name; unsigned int type = dt_type(pos); int len = strlen(name); ino_t ino = pos->s_ino; ctx->pos = pos->s_hash; file->private_data = sysfs_get(pos); mutex_unlock(&sysfs_mutex); if (!dir_emit(ctx, name, len, ino, type)) return 0; mutex_lock(&sysfs_mutex); } mutex_unlock(&sysfs_mutex); file->private_data = NULL; ctx->pos = INT_MAX; return 0; }
/* * read the entries from a directory */ static int romfs_readdir(struct file *file, struct dir_context *ctx) { struct inode *i = file_inode(file); struct romfs_inode ri; unsigned long offset, maxoff; int j, ino, nextfh; char fsname[ROMFS_MAXFN]; /* XXX dynamic? */ int ret; maxoff = romfs_maxsize(i->i_sb); offset = ctx->pos; if (!offset) { offset = i->i_ino & ROMFH_MASK; ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE); if (ret < 0) goto out; offset = be32_to_cpu(ri.spec) & ROMFH_MASK; } /* Not really failsafe, but we are read-only... */ for (;;) { if (!offset || offset >= maxoff) { offset = maxoff; ctx->pos = offset; goto out; } ctx->pos = offset; /* Fetch inode info */ ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE); if (ret < 0) goto out; j = romfs_dev_strnlen(i->i_sb, offset + ROMFH_SIZE, sizeof(fsname) - 1); if (j < 0) goto out; ret = romfs_dev_read(i->i_sb, offset + ROMFH_SIZE, fsname, j); if (ret < 0) goto out; fsname[j] = '\0'; ino = offset; nextfh = be32_to_cpu(ri.next); if ((nextfh & ROMFH_TYPE) == ROMFH_HRD) ino = be32_to_cpu(ri.spec); if (!dir_emit(ctx, fsname, j, ino, romfs_dtype_table[nextfh & ROMFH_TYPE])) goto out; offset = nextfh & ROMFH_MASK; } out: return 0; }
static int simplefs_readdir(struct file *filp, void *dirent, filldir_t filldir) #endif { loff_t pos; struct inode *inode; struct super_block *sb; struct buffer_head *bh; struct simplefs_inode *sfs_inode; struct simplefs_dir_record *record; int i; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) pos = ctx->pos; #else pos = filp->f_pos; #endif inode = filp->f_dentry->d_inode; sb = inode->i_sb; if (pos) { /* FIXME: We use a hack of reading pos to figure if we have filled in all data. * We should probably fix this to work in a cursor based model and * use the tokens correctly to not fill too many data in each cursor based call */ return 0; } sfs_inode = SIMPLEFS_INODE(inode); if (unlikely(!S_ISDIR(sfs_inode->mode))) { printk(KERN_ERR "inode [%llu][%lu] for fs object [%s] not a directory\n", sfs_inode->inode_no, inode->i_ino, filp->f_dentry->d_name.name); return -ENOTDIR; } bh = sb_bread(sb, sfs_inode->data_block_number); BUG_ON(!bh); record = (struct simplefs_dir_record *)bh->b_data; for (i = 0; i < sfs_inode->dir_children_count; i++) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) dir_emit(ctx, record->filename, SIMPLEFS_FILENAME_MAXLEN, record->inode_no, DT_UNKNOWN); ctx->pos += sizeof(struct simplefs_dir_record); #else filldir(dirent, record->filename, SIMPLEFS_FILENAME_MAXLEN, pos, record->inode_no, DT_UNKNOWN); filp->f_pos += sizeof(struct simplefs_dir_record); #endif pos += sizeof(struct simplefs_dir_record); record++; } brelse(bh); return 0; }
static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx) { bool over; struct p9_wstat st; int err = 0; struct p9_fid *fid; int buflen; int reclen = 0; struct p9_rdir *rdir; struct kvec kvec; p9_debug(P9_DEBUG_VFS, "name %pD\n", file); fid = file->private_data; buflen = fid->clnt->msize - P9_IOHDRSZ; rdir = v9fs_alloc_rdir_buf(file, buflen); if (!rdir) return -ENOMEM; kvec.iov_base = rdir->buf; kvec.iov_len = buflen; while (1) { if (rdir->tail == rdir->head) { struct iov_iter to; int n; iov_iter_kvec(&to, READ | ITER_KVEC, &kvec, 1, buflen); n = p9_client_read(file->private_data, ctx->pos, &to, &err); if (err) return err; rdir->head = 0; rdir->tail = n; } while (rdir->head < rdir->tail) { p9stat_init(&st); err = p9stat_read(fid->clnt, rdir->buf + rdir->head, rdir->tail - rdir->head, &st); if (err) { p9_debug(P9_DEBUG_VFS, "returned %d\n", err); p9stat_free(&st); return -EIO; } reclen = st.size+2; over = !dir_emit(ctx, st.name, strlen(st.name), v9fs_qid2ino(&st.qid), dt_type(&st)); p9stat_free(&st); if (over) return 0; rdir->head += reclen; ctx->pos += reclen; } } }
static int dedupfs_iterate(struct file *filp, struct dir_context *ctx) { loff_t pos; struct inode *inode; struct super_block *sb; struct buffer_head *bh; struct dedupfs_inode *dfs_inode; struct dedupfs_file_record *record; pos = ctx->pos; inode = file_inode(filp); sb = inode->i_sb; printk(KERN_INFO "We are inside iterate. The pos[%lu], inode_number[%lu]\n", (long unsigned int)pos, inode->i_ino); if (pos) { printk(KERN_INFO "pos seem to be non-zero which means " "we have already filled in all the details\n"); return 0; } dfs_inode = DEDUPFS_INODE(inode); if(!S_ISDIR(dfs_inode->mode)) { printk(KERN_ERR "inode [%d] is not a directory\n", dfs_inode->inode_no); return -ENOTDIR; } bh = (struct buffer_head *)sb_bread(sb, dfs_inode->data_block_number); record = (struct dedupfs_file_record *)bh->b_data; for(int i = 0; i < dfs_inode->dir_children_count; i++) { printk(KERN_INFO "Got filename: %s\n", record->filename); dir_emit(ctx, record->filename, DEDUPFS_FILENAME_MAXLEN, record->inode_no, DT_UNKNOWN); ctx->pos += sizeof(struct dedupfs_file_record); record++; } brelse(bh); return 0; }
/** * v9fs_dir_readdir_dotl - iterate through a directory * @file: opened file structure * @ctx: actor we feed the entries to * */ static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx) { int err = 0; struct p9_fid *fid; int buflen; struct p9_rdir *rdir; struct p9_dirent curdirent; p9_debug(P9_DEBUG_VFS, "name %pD\n", file); fid = file->private_data; buflen = fid->clnt->msize - P9_READDIRHDRSZ; rdir = v9fs_alloc_rdir_buf(file, buflen); if (!rdir) return -ENOMEM; while (1) { if (rdir->tail == rdir->head) { err = p9_client_readdir(fid, rdir->buf, buflen, ctx->pos); if (err <= 0) return err; rdir->head = 0; rdir->tail = err; } while (rdir->head < rdir->tail) { err = p9dirent_read(fid->clnt, rdir->buf + rdir->head, rdir->tail - rdir->head, &curdirent); if (err < 0) { p9_debug(P9_DEBUG_VFS, "returned %d\n", err); return -EIO; } if (!dir_emit(ctx, curdirent.d_name, strlen(curdirent.d_name), v9fs_qid2ino(&curdirent.qid), curdirent.d_type)) return 0; ctx->pos = curdirent.d_off; rdir->head += err; } } }
/* Inspired by generic filldir in fs/readdir.c */ static int tierfs_filldir(void *dirent, const char *lower_name, int lower_namelen, loff_t offset, u64 ino, unsigned int d_type) { struct tierfs_getdents_callback *buf = (struct tierfs_getdents_callback *)dirent; int rc; TRACE_ENTRY(); buf->filldir_called++; buf->caller->pos = buf->ctx.pos; rc = !dir_emit(buf->caller, lower_name, lower_namelen, ino, d_type); if (!rc) buf->entries_written++; TRACE_EXIT(); return rc; }
static int nvmm_readdir(struct file *file, struct dir_context *ctx) { loff_t pos = ctx->pos; int err = 0; struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; unsigned long offset = pos && ~PAGE_CACHE_MASK; unsigned char *types = NULL; char *start_addr, *end_addr; struct nvmm_dir_entry *nde; types = nvmm_filetype_table; /*establish mapping*/ err = nvmm_establish_mapping(inode); if(pos >= inode->i_size) goto final; if(file->f_version != inode->i_version){ if(offset){ offset = 0; ctx->pos = (loff_t)(NVMM_I(inode)->i_virt_addr); } file->f_version = inode->i_version; } start_addr = NVMM_I(inode)->i_virt_addr; end_addr = start_addr + inode->i_size; if(start_addr >= end_addr) goto final; nde = (struct nvmm_dir_entry *)start_addr; for(;(char *)nde < end_addr;nde = nvmm_next_entry(nde)){ if(nde->rec_len == 0){ nvmm_error(sb, __FUNCTION__, "zero-length directory entry\n"); err = -EIO; goto final; } if(nde->inode){ unsigned char d_type = DT_UNKNOWN; if(types && nde->file_type < NVMM_FT_MAX) d_type = types[nde->file_type]; if(!dir_emit(ctx, nde->name, nde->name_len, le64_to_cpu(nde->inode),d_type)) goto final; }
static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file, struct dir_context *ctx, u64 attr_version) { struct fuse_direntplus *direntplus; struct fuse_dirent *dirent; size_t reclen; int over = 0; int ret; while (nbytes >= FUSE_NAME_OFFSET_DIRENTPLUS) { direntplus = (struct fuse_direntplus *) buf; dirent = &direntplus->dirent; reclen = FUSE_DIRENTPLUS_SIZE(direntplus); if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX) return -EIO; if (reclen > nbytes) break; if (memchr(dirent->name, '/', dirent->namelen) != NULL) return -EIO; if (!over) { /* We fill entries into dstbuf only as much as it can hold. But we still continue iterating over remaining entries to link them. If not, we need to send a FORGET for each of those which we did not link. */ over = !dir_emit(ctx, dirent->name, dirent->namelen, dirent->ino, dirent->type); ctx->pos = dirent->off; } buf += reclen; nbytes -= reclen; ret = fuse_direntplus_link(file, direntplus, attr_version); if (ret) fuse_force_forget(file, direntplus->entry_out.nodeid); } return 0; }
static int sysv_readdir(struct file *file, struct dir_context *ctx) { unsigned long pos = ctx->pos; struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; unsigned long npages = dir_pages(inode); unsigned offset; unsigned long n; ctx->pos = pos = (pos + SYSV_DIRSIZE-1) & ~(SYSV_DIRSIZE-1); if (pos >= inode->i_size) return 0; offset = pos & ~PAGE_MASK; n = pos >> PAGE_SHIFT; for ( ; n < npages; n++, offset = 0) { char *kaddr, *limit; struct sysv_dir_entry *de; struct page *page = dir_get_page(inode, n); if (IS_ERR(page)) continue; kaddr = (char *)page_address(page); de = (struct sysv_dir_entry *)(kaddr+offset); limit = kaddr + PAGE_SIZE - SYSV_DIRSIZE; for ( ;(char*)de <= limit; de++, ctx->pos += sizeof(*de)) { char *name = de->name; if (!de->inode) continue; if (!dir_emit(ctx, name, strnlen(name,SYSV_NAMELEN), fs16_to_cpu(SYSV_SB(sb), de->inode), DT_UNKNOWN)) { dir_put_page(page); return 0; } } dir_put_page(page); } return 0; }
/* FIXME: readdir currently has it's own dir_walk code. I don't see a good * way to combine the two copies */ static int logfs_readdir(struct file *file, struct dir_context *ctx) { struct inode *dir = file_inode(file); loff_t pos; struct page *page; struct logfs_disk_dentry *dd; if (ctx->pos < 0) return -EINVAL; if (!dir_emit_dots(file, ctx)) return 0; pos = ctx->pos - 2; BUG_ON(pos < 0); for (;; pos++, ctx->pos++) { bool full; if (beyond_eof(dir, pos)) break; if (!logfs_exist_block(dir, pos)) { /* deleted dentry */ pos = dir_seek_data(dir, pos); continue; } page = read_cache_page(dir->i_mapping, pos, (filler_t *)logfs_readpage, NULL); if (IS_ERR(page)) return PTR_ERR(page); dd = kmap(page); BUG_ON(dd->namelen == 0); full = !dir_emit(ctx, (char *)dd->name, be16_to_cpu(dd->namelen), be64_to_cpu(dd->ino), dd->type); kunmap(page); page_cache_release(page); if (full) break; } return 0; }
static int zpl_snapdir_iterate(struct file *filp, struct dir_context *ctx) { zfs_sb_t *zsb = ITOZSB(filp->f_path.dentry->d_inode); fstrans_cookie_t cookie; char snapname[MAXNAMELEN]; boolean_t case_conflict; uint64_t id, pos; int error = 0; ZFS_ENTER(zsb); cookie = spl_fstrans_mark(); if (!dir_emit_dots(filp, ctx)) goto out; pos = ctx->pos; while (error == 0) { dsl_pool_config_enter(dmu_objset_pool(zsb->z_os), FTAG); error = -dmu_snapshot_list_next(zsb->z_os, MAXNAMELEN, snapname, &id, &pos, &case_conflict); dsl_pool_config_exit(dmu_objset_pool(zsb->z_os), FTAG); if (error) goto out; if (!dir_emit(ctx, snapname, strlen(snapname), ZFSCTL_INO_SHARES - id, DT_DIR)) goto out; ctx->pos = pos; } out: spl_fstrans_unmark(cookie); ZFS_EXIT(zsb); if (error == -ENOENT) return (0); return (error); }
static int parse_dirfile(char *buf, size_t nbytes, struct file *file, struct dir_context *ctx) { while (nbytes >= FUSE_NAME_OFFSET) { struct fuse_dirent *dirent = (struct fuse_dirent *) buf; size_t reclen = FUSE_DIRENT_SIZE(dirent); if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX) return -EIO; if (reclen > nbytes) break; if (!dir_emit(ctx, dirent->name, dirent->namelen, dirent->ino, dirent->type)) break; buf += reclen; nbytes -= reclen; ctx->pos = dirent->off; } return 0; }
static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx) { struct dentry *dentry = file->f_path.dentry; struct kernfs_node *parent = dentry->d_fsdata; struct kernfs_node *pos = file->private_data; const void *ns = NULL; if (!dir_emit_dots(file, ctx)) return 0; mutex_lock(&kernfs_mutex); if (kernfs_ns_enabled(parent)) ns = kernfs_info(dentry->d_sb)->ns; for (pos = kernfs_dir_pos(ns, parent, ctx->pos, pos); pos; pos = kernfs_dir_next_pos(ns, parent, ctx->pos, pos)) { const char *name = pos->name; unsigned int type = dt_type(pos); int len = strlen(name); ino_t ino = pos->ino; ctx->pos = pos->hash; file->private_data = pos; kernfs_get(pos); mutex_unlock(&kernfs_mutex); if (!dir_emit(ctx, name, len, ino, type)) return 0; mutex_lock(&kernfs_mutex); } mutex_unlock(&kernfs_mutex); file->private_data = NULL; ctx->pos = INT_MAX; return 0; }
static int jffs2_readdir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_full_dirent *fd; unsigned long curofs = 1; jffs2_dbg(1, "jffs2_readdir() for dir_i #%lu\n", inode->i_ino); if (!dir_emit_dots(file, ctx)) return 0; mutex_lock(&f->sem); for (fd = f->dents; fd; fd = fd->next) { curofs++; /* First loop: curofs = 2; pos = 2 */ if (curofs < ctx->pos) { jffs2_dbg(2, "Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n", fd->name, fd->ino, fd->type, curofs, (unsigned long)ctx->pos); continue; } if (!fd->ino) { jffs2_dbg(2, "Skipping deletion dirent \"%s\"\n", fd->name); ctx->pos++; continue; } jffs2_dbg(2, "Dirent %ld: \"%s\", ino #%u, type %d\n", (unsigned long)ctx->pos, fd->name, fd->ino, fd->type); if (!dir_emit(ctx, fd->name, strlen(fd->name), fd->ino, fd->type)) break; ctx->pos++; } mutex_unlock(&f->sem); return 0; }
static int cifs_filldir(char *find_entry, struct file *file, struct dir_context *ctx, char *scratch_buf, unsigned int max_len) { struct cifsFileInfo *file_info = file->private_data; struct super_block *sb = file_inode(file)->i_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_dirent de = { NULL, }; struct cifs_fattr fattr; struct qstr name; int rc = 0; ino_t ino; rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level, file_info->srch_inf.unicode); if (rc) return rc; if (de.namelen > max_len) { cifs_dbg(VFS, "bad search response length %zd past smb end\n", de.namelen); return -EINVAL; } /* skip . and .. since we added them first */ if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode)) return 0; if (file_info->srch_inf.unicode) { struct nls_table *nlt = cifs_sb->local_nls; int map_type; map_type = cifs_remap(cifs_sb); name.name = scratch_buf; name.len = cifs_from_utf16((char *)name.name, (__le16 *)de.name, UNICODE_NAME_MAX, min_t(size_t, de.namelen, (size_t)max_len), nlt, map_type); name.len -= nls_nullsize(nlt); } else { name.name = de.name; name.len = de.namelen; } switch (file_info->srch_inf.info_level) { case SMB_FIND_FILE_UNIX: cifs_unix_basic_to_fattr(&fattr, &((FILE_UNIX_INFO *)find_entry)->basic, cifs_sb); break; case SMB_FIND_FILE_INFO_STANDARD: cifs_std_info_to_fattr(&fattr, (FIND_FILE_STANDARD_INFO *)find_entry, cifs_sb); break; default: cifs_dir_info_to_fattr(&fattr, (FILE_DIRECTORY_INFO *)find_entry, cifs_sb); break; } if (de.ino && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { fattr.cf_uniqueid = de.ino; } else { fattr.cf_uniqueid = iunique(sb, ROOT_I); cifs_autodisable_serverino(cifs_sb); } if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) && couldbe_mf_symlink(&fattr)) /* * trying to get the type and mode can be slow, * so just call those regular files for now, and mark * for reval */ fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; cifs_prime_dcache(file->f_path.dentry, &name, &fattr); ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid); return !dir_emit(ctx, name.name, name.len, ino, fattr.cf_dtype); }
/** * vxfs_readdir - read a directory * @fp: the directory to read * @retp: return buffer * @filler: filldir callback * * Description: * vxfs_readdir fills @retp with directory entries from @fp * using the VFS supplied callback @filler. * * Returns: * Zero. */ static int vxfs_readdir(struct file *fp, struct dir_context *ctx) { struct inode *ip = file_inode(fp); struct super_block *sbp = ip->i_sb; u_long bsize = sbp->s_blocksize; loff_t pos, limit; struct vxfs_sb_info *sbi = VXFS_SBI(sbp); if (ctx->pos == 0) { if (!dir_emit_dot(fp, ctx)) goto out; ctx->pos++; } if (ctx->pos == 1) { if (!dir_emit(ctx, "..", 2, VXFS_INO(ip)->vii_dotdot, DT_DIR)) goto out; ctx->pos++; } limit = VXFS_DIRROUND(ip->i_size); if (ctx->pos > limit) goto out; pos = ctx->pos & ~3L; while (pos < limit) { struct page *pp; char *kaddr; int pg_ofs = pos & ~PAGE_MASK; int rc = 0; pp = vxfs_get_page(ip->i_mapping, pos >> PAGE_SHIFT); if (IS_ERR(pp)) return -ENOMEM; kaddr = (char *)page_address(pp); while (pg_ofs < PAGE_SIZE && pos < limit) { struct vxfs_direct *de; if ((pos & (bsize - 1)) < 4) { struct vxfs_dirblk *dbp = (struct vxfs_dirblk *) (kaddr + (pos & ~PAGE_MASK)); int overhead = VXFS_DIRBLKOV(sbi, dbp); pos += overhead; pg_ofs += overhead; } de = (struct vxfs_direct *)(kaddr + pg_ofs); if (!de->d_reclen) { pos += bsize - 1; pos &= ~(bsize - 1); break; } pg_ofs += fs16_to_cpu(sbi, de->d_reclen); pos += fs16_to_cpu(sbi, de->d_reclen); if (!de->d_ino) continue; rc = dir_emit(ctx, de->d_name, fs16_to_cpu(sbi, de->d_namelen), fs32_to_cpu(sbi, de->d_ino), DT_UNKNOWN); if (!rc) { /* the dir entry was not read, fix pos. */ pos -= fs16_to_cpu(sbi, de->d_reclen); break; } } vxfs_put_page(pp); if (!rc) break; } ctx->pos = pos | 2; out: return 0; }
/* * deal with one block in an AFS directory */ static int afs_dir_iterate_block(struct dir_context *ctx, union afs_dir_block *block, unsigned blkoff) { union afs_dirent *dire; unsigned offset, next, curr; size_t nlen; int tmp; _enter("%u,%x,%p,,",(unsigned)ctx->pos,blkoff,block); curr = (ctx->pos - blkoff) / sizeof(union afs_dirent); /* walk through the block, an entry at a time */ for (offset = AFS_DIRENT_PER_BLOCK - block->pagehdr.nentries; offset < AFS_DIRENT_PER_BLOCK; offset = next ) { next = offset + 1; /* skip entries marked unused in the bitmap */ if (!(block->pagehdr.bitmap[offset / 8] & (1 << (offset % 8)))) { _debug("ENT[%Zu.%u]: unused", blkoff / sizeof(union afs_dir_block), offset); if (offset >= curr) ctx->pos = blkoff + next * sizeof(union afs_dirent); continue; } /* got a valid entry */ dire = &block->dirents[offset]; nlen = strnlen(dire->u.name, sizeof(*block) - offset * sizeof(union afs_dirent)); _debug("ENT[%Zu.%u]: %s %Zu \"%s\"", blkoff / sizeof(union afs_dir_block), offset, (offset < curr ? "skip" : "fill"), nlen, dire->u.name); /* work out where the next possible entry is */ for (tmp = nlen; tmp > 15; tmp -= sizeof(union afs_dirent)) { if (next >= AFS_DIRENT_PER_BLOCK) { _debug("ENT[%Zu.%u]:" " %u travelled beyond end dir block" " (len %u/%Zu)", blkoff / sizeof(union afs_dir_block), offset, next, tmp, nlen); return -EIO; } if (!(block->pagehdr.bitmap[next / 8] & (1 << (next % 8)))) { _debug("ENT[%Zu.%u]:" " %u unmarked extension (len %u/%Zu)", blkoff / sizeof(union afs_dir_block), offset, next, tmp, nlen); return -EIO; } _debug("ENT[%Zu.%u]: ext %u/%Zu", blkoff / sizeof(union afs_dir_block), next, tmp, nlen); next++; } /* skip if starts before the current position */ if (offset < curr) continue; /* found the next entry */ if (!dir_emit(ctx, dire->u.name, nlen, ntohl(dire->u.vnode), ctx->actor == afs_lookup_filldir ? ntohl(dire->u.unique) : DT_UNKNOWN)) { _leave(" = 0 [full]"); return 0; } ctx->pos = blkoff + next * sizeof(union afs_dirent); } _leave(" = 1 [more]"); return 1; }
static int affs_readdir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; struct buffer_head *dir_bh = NULL; struct buffer_head *fh_bh = NULL; unsigned char *name; int namelen; u32 i; int hash_pos; int chain_pos; u32 ino; int error = 0; pr_debug("%s(ino=%lu,f_pos=%llx)\n", __func__, inode->i_ino, ctx->pos); if (ctx->pos < 2) { file->private_data = (void *)0; if (!dir_emit_dots(file, ctx)) return 0; } affs_lock_dir(inode); chain_pos = (ctx->pos - 2) & 0xffff; hash_pos = (ctx->pos - 2) >> 16; if (chain_pos == 0xffff) { affs_warning(sb, "readdir", "More than 65535 entries in chain"); chain_pos = 0; hash_pos++; ctx->pos = ((hash_pos << 16) | chain_pos) + 2; } dir_bh = affs_bread(sb, inode->i_ino); if (!dir_bh) goto out_unlock_dir; /* If the directory hasn't changed since the last call to readdir(), * we can jump directly to where we left off. */ ino = (u32)(long)file->private_data; if (ino && file->f_version == inode->i_version) { pr_debug("readdir() left off=%d\n", ino); goto inside; } ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]); for (i = 0; ino && i < chain_pos; i++) { fh_bh = affs_bread(sb, ino); if (!fh_bh) { affs_error(sb, "readdir","Cannot read block %d", i); error = -EIO; goto out_brelse_dir; } ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain); affs_brelse(fh_bh); fh_bh = NULL; } if (ino) goto inside; hash_pos++; for (; hash_pos < AFFS_SB(sb)->s_hashsize; hash_pos++) { ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]); if (!ino) continue; ctx->pos = (hash_pos << 16) + 2; inside: do { fh_bh = affs_bread(sb, ino); if (!fh_bh) { affs_error(sb, "readdir", "Cannot read block %d", ino); break; } namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], (u8)AFFSNAMEMAX); name = AFFS_TAIL(sb, fh_bh)->name + 1; pr_debug("readdir(): dir_emit(\"%.*s\", ino=%u), hash=%d, f_pos=%llx\n", namelen, name, ino, hash_pos, ctx->pos); if (!dir_emit(ctx, name, namelen, ino, DT_UNKNOWN)) goto done; ctx->pos++; ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain); affs_brelse(fh_bh); fh_bh = NULL; } while (ino); } done: file->f_version = inode->i_version; file->private_data = (void *)(long)ino; affs_brelse(fh_bh); out_brelse_dir: affs_brelse(dir_bh); out_unlock_dir: affs_unlock_dir(inode); return error; }
static int ubifs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { struct inode *inode; struct ubifs_info *c = dir->i_sb->s_fs_info; int err, sz_change = CALC_DENT_SIZE(dentry->d_name.len); struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1, .dirtied_ino = 1 }; struct ubifs_inode *dir_ui = ubifs_inode(dir); /* * Budget request settings: new inode, new direntry, changing the * parent directory inode. */ dbg_gen("dent '%pd', mode %#hx in dir ino %lu", dentry, mode, dir->i_ino); err = ubifs_budget_space(c, &req); if (err) return err; inode = ubifs_new_inode(c, dir, mode); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_budg; } err = ubifs_init_security(dir, inode, &dentry->d_name); if (err) goto out_inode; mutex_lock(&dir_ui->ui_mutex); dir->i_size += sz_change; dir_ui->ui_size = dir->i_size; dir->i_mtime = dir->i_ctime = inode->i_ctime; err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0); if (err) goto out_cancel; mutex_unlock(&dir_ui->ui_mutex); ubifs_release_budget(c, &req); insert_inode_hash(inode); d_instantiate(dentry, inode); return 0; out_cancel: dir->i_size -= sz_change; dir_ui->ui_size = dir->i_size; mutex_unlock(&dir_ui->ui_mutex); out_inode: make_bad_inode(inode); iput(inode); out_budg: ubifs_release_budget(c, &req); ubifs_err(c, "cannot create regular file, error %d", err); return err; } /** * vfs_dent_type - get VFS directory entry type. * @type: UBIFS directory entry type * * This function converts UBIFS directory entry type into VFS directory entry * type. */ static unsigned int vfs_dent_type(uint8_t type) { switch (type) { case UBIFS_ITYPE_REG: return DT_REG; case UBIFS_ITYPE_DIR: return DT_DIR; case UBIFS_ITYPE_LNK: return DT_LNK; case UBIFS_ITYPE_BLK: return DT_BLK; case UBIFS_ITYPE_CHR: return DT_CHR; case UBIFS_ITYPE_FIFO: return DT_FIFO; case UBIFS_ITYPE_SOCK: return DT_SOCK; default: BUG(); } return 0; } /* * The classical Unix view for directory is that it is a linear array of * (name, inode number) entries. Linux/VFS assumes this model as well. * Particularly, 'readdir()' call wants us to return a directory entry offset * which later may be used to continue 'readdir()'ing the directory or to * 'seek()' to that specific direntry. Obviously UBIFS does not really fit this * model because directory entries are identified by keys, which may collide. * * UBIFS uses directory entry hash value for directory offsets, so * 'seekdir()'/'telldir()' may not always work because of possible key * collisions. But UBIFS guarantees that consecutive 'readdir()' calls work * properly by means of saving full directory entry name in the private field * of the file description object. * * This means that UBIFS cannot support NFS which requires full * 'seekdir()'/'telldir()' support. */ static int ubifs_readdir(struct file *file, struct dir_context *ctx) { int err; struct qstr nm; union ubifs_key key; struct ubifs_dent_node *dent; struct inode *dir = file_inode(file); struct ubifs_info *c = dir->i_sb->s_fs_info; dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, ctx->pos); if (ctx->pos > UBIFS_S_KEY_HASH_MASK || ctx->pos == 2) /* * The directory was seek'ed to a senseless position or there * are no more entries. */ return 0; if (file->f_version == 0) { /* * The file was seek'ed, which means that @file->private_data * is now invalid. This may also be just the first * 'ubifs_readdir()' invocation, in which case * @file->private_data is NULL, and the below code is * basically a no-op. */ kfree(file->private_data); file->private_data = NULL; } /* * 'generic_file_llseek()' unconditionally sets @file->f_version to * zero, and we use this for detecting whether the file was seek'ed. */ file->f_version = 1; /* File positions 0 and 1 correspond to "." and ".." */ if (ctx->pos < 2) { ubifs_assert(!file->private_data); if (!dir_emit_dots(file, ctx)) return 0; /* Find the first entry in TNC and save it */ lowest_dent_key(c, &key, dir->i_ino); nm.name = NULL; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } ctx->pos = key_hash_flash(c, &dent->key); file->private_data = dent; } dent = file->private_data; if (!dent) { /* * The directory was seek'ed to and is now readdir'ed. * Find the entry corresponding to @ctx->pos or the closest one. */ dent_key_init_hash(c, &key, dir->i_ino, ctx->pos); nm.name = NULL; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } ctx->pos = key_hash_flash(c, &dent->key); file->private_data = dent; } while (1) { dbg_gen("feed '%s', ino %llu, new f_pos %#x", dent->name, (unsigned long long)le64_to_cpu(dent->inum), key_hash_flash(c, &dent->key)); ubifs_assert(le64_to_cpu(dent->ch.sqnum) > ubifs_inode(dir)->creat_sqnum); nm.len = le16_to_cpu(dent->nlen); if (!dir_emit(ctx, dent->name, nm.len, le64_to_cpu(dent->inum), vfs_dent_type(dent->type))) return 0; /* Switch to the next entry */ key_read(c, &dent->key, &key); nm.name = dent->name; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } kfree(file->private_data); ctx->pos = key_hash_flash(c, &dent->key); file->private_data = dent; cond_resched(); } out: if (err != -ENOENT) { ubifs_err(c, "cannot find next direntry, error %d", err); return err; } kfree(file->private_data); file->private_data = NULL; /* 2 is a special value indicating that there are no more direntries */ ctx->pos = 2; return 0; }
/* * routine called when the VFS needs to read the directory contents * * @file: the VFS file structure of the directory * @ctx: directory context * * return: 0 on success, error code otherwise */ static int wtfs_iterate(struct file * file, struct dir_context * ctx) { struct inode * dir_vi = file_inode(file); struct super_block * vsb = dir_vi->i_sb; struct wtfs_inode_info * info = WTFS_INODE_INFO(dir_vi); struct wtfs_dir_block * block = NULL; struct buffer_head * bh = NULL; uint64_t count, offset, next, inode_no; char * filename = NULL; int i, j, k; int ret = -EINVAL; /* calculate how many entries we have counted, including null entries */ count = ctx->pos / sizeof(struct wtfs_dentry); offset = ctx->pos % sizeof(struct wtfs_dentry); if (offset != 0) { wtfs_error("bad position %llu at %s:%lu\n", ctx->pos, dir_vi->i_sb->s_id, dir_vi->i_ino); goto error; } /* do iterate */ next = info->first_block; i = 0; /* valid entry counter */ j = 0; /* total entry counter, including null ones */ while (next != 0) { if ((bh = sb_bread(vsb, next)) == NULL) { wtfs_error("unable to read the block %llu\n", next); goto error; } block = (struct wtfs_dir_block *)bh->b_data; for (k = 0; k < WTFS_DENTRY_COUNT_PER_BLOCK; ++k) { inode_no = wtfs64_to_cpu(block->entries[k].inode_no); filename = block->entries[k].filename; if (inode_no != 0) { if (j >= count && i < info->dir_entry_count) { wtfs_debug("emitting entry '%s' of " "inode %llu\n", filename, inode_no); if (dir_emit(ctx, filename, strnlen(filename, WTFS_FILENAME_MAX), inode_no, DT_UNKNOWN) == 0) { brelse(bh); return 0; } } ++i; } ++j; ctx->pos += sizeof(struct wtfs_dentry); } next = wtfs64_to_cpu(block->next); brelse(bh); } return 0; error: if (bh != NULL) { brelse(bh); } return ret; }
static int sf_dir_read(struct file *dir, void *opaque, filldir_t filldir) #endif { TRACE(); for (;;) { int err; ino_t fake_ino; loff_t sanity; char d_name[NAME_MAX]; int d_type = DT_UNKNOWN; err = sf_getdent(dir, d_name, &d_type); switch (err) { case 1: return 0; case 0: break; case -1: default: /* skip erroneous entry and proceed */ LogFunc(("sf_getdent error %d\n", err)); dir->f_pos += 1; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) ctx->pos += 1; #endif continue; } /* d_name now contains a valid entry name */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) sanity = ctx->pos + 0xbeef; #else sanity = dir->f_pos + 0xbeef; #endif fake_ino = sanity; if (sanity - fake_ino) { LogRelFunc(("can not compute ino\n")); return -EINVAL; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) if (!dir_emit(ctx, d_name, strlen(d_name), fake_ino, d_type)) { LogFunc(("dir_emit failed\n")); return 0; } #else err = filldir(opaque, d_name, strlen(d_name), dir->f_pos, fake_ino, d_type); if (err) { LogFunc(("filldir returned error %d\n", err)); /* Rely on the fact that filldir returns error only when it runs out of space in opaque */ return 0; } #endif dir->f_pos += 1; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) ctx->pos += 1; #endif } BUG(); }
/* * So this function is called when the volume is mkfsed with * dir_index disabled. In order to keep f_pos persistent * after we convert from an inlined dir to a blocked based, * we just pretend that we are a normal dir and return the * offset as if '.' and '..' really take place. * */ int ext4_read_inline_dir(struct file *file, struct dir_context *ctx, int *has_inline_data) { unsigned int offset, parent_ino; int i; struct ext4_dir_entry_2 *de; struct super_block *sb; struct inode *inode = file_inode(file); int ret, inline_size = 0; struct ext4_iloc iloc; void *dir_buf = NULL; int dotdot_offset, dotdot_size, extra_offset, extra_size; ret = ext4_get_inode_loc(inode, &iloc); if (ret) return ret; down_read(&EXT4_I(inode)->xattr_sem); if (!ext4_has_inline_data(inode)) { up_read(&EXT4_I(inode)->xattr_sem); *has_inline_data = 0; goto out; } inline_size = ext4_get_inline_size(inode); dir_buf = kmalloc(inline_size, GFP_NOFS); if (!dir_buf) { ret = -ENOMEM; up_read(&EXT4_I(inode)->xattr_sem); goto out; } ret = ext4_read_inline_data(inode, dir_buf, inline_size, &iloc); up_read(&EXT4_I(inode)->xattr_sem); if (ret < 0) goto out; ret = 0; sb = inode->i_sb; parent_ino = le32_to_cpu(((struct ext4_dir_entry_2 *)dir_buf)->inode); offset = ctx->pos; /* * dotdot_offset and dotdot_size is the real offset and * size for ".." and "." if the dir is block based while * the real size for them are only EXT4_INLINE_DOTDOT_SIZE. * So we will use extra_offset and extra_size to indicate them * during the inline dir iteration. */ dotdot_offset = EXT4_DIR_REC_LEN(1); dotdot_size = dotdot_offset + EXT4_DIR_REC_LEN(2); extra_offset = dotdot_size - EXT4_INLINE_DOTDOT_SIZE; extra_size = extra_offset + inline_size; /* * If the version has changed since the last call to * readdir(2), then we might be pointing to an invalid * dirent right now. Scan from the start of the inline * dir to make sure. */ if (file->f_version != inode->i_version) { for (i = 0; i < extra_size && i < offset;) { /* * "." is with offset 0 and * ".." is dotdot_offset. */ if (!i) { i = dotdot_offset; continue; } else if (i == dotdot_offset) { i = dotdot_size; continue; } /* for other entry, the real offset in * the buf has to be tuned accordingly. */ de = (struct ext4_dir_entry_2 *) (dir_buf + i - extra_offset); /* It's too expensive to do a full * dirent test each time round this * loop, but we do have to test at * least that it is non-zero. A * failure will be detected in the * dirent test below. */ if (ext4_rec_len_from_disk(de->rec_len, extra_size) < EXT4_DIR_REC_LEN(1)) break; i += ext4_rec_len_from_disk(de->rec_len, extra_size); } offset = i; ctx->pos = offset; file->f_version = inode->i_version; } while (ctx->pos < extra_size) { if (ctx->pos == 0) { if (!dir_emit(ctx, ".", 1, inode->i_ino, DT_DIR)) goto out; ctx->pos = dotdot_offset; continue; } if (ctx->pos == dotdot_offset) { if (!dir_emit(ctx, "..", 2, parent_ino, DT_DIR)) goto out; ctx->pos = dotdot_size; continue; } de = (struct ext4_dir_entry_2 *) (dir_buf + ctx->pos - extra_offset); if (ext4_check_dir_entry(inode, file, de, iloc.bh, dir_buf, extra_size, ctx->pos)) goto out; if (le32_to_cpu(de->inode)) { if (!dir_emit(ctx, de->name, de->name_len, le32_to_cpu(de->inode), get_dtype(sb, de->file_type))) goto out; } ctx->pos += ext4_rec_len_from_disk(de->rec_len, extra_size); } out: kfree(dir_buf); brelse(iloc.bh); return ret; }
int /* error */ xfs_dir2_sf_getdents( xfs_inode_t *dp, /* incore directory inode */ struct dir_context *ctx) { int i; /* shortform entry number */ xfs_mount_t *mp; /* filesystem mount point */ xfs_dir2_dataptr_t off; /* current entry's offset */ xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ xfs_dir2_dataptr_t dot_offset; xfs_dir2_dataptr_t dotdot_offset; xfs_ino_t ino; mp = dp->i_mount; ASSERT(dp->i_df.if_flags & XFS_IFINLINE); /* * Give up if the directory is way too short. */ if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) { ASSERT(XFS_FORCED_SHUTDOWN(mp)); return XFS_ERROR(EIO); } ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); ASSERT(dp->i_df.if_u1.if_data != NULL); sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count)); /* * If the block number in the offset is out of range, we're done. */ if (xfs_dir2_dataptr_to_db(mp, ctx->pos) > mp->m_dirdatablk) return 0; /* * Precalculate offsets for . and .. as we will always need them. * * XXX(hch): the second argument is sometimes 0 and sometimes * mp->m_dirdatablk. */ dot_offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, XFS_DIR3_DATA_DOT_OFFSET(mp)); dotdot_offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, XFS_DIR3_DATA_DOTDOT_OFFSET(mp)); /* * Put . entry unless we're starting past it. */ if (ctx->pos <= dot_offset) { ctx->pos = dot_offset & 0x7fffffff; if (!dir_emit(ctx, ".", 1, dp->i_ino, DT_DIR)) return 0; } /* * Put .. entry unless we're starting past it. */ if (ctx->pos <= dotdot_offset) { ino = xfs_dir2_sf_get_parent_ino(sfp); ctx->pos = dotdot_offset & 0x7fffffff; if (!dir_emit(ctx, "..", 2, ino, DT_DIR)) return 0; } /* * Loop while there are more entries and put'ing works. */ sfep = xfs_dir2_sf_firstentry(sfp); for (i = 0; i < sfp->count; i++) { off = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, xfs_dir2_sf_get_offset(sfep)); if (ctx->pos > off) { sfep = xfs_dir2_sf_nextentry(sfp, sfep); continue; } ino = xfs_dir2_sfe_get_ino(sfp, sfep); ctx->pos = off & 0x7fffffff; if (!dir_emit(ctx, (char *)sfep->name, sfep->namelen, ino, DT_UNKNOWN)) return 0; sfep = xfs_dir2_sf_nextentry(sfp, sfep); } ctx->pos = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0) & 0x7fffffff; return 0; }