Login
[x]
Log in using an account from:
Fedora Account System
Red Hat Associate
Red Hat Customer
Or login using a Red Hat Bugzilla account
Forgot Password
Login:
Hide Forgot
Create an Account
Red Hat Bugzilla – Attachment 708389 Details for
Bug 850426
gfs2: Add xgetdents syscall to the kernel
[?]
New
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
|
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh83 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
This site requires JavaScript to be enabled to function correctly, please enable it.
[patch]
Newer version of patch with vector of pages storage
xgetdents-try8-03112013-vector-pages-based-first-bash.patch (text/plain), 47.58 KB, created by
Abhijith Das
on 2013-03-11 13:57:31 UTC
(
hide
)
Description:
Newer version of patch with vector of pages storage
Filename:
MIME Type:
Creator:
Abhijith Das
Created:
2013-03-11 13:57:31 UTC
Size:
47.58 KB
patch
obsolete
>diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl >index a85f407..35aeb58 100644 >--- a/arch/x86/syscalls/syscall_32.tbl >+++ b/arch/x86/syscalls/syscall_32.tbl >@@ -358,4 +358,5 @@ > 349 i386 kcmp sys_kcmp > 350 i386 finit_module sys_finit_module > 351 i386 xstat sys_xstat >-352 i386 fxstat sys_fxstat >\ No newline at end of file >+352 i386 fxstat sys_fxstat >+353 i386 xgetdents sys_xgetdents >diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl >index d9bac28..8b68501 100644 >--- a/arch/x86/syscalls/syscall_64.tbl >+++ b/arch/x86/syscalls/syscall_64.tbl >@@ -322,6 +322,7 @@ > 313 common finit_module sys_finit_module > 314 common xstat sys_xstat > 315 common fxstat sys_fxstat >+316 common xgetdents sys_xgetdents > > # > # x32-specific system call numbers start at 512 to avoid cache impact >diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c >index 7179478..f4697a6 100644 >--- a/fs/gfs2/dir.c >+++ b/fs/gfs2/dir.c >@@ -78,9 +78,6 @@ > > #define MAX_RA_BLOCKS 32 /* max read-ahead blocks */ > >-#define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1) >-#define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1)) >- > struct qstr gfs2_qdot __read_mostly; > struct qstr gfs2_qdotdot __read_mostly; > >@@ -1159,17 +1156,13 @@ out_kfree: > * lt: returns -1 > * eq: returns 0 > */ >- >-static int compare_dents(const void *a, const void *b) >+int compare_dents_i(const struct gfs2_dirent *dent_a, >+ const struct gfs2_dirent *dent_b) > { >- const struct gfs2_dirent *dent_a, *dent_b; > u32 hash_a, hash_b; > int ret = 0; > >- dent_a = *(const struct gfs2_dirent **)a; > hash_a = be32_to_cpu(dent_a->de_hash); >- >- dent_b = *(const struct gfs2_dirent **)b; > hash_b = be32_to_cpu(dent_b->de_hash); > > if (hash_a > hash_b) >@@ -1191,28 +1184,43 @@ static int compare_dents(const void *a, const void *b) > return ret; > } > >+static int compare_dents(const void *a, const void *b) >+{ >+ return compare_dents_i(*(const struct gfs2_dirent **)a, >+ *(const struct gfs2_dirent **)b); >+} >+ >+int process_dent(const struct gfs2_dirent *dent, loff_t off, void *opaque, >+ filldir_t filler) >+{ >+ return filler(opaque, (const char *)(dent + 1), >+ be16_to_cpu(dent->de_name_len), >+ off, be64_to_cpu(dent->de_inum.no_addr), >+ be16_to_cpu(dent->de_type)); >+} >+ > /** >- * do_filldir_main - read out directory entries >- * @dip: The GFS2 inode >- * @offset: The offset in the file to read from >- * @opaque: opaque data to pass to filldir >- * @filldir: The function to pass entries to >- * @darr: an array of struct gfs2_dirent pointers to read >+ * foreach_dent - run through list of dents and call pd_fn, keeping in >+ * mind hash collisions >+ * @offset : The offset in the file to read from >+ * @opaque : opaque data to pass to pd_fn >+ * @filldir: The filler function to psas to pd_fn >+ * @darr : an array of struct gfs2_dirent pointers to read > * @entries: the number of entries in darr >- * @copied: pointer to int that's non-zero if a entry has been copied out >+ * @copied : pointer to int that's non-zero if a entry has been copied out >+ * @pd_fn : The function to process a dentry > * > * Jump through some hoops to make sure that if there are hash collsions, > * they are read out at the beginning of a buffer. We want to minimize > * the possibility that they will fall into different readdir buffers or > * that someone will want to seek to that location. > * >- * Returns: errno, >0 on exception from filldir >+ * Returns: errno, >0 on exception from pd_fn > */ > >-static int do_filldir_main(struct gfs2_inode *dip, u64 *offset, >- void *opaque, filldir_t filldir, >- const struct gfs2_dirent **darr, u32 entries, >- int *copied) >+int foreach_dent(u64 *offset, void *opaque, filldir_t filldir, >+ const struct gfs2_dirent **darr, u32 entries, >+ int *copied, process_dent_t pd_fn) > { > const struct gfs2_dirent *dent, *dent_next; > u64 off, off_next; >@@ -1220,8 +1228,6 @@ static int do_filldir_main(struct gfs2_inode *dip, u64 *offset, > int run = 0; > int error = 0; > >- sort(darr, entries, sizeof(struct gfs2_dirent *), compare_dents, NULL); >- > dent_next = darr[0]; > off_next = be32_to_cpu(dent_next->de_hash); > off_next = gfs2_disk_hash2offset(off_next); >@@ -1250,26 +1256,48 @@ static int do_filldir_main(struct gfs2_inode *dip, u64 *offset, > continue; > *offset = off; > } >- >- error = filldir(opaque, (const char *)(dent + 1), >- be16_to_cpu(dent->de_name_len), >- off, be64_to_cpu(dent->de_inum.no_addr), >- be16_to_cpu(dent->de_type)); >+ error = pd_fn(dent, off, opaque, filldir); > if (error) > return 1; > > *copied = 1; > } >- > /* Increment the *offset by one, so the next time we come into the > do_filldir fxn, we get the next entry instead of the last one in the > current leaf */ > > (*offset)++; >- > return 0; > } > >+/** >+ * do_filldir_main - read out directory entries >+ * @dip: The GFS2 inode >+ * @offset: The offset in the file to read from >+ * @opaque: opaque data to pass to filldir >+ * @filldir: The function to pass entries to, if NULL call xreaddir_collect_dents >+ * @darr: an array of struct gfs2_dirent pointers to read >+ * @entries: the number of entries in darr >+ * @copied: pointer to int that's non-zero if a entry has been copied out >+ * >+ * Returns: 0 on success and non-zero on exception >+ */ >+ >+static int do_filldir_main(struct gfs2_inode *dip, u64 *offset, >+ void *opaque, filldir_t filldir, >+ const struct gfs2_dirent **darr, u32 entries, >+ int *copied) >+{ >+ sort(darr, entries, sizeof(struct gfs2_dirent *), compare_dents, NULL); >+ >+ /* >+ * If filldir is NULL, we got here through xreaddir and need to >+ * call the appropriate handler >+ */ >+ return foreach_dent(offset, opaque, filldir, darr, entries, copied, >+ filldir ? process_dent : gfs2_xrdir_collect_dents); >+} >+ > static void *gfs2_alloc_sort_buffer(unsigned size) > { > void *ptr = NULL; >diff --git a/fs/gfs2/dir.h b/fs/gfs2/dir.h >index 98c960b..3bb919b 100644 >--- a/fs/gfs2/dir.h >+++ b/fs/gfs2/dir.h >@@ -12,11 +12,71 @@ > > #include <linux/dcache.h> > #include <linux/crc32.h> >+#include "util.h" >+ >+#define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1) >+#define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1)) > > struct inode; > struct gfs2_inode; > struct gfs2_inum; > >+struct gfs2_xd_xattr { >+ unsigned int xa_reclen; >+ void *xa_vb_value_ptr; >+ unsigned long xa_value_len; >+ unsigned int xa_keylen; >+ char __pad[7]; >+ char xa_keyname[1]; >+}; >+ >+struct gfs2_xdirent { >+ u64 x_ino; >+ char x_type; >+ struct kstat x_kstat; >+ unsigned int x_xattr_count; >+ void *x_vb_xattr_arr_ptr; >+ unsigned int x_namelen; >+ char x_name[1]; >+}; >+ >+/* >+ * readdir ctx >+ */ >+struct gfs2_rddir_ctx { >+ unsigned int rc_flags; >+ unsigned long rc_count; >+ void *rc_next_dent; >+ void **rc_vb_dptrs; >+ struct vbuf rc_dirents; >+ struct vbuf rc_xattr_keys; >+ struct vbuf rc_xattr_values; >+}; >+ >+struct xreaddir_ctx { >+ int xc_err; >+ unsigned int xc_entries; >+ unsigned int xc_bufsize; >+ unsigned int xc_flags; >+ unsigned int xc_mask; >+ size_t xc_ubufsize; >+ unsigned int xc_ubuf_ent_cap; >+ struct gfs2_inode *xc_dip; >+ char *xc_buf; >+ char *xc_buftop; >+ struct xdirent **xc_xdents; >+ void *xc_opaque; >+}; >+ >+typedef int (*process_dent_t)(const struct gfs2_dirent *, loff_t, void *, filldir_t); >+ >+extern int compare_dents_i(const struct gfs2_dirent *dent_a, >+ const struct gfs2_dirent *dent_b); >+extern int xreaddir_collect_dents(void *opaque, u64 *offset, >+ const struct gfs2_dirent **darr, u32 entries); >+extern int foreach_dent(u64 *offset, void *opaque, filldir_t filldir, >+ const struct gfs2_dirent **darr, u32 entries, >+ int *copied, process_dent_t pd_fn); > extern struct inode *gfs2_dir_search(struct inode *dir, > const struct qstr *filename); > extern int gfs2_dir_check(struct inode *dir, const struct qstr *filename, >@@ -66,4 +126,7 @@ static inline void gfs2_qstr2dirent(const struct qstr *name, u16 reclen, struct > extern struct qstr gfs2_qdot; > extern struct qstr gfs2_qdotdot; > >+extern int gfs2_xrdir_collect_dents(const struct gfs2_dirent *dent, loff_t off, >+ void *opaque, filldir_t filldir); >+ > #endif /* __DIR_DOT_H__ */ >diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c >index 06b7092..558a312 100644 >--- a/fs/gfs2/file.c >+++ b/fs/gfs2/file.c >@@ -16,6 +16,9 @@ > #include <linux/blkdev.h> > #include <linux/mm.h> > #include <linux/mount.h> >+#include <linux/stat.h> >+#include <linux/sort.h> >+#include <linux/xattr.h> > #include <linux/fs.h> > #include <linux/gfs2_ondisk.h> > #include <linux/falloc.h> >@@ -39,6 +42,7 @@ > #include "rgrp.h" > #include "trans.h" > #include "util.h" >+#include "xattr.h" > > /** > * gfs2_llseek - seek to a location in a file >@@ -113,6 +117,352 @@ static int gfs2_readdir(struct file *file, void *dirent, filldir_t filldir) > return error; > } > >+static int gfs2_dirent_dot_or_dotdot(const struct gfs2_dirent *dent) >+{ >+ const char *name = (char *)(dent + 1); >+ >+ if (be16_to_cpu(dent->de_type) == DT_DIR) { >+ if (be16_to_cpu(dent->de_name_len) == 1 && name[0] == '.') >+ return 1; >+ if (be16_to_cpu(dent->de_name_len) == 2 && >+ strncmp(name, "..", 2) == 0) >+ return 1; >+ } >+ return 0; >+} >+ >+/* >+ * Add a gfs2_dirent to the xreaddir context >+ */ >+int gfs2_xrdir_collect_dents(const struct gfs2_dirent *dent, loff_t off, >+ void *opaque, filldir_t filldir) >+{ >+ struct gfs2_rddir_ctx *rc = opaque; >+ struct gfs2_xdirent *x; >+ u64 x_ino; >+ char x_type; >+ unsigned int x_xattr_count, x_namelen; >+ const void *nullptr = NULL; >+ >+ if (gfs2_dirent_dot_or_dotdot(dent)) >+ goto out; >+ >+ if (rc->rc_next_dent == NULL) >+ rc->rc_next_dent = rc->rc_dirents.v_ptr; >+ >+ /* Copy the dirent contents */ >+ x_ino = be64_to_cpu(dent->de_inum.no_addr); >+ x_type = be16_to_cpu(dent->de_type); >+ x_xattr_count = 0; >+ x_namelen = be16_to_cpu(dent->de_name_len); >+ >+ x = rc->rc_next_dent; >+ >+ vp_write(&rc->rc_dirents, &x->x_ino, &x_ino, sizeof(x->x_ino)); >+ vp_write(&rc->rc_dirents, &x->x_type, &x_type, sizeof(x->x_type)); >+ vp_write(&rc->rc_dirents, &x->x_xattr_count, &x_xattr_count, sizeof(x->x_xattr_count)); >+ vp_write(&rc->rc_dirents, &x->x_vb_xattr_arr_ptr, &nullptr, sizeof(x->x_vb_xattr_arr_ptr)); >+ vp_write(&rc->rc_dirents, &x->x_namelen, &x_namelen, sizeof(x->x_namelen)); >+ vp_write(&rc->rc_dirents, &x->x_name, (char*)(dent + 1), x_namelen); >+ >+ rc->rc_next_dent = x->x_name + x_namelen; >+ rc->rc_count++; >+out: >+ return 0; >+} >+ >+static int gfs2_xrdir_create_dptrs(struct gfs2_rddir_ctx *rc) >+{ >+ int error = -ENOMEM, i; >+ unsigned int namelen; >+ struct gfs2_xdirent *x = NULL; >+ >+ BUG_ON(rc->rc_vb_dptrs || rc->rc_count == 0); >+ >+ /* allocate the dirent pointers */ >+ rc->rc_vb_dptrs = kmalloc(sizeof(struct gfs2_xdirent *) * rc->rc_count, GFP_KERNEL); >+ if (rc->rc_vb_dptrs == NULL) >+ goto out; >+ >+ for (i=0; i<rc->rc_count; i++) { >+ if (!x) >+ x = rc->rc_dirents.v_ptr; >+ rc->rc_vb_dptrs[i] = x; >+ vp_read(&rc->rc_dirents, &namelen, &x->x_namelen, sizeof(x->x_namelen)); >+ /* reclen is sizeof(struct gfs2_xdirent) + x_namelen. >+ * see struct gfs2_xdirent for more info */ >+ x = (void *)x->x_name + namelen; >+ } >+out: >+ return error; >+} >+ >+static int gfs2_xrdir_collect_xstat(struct gfs2_rddir_ctx *rc, struct gfs2_xdirent *x, >+ struct gfs2_inode *ip) >+{ >+ struct kstat st; >+ >+ gfs2_getattr_i(ip, &st); >+ vp_write(&rc->rc_dirents, &x->x_kstat, &st, sizeof(struct kstat)); >+ >+ return 0; >+} >+ >+static int gfs2_xrdir_collect_extra_info(struct gfs2_rddir_ctx *rc, struct gfs2_inode *dip) >+{ >+ int error = 0, i; >+ >+ /* First sort the dptrs in block order */ >+ /* Don't know how to sort yet using vector pages */ >+ >+ /* Lookup all the inodes for stat info */ >+ for (i=0; i<rc->rc_count; i++) { >+ struct gfs2_xdirent *x_vb_p = rc->rc_vb_dptrs[i]; >+ u64 ino; >+ struct inode *inode; >+ struct gfs2_inode *ip; >+ struct gfs2_holder gh; >+ >+ vp_read(&rc->rc_dirents, &ino, &x_vb_p->x_ino, sizeof(x_vb_p->x_ino)); >+ >+ inode = gfs2_lookup_by_inum(GFS2_SB(&dip->i_inode), ino, NULL, GFS2_BLKST_DINODE); >+ if (IS_ERR(inode)) { >+ error = -1; >+ break; >+ } >+ ip = GFS2_I(inode); >+ error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh); >+ if (error) { >+ iput(inode); >+ break; >+ } >+ >+ gfs2_xrdir_collect_xstat(rc, x_vb_p, ip); >+ if (ip->i_eattr) >+ error = gfs2_xrdir_collect_xattrs(rc, x_vb_p, ip, XSTAT_ALL_XATTRS); >+ >+ gfs2_glock_dq_uninit(&gh); >+ iput(inode); >+ } >+ return error; >+} >+ >+static size_t gfs2_xrdir_to_user(struct gfs2_rddir_ctx *rc, void __user *buf, size_t count) >+{ >+ size_t ret = -EINVAL, bytes = 0, bytes_bef = 0; >+ int i, skip = 1, written = 0, attrcount; >+ struct gfs2_xdirent x, *x_vb_p; >+ struct gfs2_xd_xattr xdx, *xdx_vb_p; >+ struct linux_xdirent __user *lxd = buf; >+ struct xdirent_blob __user *xblob; >+ struct xdirent_xattr __user *xx; >+ char *tempbuf = NULL; >+ >+ tempbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); >+ if (!tempbuf) { >+ ret = -ENOMEM; >+ goto out; >+ } >+ >+ for (i=0; i<rc->rc_count; i++) { >+ if (skip && (rc->rc_vb_dptrs[i] != rc->rc_next_dent)) >+ continue; >+ skip = 0; >+ x_vb_p = rc->rc_vb_dptrs[i]; >+ >+ vp_read(&rc->rc_dirents, &x, x_vb_p, sizeof(struct gfs2_xdirent)); >+ >+ if ((count - bytes) < sizeof(struct linux_xdirent)) >+ goto overflow; >+ >+ if (__put_user(x.x_ino, &lxd->xd_ino)) >+ goto out; >+ if (__put_user(x.x_type, &lxd->xd_type)) >+ goto out; >+ if (__put_user(0, &lxd->xd_off)) >+ goto out; >+ >+ ret = xstat_set_result(&x.x_kstat, &lxd->xd_stat); >+ if (ret) >+ goto out; >+ >+ xblob = &lxd->xd_blob; >+ ret = -EINVAL; >+ >+ if (__put_user(x.x_xattr_count, &xblob->xb_xattr_count)) >+ goto out; >+ >+ /* copied all the fixed size fields */ >+ bytes += sizeof(struct linux_xdirent); >+ >+ /* copy all the variable length fields */ >+ if ((count - bytes) < x.x_namelen) >+ goto overflow; >+ >+ vp_read(&rc->rc_dirents, tempbuf, x_vb_p->x_name, x.x_namelen); >+ >+ if (copy_to_user(xblob->xb_blob, tempbuf, x.x_namelen)) >+ goto out; >+ if (__put_user(0, xblob->xb_blob + x.x_namelen)) >+ goto out; >+ >+ bytes += x.x_namelen; >+ >+ xx = (struct xdirent_xattr __user *)(xblob->xb_blob + x.x_namelen + 1); >+ attrcount = 0; >+ xdx_vb_p = x.x_vb_xattr_arr_ptr; >+ >+ while (attrcount < x.x_xattr_count) { >+ vp_read(&rc->rc_xattr_keys, &xdx, xdx_vb_p, sizeof(struct gfs2_xd_xattr)); >+ >+ if ((count - bytes) < >+ (sizeof(struct xdirent_xattr) + xdx.xa_keylen + xdx.xa_value_len)) >+ goto overflow; >+ >+ if (__put_user(xdx.xa_value_len, &xx->xa_value_len)) >+ goto out; >+ >+ vp_read(&rc->rc_xattr_keys, tempbuf, xdx_vb_p->xa_keyname, xdx.xa_keylen); >+ >+ if (copy_to_user(xx->xa_name_val, tempbuf, xdx.xa_keylen)) >+ goto out; >+ if (__put_user(0, xx->xa_name_val + xdx.xa_keylen)) >+ goto out; >+ >+ vp_read(&rc->rc_xattr_values, tempbuf, xdx.xa_vb_value_ptr, xdx.xa_value_len); >+ >+ if (copy_to_user(xx->xa_name_val + xdx.xa_keylen + 1, tempbuf, xdx.xa_value_len)) >+ goto out; >+ >+ xx = (struct xdirent_xattr __user *) >+ ((char *)xx + sizeof(xx->xa_value_len) >+ + xdx.xa_keylen + 1 + xdx.xa_value_len); >+ xdx_vb_p = (void*) xdx_vb_p + xdx.xa_reclen; >+ >+ bytes += sizeof(struct xdirent_xattr) + xdx.xa_keylen + xdx.xa_value_len; >+ attrcount++; >+ } >+ >+ if (__put_user(bytes - bytes_bef, &lxd->xd_reclen)) >+ goto out; >+ >+ lxd = (void *)lxd + (bytes - bytes_bef); >+ rc->rc_next_dent = rc->rc_vb_dptrs[i+1]; >+ written = 1; >+ bytes_bef = bytes; >+ } >+overflow: >+ if (!written && !skip) { >+ ret = -EOVERFLOW; >+ goto out; >+ } >+ ret = bytes_bef; >+out: >+ if (tempbuf) >+ kfree(tempbuf); >+ return ret; >+} >+ >+/** >+ * gfs2_xreaddir - GFS2's implementation of xreaddir functionality >+ * @file : The directory to xreaddir >+ * @flags : flags used by xstat >+ * @mask : field mask for xstat and xattrs >+ * @count : size of user buffer; needed to estimate # of dents to process >+ * @opaque: vfs state to pass back with the filler fn >+ * @filler: The vfs filler function that will write to the user buffer >+ * >+ * Collect extended information (xstat, xattrs) about the dents in the >+ * given directory and pass them to filler(). When filler() signals that >+ * the user buffer is full, adjust file offset to next unread dent and >+ * return. >+ * >+ * Returns: 0 if successful and -ve errno if an exception occurred. >+ */ >+#define RC_FL_ALLOCATED 1 >+#define RC_FL_UPDATED 2 >+ >+static size_t gfs2_xreaddir(struct file *file, unsigned int flags, unsigned int mask, >+ void __user *buf, size_t count) >+{ >+ struct gfs2_rddir_ctx *rc = ((struct gfs2_file *)file->private_data)->f_rdctx; >+ size_t error = 0; >+ loff_t offset; >+ >+ if (!(rc->rc_flags & RC_FL_UPDATED)) { >+ struct inode *dir = file->f_mapping->host; >+ struct gfs2_inode *dip = GFS2_I(dir); >+ struct gfs2_holder d_gh; >+ >+ gfs2_holder_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); >+ error = gfs2_glock_nq(&d_gh); >+ if (error) { >+ gfs2_holder_uninit(&d_gh); >+ goto out; >+ } >+ >+ error = gfs2_dir_read(dir, &offset, rc, NULL, &file->f_ra); >+ if (error < 0 || rc->rc_count == 0) >+ goto out; >+ >+ error = gfs2_xrdir_create_dptrs(rc); >+ if (error < 0) >+ goto out; >+ >+ error = gfs2_xrdir_collect_extra_info(rc, dip); >+ if (error < 0) >+ goto out; >+ >+ gfs2_glock_dq_uninit(&d_gh); >+ rc->rc_next_dent = rc->rc_vb_dptrs[0]; >+ rc->rc_flags |= RC_FL_UPDATED; >+ } >+ >+ error = gfs2_xrdir_to_user(rc, buf, count); >+out: >+ return error; >+} >+ >+void gfs2_rddir_ctx_uninit(struct gfs2_file *fp) >+{ >+ struct gfs2_rddir_ctx *rc; >+ >+ if (!fp || !fp->f_rdctx) >+ return; >+ >+ rc = fp->f_rdctx; >+ kfree(rc->rc_vb_dptrs); >+ vp_uninit(&rc->rc_xattr_values); >+ vp_uninit(&rc->rc_xattr_keys); >+ vp_uninit(&rc->rc_dirents); >+ kfree(rc); >+ fp->f_rdctx = NULL; >+} >+ >+int gfs2_rddir_ctx_init(struct gfs2_file *fp) >+{ >+ struct gfs2_rddir_ctx *rc; >+ if (!fp) >+ return -EINVAL; >+ >+ BUG_ON(fp->f_rdctx != NULL); >+ >+ rc = kzalloc(sizeof(struct gfs2_rddir_ctx), GFP_KERNEL); >+ if (rc == NULL) >+ return -ENOMEM; >+ >+ if (vp_init(&rc->rc_dirents, 1) || >+ vp_init(&rc->rc_xattr_keys, 1) || >+ vp_init(&rc->rc_xattr_values, 1)) { >+ gfs2_rddir_ctx_uninit(fp); >+ return -ENOMEM; >+ } >+ rc->rc_flags |= RC_FL_ALLOCATED; >+ fp->f_rdctx = rc; >+ >+ return 0; >+} >+ > /** > * fsflags_cvt > * @table: A table of 32 u32 flags >@@ -549,8 +899,13 @@ static int gfs2_open(struct inode *inode, struct file *file) > return -ENOMEM; > > mutex_init(&fp->f_fl_mutex); >- > gfs2_assert_warn(GFS2_SB(inode), !file->private_data); >+ >+ if (S_ISDIR(ip->i_inode.i_mode)) { >+ error = gfs2_rddir_ctx_init(fp); >+ if (error) >+ goto fail; >+ } > file->private_data = fp; > > if (S_ISREG(ip->i_inode.i_mode)) { >@@ -590,6 +945,9 @@ static int gfs2_release(struct inode *inode, struct file *file) > { > struct gfs2_inode *ip = GFS2_I(inode); > >+ if (S_ISDIR(ip->i_inode.i_mode)) >+ gfs2_rddir_ctx_uninit((struct gfs2_file *)file->private_data); >+ > kfree(file->private_data); > file->private_data = NULL; > >@@ -1039,6 +1397,7 @@ const struct file_operations gfs2_file_fops = { > > const struct file_operations gfs2_dir_fops = { > .readdir = gfs2_readdir, >+ .xreaddir = gfs2_xreaddir, > .unlocked_ioctl = gfs2_ioctl, > .open = gfs2_open, > .release = gfs2_release, >@@ -1069,10 +1428,10 @@ const struct file_operations gfs2_file_fops_nolock = { > > const struct file_operations gfs2_dir_fops_nolock = { > .readdir = gfs2_readdir, >+ .xreaddir = gfs2_xreaddir, > .unlocked_ioctl = gfs2_ioctl, > .open = gfs2_open, > .release = gfs2_release, > .fsync = gfs2_fsync, > .llseek = default_llseek, > }; >- >diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h >index 19750bc..05e5c39 100644 >--- a/fs/gfs2/incore.h >+++ b/fs/gfs2/incore.h >@@ -381,6 +381,7 @@ static inline struct gfs2_sbd *GFS2_SB(const struct inode *inode) > struct gfs2_file { > struct mutex f_fl_mutex; > struct gfs2_holder f_fl_gh; >+ struct gfs2_rddir_ctx *f_rdctx; > }; > > struct gfs2_revoke_replay { >diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c >index db048a8..b4528b8 100644 >--- a/fs/gfs2/inode.c >+++ b/fs/gfs2/inode.c >@@ -1705,7 +1705,8 @@ static int gfs2_getattr(struct vfsmount *mnt, struct dentry *dentry, > unlock = 1; > } > >- generic_fillattr(inode, stat); >+ gfs2_getattr_i(ip, stat); >+ > if (unlock) > gfs2_glock_dq_uninit(&gh); > >diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h >index c53c747..9559c10 100644 >--- a/fs/gfs2/inode.h >+++ b/fs/gfs2/inode.h >@@ -93,6 +93,11 @@ err: > return -EIO; > } > >+static inline void gfs2_getattr_i(struct gfs2_inode *ip, struct kstat *stat) >+{ >+ generic_fillattr(&ip->i_inode, stat); >+} >+ > extern struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type, > u64 no_addr, u64 no_formal_ino, > int non_block); >diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c >index f00d7c5..1453eac 100644 >--- a/fs/gfs2/util.c >+++ b/fs/gfs2/util.c >@@ -285,3 +285,253 @@ void gfs2_icbit_munge(struct gfs2_sbd *sdp, unsigned char **bitmap, > bitmap[c][o] &= ~(1 << b); > } > >+/* >+ * Fast vector-of-pages backed buffer >+ */ >+ >+static int vp_alloc_pages(struct vp_ctx *vpx, int start, int end) >+{ >+ int i; >+ >+ for (i=start; i<end; i++) { >+ vpx->vp_pages[i] = alloc_page(GFP_KERNEL | GFP_NOFS); >+ if (vpx->vp_pages[i] == NULL) >+ goto free; >+ } >+ return 0; >+free: >+ for (i=start; i<end; i++) >+ if (vpx->vp_pages[i]) { >+ __free_page(vpx->vp_pages[i]); >+ vpx->vp_pages[i] = NULL; >+ } >+ return -ENOMEM; >+} >+ >+static void vp_free_pages(struct vp_ctx *vpx) >+{ >+ int i; >+ >+ for (i=0; i<vpx->vp_size; i++) >+ if (vpx->vp_pages[i]) { >+ __free_page(vpx->vp_pages[i]); >+ vpx->vp_pages[i] = NULL; >+ } >+} >+ >+static int vp_extend(struct vp_ctx *vpx, int size) >+{ >+ /* first make room for more pointers */ >+ >+ if (size <= 0) >+ return -EINVAL; >+ >+ vpx->vp_pages = krealloc(vpx->vp_pages, >+ sizeof(struct page *) * (vpx->vp_size + size), >+ GFP_KERNEL); >+ if (vpx->vp_pages == NULL) >+ goto out; >+ >+ /* Zero out the new pointers and allocate pages*/ >+ memset(&vpx->vp_pages[vpx->vp_size], 0, sizeof(struct page *) * size); >+ if (vp_alloc_pages(vpx, vpx->vp_size, vpx->vp_size + size)) >+ goto out; >+ >+ vpx->vp_size += size; >+ return 0; >+out: >+ return -ENOMEM; >+} >+ >+int vp_init(struct vbuf *vb, int init_cap) >+{ >+ int cap, err = -ENOMEM; >+ struct vp_ctx *vpx; >+ >+ init_cap = DIV_ROUND_UP(init_cap, PAGE_SIZE); >+ cap = init_cap > 0 ? init_cap : 1; >+ >+ vpx = kmalloc(sizeof(struct vp_ctx), GFP_KERNEL); >+ if (vpx == NULL) >+ goto out; >+ >+ vpx->vp_magic = VP_MAGIC; >+ vpx->vp_size = cap; >+ vpx->vp_pages = kzalloc(sizeof(struct page *) * cap, GFP_KERNEL); >+ if (vpx->vp_pages == NULL) >+ goto free; >+ >+ if (vp_alloc_pages(vpx, 0, cap)) >+ goto free_all; >+ >+ vpx->vp_baseptr = vpx->vp_top = page_address(vpx->vp_pages[0]); >+ vb->v_ptr = vpx->vp_baseptr; >+ vb->v_opaque = vpx; >+ >+ err = 0; >+ goto out; >+ >+free_all: >+ vp_free_pages(vpx); >+ kfree(vpx->vp_pages); >+free: >+ kfree(vpx); >+ vpx = NULL; >+out: >+ return err; >+} >+ >+void vp_uninit(struct vbuf *vb) >+{ >+ struct vp_ctx *vpx; >+ >+ if (!vb || !vb->v_opaque) >+ return; >+ >+ vpx = vb->v_opaque; >+ if (vpx->vp_magic != VP_MAGIC) >+ return; >+ >+ vp_free_pages(vpx); >+ kfree(vpx->vp_pages); >+ kfree(vpx); >+ vb->v_ptr = vb->v_opaque = NULL; >+} >+ >+static int vp_rw_pages(struct vp_ctx *vpx, void *to, void *from, size_t count, int what) >+{ >+ int pg_ind, pg_off, bytes, rw = 0; >+ >+ while (count > 0) { >+ pg_ind = what == VP_READ ? VP_PAGE_INDEX(vpx, from) : VP_PAGE_INDEX(vpx, to); >+ pg_off = what == VP_READ ? VP_PAGE_OFFSET(vpx, from) : VP_PAGE_OFFSET(vpx, to); >+ bytes = what == VP_READ ? VP_PAGE_BYTES_LEFT(vpx, from) : VP_PAGE_BYTES_LEFT(vpx, to); >+ bytes = bytes > count ? count : bytes; >+ >+ if (what == VP_READ) >+ memcpy(to, VP_PAGE_PTR(vpx, pg_ind, pg_off), bytes); >+ else { >+ memcpy(VP_PAGE_PTR(vpx, pg_ind, pg_off), from, bytes); >+ if ((to + count) > vpx->vp_top) >+ vpx->vp_top = to + count; >+ } >+ to += bytes; >+ from += bytes; >+ rw += bytes; >+ count -= bytes; >+ } >+ return rw; >+} >+ >+struct vp_ctx* vp_get_vpx(struct vbuf *vb) >+{ >+ struct vp_ctx *vpx = NULL; >+ >+ if (!vb || !vb->v_opaque) >+ goto out; >+ >+ vpx = vb->v_opaque; >+ if (vpx->vp_magic != VP_MAGIC) { >+ vpx = NULL; >+ goto out; >+ } >+out: >+ return vpx; >+} >+ >+int vp_read(struct vbuf *vb, void *to, void *from, size_t count) >+{ >+ struct vp_ctx *vpx; >+ void *buf_end = NULL; >+ int err = -EINVAL; >+ >+ vpx = vp_get_vpx(vb); >+ if (!vpx) >+ return err; >+ >+ buf_end = vpx->vp_baseptr + PAGE_SIZE * vpx->vp_size; >+ if (count <= 0 || (from + count > buf_end)) >+ return err; >+ >+ return vp_rw_pages(vpx, to, from, count, VP_READ); >+} >+ >+int vp_write_i(struct vp_ctx *vpx, void *to, void *from, size_t count) >+{ >+ int err = -EINVAL; >+ void *buf_end = NULL; >+ >+ /* Check to see if the write will fall within limits */ >+ buf_end = vpx->vp_baseptr + PAGE_SIZE * vpx->vp_size; >+ if (to < vpx->vp_baseptr || >+ (to + count) > (buf_end + PAGE_SIZE * VP_MAX_ALLOC_STEP)) >+ return err; >+ >+ if ((to + count) > buf_end) { >+ err = vp_extend(vpx, DIV_ROUND_UP(to + count - buf_end, PAGE_SIZE)); >+ if (err) >+ return err; >+ } >+ >+ return vp_rw_pages(vpx, to, from, count, VP_WRITE); >+} >+ >+int vp_write(struct vbuf *vb, void *to, void *from, size_t count) >+{ >+ struct vp_ctx *vpx; >+ int err = -EINVAL; >+ >+ if (count <= 0 || count > VP_MAX_WRITE) >+ return err; >+ >+ vpx = vp_get_vpx(vb); >+ if (!vpx) >+ return err; >+ >+ return vp_write_i(vpx, to, from, count); >+} >+ >+int vp_append(struct vbuf *vb, void *from, size_t count) >+{ >+ struct vp_ctx *vpx; >+ int err = -EINVAL; >+ >+ if (count <= 0 || count > VP_MAX_WRITE) >+ return err; >+ >+ vpx = vp_get_vpx(vb); >+ if (!vpx) >+ return err; >+ >+ return vp_write_i(vpx, vpx->vp_top, from, count); >+} >+ >+int vp_memset(struct vbuf *vb, void *to, char c, size_t count) >+{ >+ struct vp_ctx *vpx; >+ int err = -EINVAL, i; >+ >+ if (count <=0 || count > VP_MAX_WRITE) >+ return err; >+ >+ vpx = vp_get_vpx(vb); >+ if (!vpx) >+ return err; >+ >+ for (i=0; i<count; i++) { >+ err = vp_write_i(vpx, to + i, &c, 1); >+ if (err != 1) >+ break; >+ } >+ return err; >+} >+ >+void* vp_get_top(struct vbuf *vb) >+{ >+ struct vp_ctx *vpx; >+ vpx = vp_get_vpx(vb); >+ if (!vpx) >+ return NULL; >+ else >+ return vpx->vp_top; >+} >diff --git a/fs/gfs2/util.h b/fs/gfs2/util.h >index 8053573..9397f32 100644 >--- a/fs/gfs2/util.h >+++ b/fs/gfs2/util.h >@@ -11,6 +11,7 @@ > #define __UTIL_DOT_H__ > > #include <linux/mempool.h> >+#include <linux/slab.h> > > #include "incore.h" > >@@ -168,5 +169,40 @@ void gfs2_icbit_munge(struct gfs2_sbd *sdp, unsigned char **bitmap, > unsigned int bit, int new_value); > int gfs2_lm_withdraw(struct gfs2_sbd *sdp, char *fmt, ...); > >+/* >+ * Fast buffer backed by vector of pages. >+ * Always allocate one page at a time >+ */ >+struct vbuf { >+ void *v_ptr; >+ void *v_opaque; >+}; >+ >+struct vp_ctx { >+ unsigned int vp_magic; >+ void *vp_baseptr; >+ void *vp_top; >+ unsigned long vp_size; /* size in pages (for bytes, * PAGE_SIZE) */ >+ struct page **vp_pages; >+}; >+ >+#define VP_MAGIC 0xfabd1cc5 >+#define VP_MAX_WRITE (PAGE_SIZE * 1024) >+#define VP_MAX_ALLOC_STEP 128 /* Can only alloc these many pages at a time */ >+ >+#define VP_READ 1 >+#define VP_WRITE 2 >+ >+#define VP_PAGE_INDEX(vpx, a) (((char*)a - (char*)vpx->vp_baseptr) / PAGE_SIZE) >+#define VP_PAGE_OFFSET(vpx, a) (((char*)a - (char*)vpx->vp_baseptr) % PAGE_SIZE) >+#define VP_PAGE_BYTES_LEFT(vpx, a) (PAGE_SIZE - VP_PAGE_OFFSET(vpx, a)) >+#define VP_PAGE_PTR(vpx, ind, off) (page_address(vpx->vp_pages[ind]) + off) >+ >+int vp_init(struct vbuf *vb, int init_cap); >+void vp_uninit(struct vbuf *vb); >+void* vp_get_top(struct vbuf *vb); >+int vp_read(struct vbuf *vb, void *to, void *from, size_t count); >+int vp_write(struct vbuf *vb, void *to, void *from, size_t count); >+int vp_append(struct vbuf *vb, void *from, size_t count); >+int vp_memset(struct vbuf *vb, void *to, char c, size_t count); > #endif /* __UTIL_DOT_H__ */ >- >diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c >index cbb46c2..8e4432a 100644 >--- a/fs/gfs2/xattr.c >+++ b/fs/gfs2/xattr.c >@@ -11,6 +11,7 @@ > #include <linux/spinlock.h> > #include <linux/completion.h> > #include <linux/buffer_head.h> >+#include <linux/sort.h> > #include <linux/xattr.h> > #include <linux/gfs2_ondisk.h> > #include <asm/uaccess.h> >@@ -18,6 +19,7 @@ > #include "gfs2.h" > #include "incore.h" > #include "acl.h" >+#include "dir.h" > #include "xattr.h" > #include "glock.h" > #include "inode.h" >@@ -73,10 +75,11 @@ static int ea_check_size(struct gfs2_sbd *sdp, unsigned int nsize, size_t dsize) > > typedef int (*ea_call_t) (struct gfs2_inode *ip, struct buffer_head *bh, > struct gfs2_ea_header *ea, >- struct gfs2_ea_header *prev, void *private); >+ struct gfs2_ea_header *prev, void *private, >+ unsigned int mask); > > static int ea_foreach_i(struct gfs2_inode *ip, struct buffer_head *bh, >- ea_call_t ea_call, void *data) >+ ea_call_t ea_call, void *data, unsigned int mask) > { > struct gfs2_ea_header *ea, *prev = NULL; > int error = 0; >@@ -93,7 +96,7 @@ static int ea_foreach_i(struct gfs2_inode *ip, struct buffer_head *bh, > if (!GFS2_EATYPE_VALID(ea->ea_type)) > goto fail; > >- error = ea_call(ip, bh, ea, prev, data); >+ error = ea_call(ip, bh, ea, prev, data, mask); > if (error) > return error; > >@@ -112,7 +115,8 @@ fail: > return -EIO; > } > >-static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data) >+static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data, >+ unsigned int mask) > { > struct buffer_head *bh, *eabh; > __be64 *eablk, *end; >@@ -123,7 +127,7 @@ static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data) > return error; > > if (!(ip->i_diskflags & GFS2_DIF_EA_INDIRECT)) { >- error = ea_foreach_i(ip, bh, ea_call, data); >+ error = ea_foreach_i(ip, bh, ea_call, data, mask); > goto out; > } > >@@ -145,7 +149,7 @@ static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data) > error = gfs2_meta_read(ip->i_gl, bn, DIO_WAIT, &eabh); > if (error) > break; >- error = ea_foreach_i(ip, eabh, ea_call, data); >+ error = ea_foreach_i(ip, eabh, ea_call, data, mask); > brelse(eabh); > if (error) > break; >@@ -164,7 +168,7 @@ struct ea_find { > > static int ea_find_i(struct gfs2_inode *ip, struct buffer_head *bh, > struct gfs2_ea_header *ea, struct gfs2_ea_header *prev, >- void *private) >+ void *private, unsigned mask) > { > struct ea_find *ef = private; > >@@ -199,7 +203,7 @@ static int gfs2_ea_find(struct gfs2_inode *ip, int type, const char *name, > > memset(el, 0, sizeof(struct gfs2_ea_location)); > >- error = ea_foreach(ip, ea_find_i, &ef); >+ error = ea_foreach(ip, ea_find_i, &ef, 0); > if (error > 0) > return 0; > >@@ -223,7 +227,8 @@ static int gfs2_ea_find(struct gfs2_inode *ip, int type, const char *name, > > static int ea_dealloc_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh, > struct gfs2_ea_header *ea, >- struct gfs2_ea_header *prev, void *private) >+ struct gfs2_ea_header *prev, void *private, >+ unsigned int mask) > { > int *leave = private; > struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); >@@ -335,7 +340,7 @@ static int ea_remove_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh, > if (error) > goto out_alloc; > >- error = ea_dealloc_unstuffed(ip, bh, ea, prev, (leave) ? &error : NULL); >+ error = ea_dealloc_unstuffed(ip, bh, ea, prev, (leave) ? &error : NULL, 0); > > gfs2_quota_unhold(ip); > out_alloc: >@@ -361,9 +366,26 @@ static inline unsigned int gfs2_ea_strlen(struct gfs2_ea_header *ea) > } > } > >+static __inline__ int ea_prefix(struct gfs2_ea_header *ea, char *buf, int size) >+{ >+ BUG_ON(size < 9); >+ switch (ea->ea_type) { >+ case GFS2_EATYPE_USR: >+ strncpy(buf, "user.", 5); >+ return 5; >+ case GFS2_EATYPE_SYS: >+ strncpy(buf, "system.", 7); >+ return 7; >+ case GFS2_EATYPE_SECURITY: >+ strncpy(buf, "security.", 9); >+ return 9; >+ } >+ return 0; >+} >+ > static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh, > struct gfs2_ea_header *ea, struct gfs2_ea_header *prev, >- void *private) >+ void *private, unsigned int mask) > { > struct ea_list *ei = private; > struct gfs2_ea_request *er = ei->ei_er; >@@ -373,28 +395,14 @@ static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh, > return 0; > > if (er->er_data_len) { >- char *prefix = NULL; >+ char prefix[9]; > unsigned int l = 0; > char c = 0; > > if (ei->ei_size + ea_size > er->er_data_len) > return -ERANGE; > >- switch (ea->ea_type) { >- case GFS2_EATYPE_USR: >- prefix = "user."; >- l = 5; >- break; >- case GFS2_EATYPE_SYS: >- prefix = "system."; >- l = 7; >- break; >- case GFS2_EATYPE_SECURITY: >- prefix = "security."; >- l = 9; >- break; >- } >- >+ l = ea_prefix(ea, prefix, 9); > BUG_ON(l == 0); > > memcpy(er->er_data + ei->ei_size, prefix, l); >@@ -408,6 +416,93 @@ static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh, > return 0; > } > >+static inline int xattr_requested(char type, unsigned int mask) >+{ >+ if ((type == GFS2_EATYPE_USR) && (mask & XSTAT_XATTR_USER)) >+ return 1; >+ if ((type == GFS2_EATYPE_SYS) && (mask & XSTAT_XATTR_SYSTEM)) >+ return 1; >+ if ((type == GFS2_EATYPE_SECURITY) && (mask & XSTAT_XATTR_SECURITY)) >+ return 1; >+ return 0; >+} >+ >+struct gfs2_xdir_ctx_bndle { >+ struct gfs2_rddir_ctx *xcb_rc; >+ struct gfs2_xdirent *xcb_xd; >+}; >+ >+static int ea_xdirent_list_i(struct gfs2_inode *ip, struct buffer_head *bh, >+ struct gfs2_ea_header *ea, struct gfs2_ea_header *prev, >+ void *private, unsigned int mask) >+{ >+ struct gfs2_xdir_ctx_bndle *bundle = private; >+ struct gfs2_rddir_ctx *rc = bundle->xcb_rc; >+ struct gfs2_xdirent *x = bundle->xcb_xd; >+ struct gfs2_xd_xattr *xtr; >+ char prefix[9]; >+ unsigned int l = 0, xtr_count; >+ unsigned int namlen, reclen; >+ void *p; >+ >+ if (!xattr_requested(ea->ea_type, mask)) >+ return 0; >+ >+ if (ea->ea_type == GFS2_EATYPE_UNUSED) >+ return 0; >+ >+ l = ea_prefix(ea, prefix, 9); >+ BUG_ON(l == 0); >+ >+ xtr = vp_get_top(&rc->rc_xattr_keys); >+ vp_memset(&rc->rc_xattr_keys, xtr, 0, sizeof(struct gfs2_xd_xattr)); >+ >+ /* if mask says don't do values, skip the following lines */ >+ if (GFS2_EA_DATA_LEN(ea) > 0) { >+ unsigned long len = GFS2_EA_DATA_LEN(ea); >+ void *valptr = vp_get_top(&rc->rc_xattr_values); >+ >+ vp_write(&rc->rc_xattr_keys, &xtr->xa_value_len, >+ &len, sizeof(xtr->xa_value_len)); >+ vp_write(&rc->rc_xattr_keys, &xtr->xa_vb_value_ptr, &valptr, sizeof(void*)); >+ vp_read(&rc->rc_xattr_keys, &p, &xtr->xa_vb_value_ptr, sizeof(void*)); >+ vp_append(&rc->rc_xattr_values, GFS2_EA2DATA(ea), len); >+ } >+ >+ namlen = l + ea->ea_name_len; >+ vp_write(&rc->rc_xattr_keys, &xtr->xa_keylen, &namlen, sizeof(xtr->xa_keylen)); >+ vp_write(&rc->rc_xattr_keys, xtr->xa_keyname, &prefix, l); >+ vp_write(&rc->rc_xattr_keys, xtr->xa_keyname + l, GFS2_EA2NAME(ea), namlen); >+ >+ /* gfs2_xd_xattr.xa_keyname[1] has an extra byte */ >+ reclen = (xtr->xa_keyname + l + namlen) - (char *)xtr; >+ vp_write(&rc->rc_xattr_keys, &xtr->xa_reclen, &reclen, sizeof(xtr->xa_reclen)); >+ >+ vp_read(&rc->rc_dirents, &xtr_count, &x->x_xattr_count, sizeof(x->x_xattr_count)); >+ xtr_count++; >+ vp_write(&rc->rc_dirents, &x->x_xattr_count, &xtr_count, sizeof(x->x_xattr_count)); >+ >+ return 0; >+} >+ >+int gfs2_xrdir_collect_xattrs(struct gfs2_rddir_ctx *rc, struct gfs2_xdirent *x_vb_p, >+ struct gfs2_inode *ip, unsigned int mask) >+{ >+ int error = 0; >+ struct gfs2_xdir_ctx_bndle bundle; >+ struct gfs2_xdirent *xtop; >+ >+ bundle.xcb_rc = rc; >+ bundle.xcb_xd = x_vb_p; >+ >+ xtop = vp_get_top(&rc->rc_xattr_keys); >+ vp_write(&rc->rc_dirents, &x_vb_p->x_vb_xattr_arr_ptr, &xtop, sizeof(struct gfs2_xd_xattr*)); >+ >+ error = ea_foreach(ip, ea_xdirent_list_i, &bundle, mask); >+ >+ return error; >+} >+ > /** > * gfs2_listxattr - List gfs2 extended attributes > * @dentry: The dentry whose inode we are interested in >@@ -437,7 +532,7 @@ ssize_t gfs2_listxattr(struct dentry *dentry, char *buffer, size_t size) > if (ip->i_eattr) { > struct ea_list ei = { .ei_er = &er, .ei_size = 0 }; > >- error = ea_foreach(ip, ea_list_i, &ei); >+ error = ea_foreach(ip, ea_list_i, &ei, 0); > if (!error) > error = ei.ei_size; > } >@@ -918,7 +1013,7 @@ static int ea_set_simple_alloc(struct gfs2_inode *ip, > > static int ea_set_simple(struct gfs2_inode *ip, struct buffer_head *bh, > struct gfs2_ea_header *ea, struct gfs2_ea_header *prev, >- void *private) >+ void *private, unsigned int mask) > { > struct ea_set *es = private; > unsigned int size; >@@ -1055,7 +1150,7 @@ static int ea_set_i(struct gfs2_inode *ip, int type, const char *name, > es.es_er = &er; > es.es_el = el; > >- error = ea_foreach(ip, ea_set_simple, &es); >+ error = ea_foreach(ip, ea_set_simple, &es, 0); > if (error > 0) > return 0; > if (error) >@@ -1465,7 +1560,7 @@ int gfs2_ea_dealloc(struct gfs2_inode *ip) > if (error) > return error; > >- error = ea_foreach(ip, ea_dealloc_unstuffed, NULL); >+ error = ea_foreach(ip, ea_dealloc_unstuffed, NULL, 0); > if (error) > goto out_quota; > >diff --git a/fs/gfs2/xattr.h b/fs/gfs2/xattr.h >index d392f83..1cebebf 100644 >--- a/fs/gfs2/xattr.h >+++ b/fs/gfs2/xattr.h >@@ -10,6 +10,8 @@ > #ifndef __EATTR_DOT_H__ > #define __EATTR_DOT_H__ > >+#include "dir.h" >+ > struct gfs2_inode; > struct iattr; > >@@ -56,6 +58,8 @@ struct gfs2_ea_location { > extern int __gfs2_xattr_set(struct inode *inode, const char *name, > const void *value, size_t size, > int flags, int type); >+extern int gfs2_xrdir_collect_xattrs(struct gfs2_rddir_ctx *rc, struct gfs2_xdirent *x, >+ struct gfs2_inode *ip, unsigned int mask); > extern ssize_t gfs2_listxattr(struct dentry *dentry, char *buffer, size_t size); > extern int gfs2_ea_dealloc(struct gfs2_inode *ip); > >diff --git a/fs/readdir.c b/fs/readdir.c >index 5e69ef5..a3482d2 100644 >--- a/fs/readdir.c >+++ b/fs/readdir.c >@@ -143,6 +143,105 @@ struct getdents_callback { > int error; > }; > >+static size_t get_xdirent_size(const struct xdirent_info *xinfo) >+{ >+ int i; >+ /* Start with the base size */ >+ size_t size = sizeof(struct linux_xdirent); >+ /* Account for the various variable length fields >+ * starting with dirent name. Note that one trailing >+ * '\0' is already accounted for by the xd_blob field >+ * in struct linux_xdirent >+ */ >+ size += xinfo->xi_namelen; >+ if (!xinfo->xi_xattr_count) >+ return size; /* No xattrs to worry about */ >+ for (i=0; i<xinfo->xi_xattr_count; i++) { >+ struct xattr *xattr = xinfo->xi_xattrs[i]; >+ size += sizeof(size_t); /* for xdirent_xattr.xa_value_len */ >+ size += strlen(xattr->name) + 1; >+ size += xattr->value_len; >+ } >+ return size; >+} >+ >+static int xfill_variables(struct linux_xdirent __user *lxd, >+ const struct xdirent_info *xinfo) >+{ >+ int error = -EINVAL, i; >+ struct xdirent_blob __user *xblob; >+ struct xdirent_xattr __user *xx; >+ >+ xblob = &lxd->xd_blob; >+ >+ if (__put_user(xinfo->xi_xattr_count, &xblob->xb_xattr_count)) >+ goto out; >+ >+ if (copy_to_user(xblob->xb_blob, xinfo->xi_name, xinfo->xi_namelen)) >+ goto out; >+ if (__put_user(0, xblob->xb_blob + xinfo->xi_namelen)) >+ goto out; >+ >+ xx = (struct xdirent_xattr __user *)(xblob->xb_blob + xinfo->xi_namelen + 1); >+ >+ for (i=0; i< xinfo->xi_xattr_count; i++) { >+ struct xattr *xattr = xinfo->xi_xattrs[i]; >+ int namlen = strlen(xattr->name); >+ >+ if (__put_user(xattr->value_len, &xx->xa_value_len)) >+ goto out; >+ if (copy_to_user(xx->xa_name_val, xattr->name, namlen)) >+ goto out; >+ if (__put_user(0, xx->xa_name_val + namlen)) >+ goto out; >+ if (copy_to_user(xx->xa_name_val + namlen + 1, xattr->value, >+ xattr->value_len)) >+ goto out; >+ xx = (struct xdirent_xattr __user *) >+ ((char *)xx + sizeof(xx->xa_value_len) >+ + namlen + 1 + xattr->value_len); >+ } >+ error = 0; >+out: >+ return error; >+} >+ >+static int xfilldir(void *__opaque, const struct xdirent_info *xinfo) >+{ >+ struct xdirent_buf *xbuf = (struct xdirent_buf *) __opaque; >+ struct linux_xdirent __user *lxd; >+ int error = -EOVERFLOW; >+ size_t entsize; >+ >+ /* Before proceeding any further, check that the dirent will >+ * fit in its entirety in the remaining space of the user >+ * buffer >+ */ >+ entsize = get_xdirent_size(xinfo); >+ if ((xbuf->xb_count - xbuf->xb_used) < entsize) >+ goto out; >+ >+ error = -EINVAL; >+ lxd = (struct linux_xdirent __user *)(xbuf->xb_buf + xbuf->xb_used); >+ if (__put_user(xinfo->xi_ino, &lxd->xd_ino)) >+ goto out; >+ if (__put_user(xinfo->xi_type, &lxd->xd_type)) >+ goto out; >+ if (__put_user(xinfo->xi_offset, &lxd->xd_off)) >+ goto out; >+ if (__put_user(entsize, &lxd->xd_reclen)) >+ goto out; >+ error = xstat_set_result((struct kstat *)&xinfo->xi_stat, &lxd->xd_stat); >+ if (error) >+ goto out; >+ error = xfill_variables(lxd, xinfo); >+ if (error) >+ goto out; >+ xbuf->xb_used += entsize; >+out: >+ return error; >+} >+ > static int filldir(void * __buf, const char * name, int namlen, loff_t offset, > u64 ino, unsigned int d_type) > { >@@ -220,6 +319,93 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd, > return error; > } > >+SYSCALL_DEFINE5(xgetdents, unsigned int, fd, unsigned, flags, unsigned int, mask, >+ void __user *, buf, unsigned int, count) >+{ >+ struct file *file; >+ struct inode *inode; >+ int fput_needed; >+ int error = -ENOTDIR; >+ >+ if (!access_ok(VERIFY_WRITE, buf, count)) >+ return -EFAULT; >+ >+ file = fget_light(fd, &fput_needed); >+ if (!file) >+ return -EBADF; >+ >+ inode = file->f_path.dentry->d_inode; >+ >+ if (!file->f_op || !file->f_op->xreaddir) >+ goto out; >+ >+ error = security_file_permission(file, MAY_READ); >+ if (error) >+ goto out; >+ >+ error = mutex_lock_killable(&inode->i_mutex); >+ if (error) >+ goto out; >+ >+ error = -ENOENT; >+ if (!IS_DEADDIR(inode)) { >+ error = file->f_op->xreaddir(file, flags, mask, buf, count); >+ file_accessed(file); >+ } >+ mutex_unlock(&inode->i_mutex); >+out: >+ fput_light(file, fput_needed); >+ return error; >+} >+ >+#if 0 >+SYSCALL_DEFINE5(xgetdents, unsigned int, fd, unsigned, flags, unsigned int, mask, >+ void __user *, buf, unsigned int, count) >+{ >+ struct file *file; >+ struct inode *inode; >+ int fput_needed; >+ int error = -ENOTDIR; >+ >+ if (!access_ok(VERIFY_WRITE, buf, count)) >+ return -EFAULT; >+ >+ file = fget_light(fd, &fput_needed); >+ if (!file) >+ return -EBADF; >+ >+ inode = file->f_path.dentry->d_inode; >+ >+ if (!file->f_op || !file->f_op->xreaddir) >+ goto out; >+ >+ error = security_file_permission(file, MAY_READ); >+ if (error) >+ goto out; >+ >+ error = mutex_lock_killable(&inode->i_mutex); >+ if (error) >+ goto out; >+ >+ error = -ENOENT; >+ if (!IS_DEADDIR(inode)) { >+ struct xdirent_buf xbuf; >+ xbuf.xb_buf = buf; >+ xbuf.xb_used = 0; >+ xbuf.xb_count = count; >+ error = file->f_op->xreaddir(file, flags, mask, count, &xbuf, xfilldir); >+ if (!error || error == -EOVERFLOW) >+ error = xbuf.xb_used; >+ >+ file_accessed(file); >+ } >+ mutex_unlock(&inode->i_mutex); >+out: >+ fput_light(file, fput_needed); >+ return error; >+} >+#endif >+ > struct getdents_callback64 { > struct linux_dirent64 __user * current_dir; > struct linux_dirent64 __user * previous; >diff --git a/fs/stat.c b/fs/stat.c >index 5303f40..60376d3 100644 >--- a/fs/stat.c >+++ b/fs/stat.c >@@ -635,7 +635,7 @@ static int xstat_get_params(unsigned int mask, struct xstat __user *buffer, > * Otherwise we copy the extended stats to userspace and return the amount of > * data written into the buffer (or -EFAULT). > */ >-static long xstat_set_result(struct kstat *stat, struct xstat __user *buffer) >+long xstat_set_result(struct kstat *stat, struct xstat __user *buffer) > { > u32 mask = stat->result_mask, gran = stat->tv_granularity; > >@@ -685,6 +685,8 @@ static long xstat_set_result(struct kstat *stat, struct xstat __user *buffer) > return 0; > } > >+EXPORT_SYMBOL(xstat_set_result); >+ > /* > * System call to get extended stats by path > */ >diff --git a/include/linux/fs.h b/include/linux/fs.h >index 44c6c84..35f1418 100644 >--- a/include/linux/fs.h >+++ b/include/linux/fs.h >@@ -1505,6 +1505,8 @@ int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags); > * to have different dirent layouts depending on the binary type. > */ > typedef int (*filldir_t)(void *, const char *, int, loff_t, u64, unsigned); >+typedef int (*xfilldir_t)(void *, const struct xdirent_info *); >+ > struct block_device_operations; > > /* These macros are for out of kernel modules to test that >@@ -1521,6 +1523,10 @@ struct file_operations { > ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); > ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); > int (*readdir) (struct file *, void *, filldir_t); >+ size_t (*xreaddir) (struct file *, unsigned int, unsigned int, void __user *, size_t); >+#if 0 >+ int (*xreaddir) (struct file *, unsigned int, unsigned int, size_t, void *, xfilldir_t); >+#endif > unsigned int (*poll) (struct file *, struct poll_table_struct *); > long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); > long (*compat_ioctl) (struct file *, unsigned int, unsigned long); >diff --git a/include/linux/list_bl.h b/include/linux/list_bl.h >index 31f9d75..2eb8855 100644 >--- a/include/linux/list_bl.h >+++ b/include/linux/list_bl.h >@@ -125,6 +125,11 @@ static inline void hlist_bl_unlock(struct hlist_bl_head *b) > __bit_spin_unlock(0, (unsigned long *)b); > } > >+static inline bool hlist_bl_is_locked(struct hlist_bl_head *b) >+{ >+ return bit_spin_is_locked(0, (unsigned long *)b); >+} >+ > /** > * hlist_bl_for_each_entry - iterate over list of given type > * @tpos: the type * to use as a loop cursor. >diff --git a/include/linux/rculist_bl.h b/include/linux/rculist_bl.h >index cf1244f..4f216c5 100644 >--- a/include/linux/rculist_bl.h >+++ b/include/linux/rculist_bl.h >@@ -20,7 +20,7 @@ static inline void hlist_bl_set_first_rcu(struct hlist_bl_head *h, > static inline struct hlist_bl_node *hlist_bl_first_rcu(struct hlist_bl_head *h) > { > return (struct hlist_bl_node *) >- ((unsigned long)rcu_dereference(h->first) & ~LIST_BL_LOCKMASK); >+ ((unsigned long)rcu_dereference_check(h->first, hlist_bl_is_locked(h)) & ~LIST_BL_LOCKMASK); > } > > /** >diff --git a/include/linux/stat.h b/include/linux/stat.h >index 552e047..e18d107 100644 >--- a/include/linux/stat.h >+++ b/include/linux/stat.h >@@ -18,6 +18,7 @@ > #include <linux/types.h> > #include <linux/time.h> > #include <linux/uidgid.h> >+#include <linux/xattr.h> > > struct kstat { > u32 query_flags; /* operational flags */ >@@ -46,4 +47,23 @@ struct kstat { > unsigned char volume_id[16]; /* volume identifier */ > }; > >+long xstat_set_result(struct kstat *stat, struct xstat __user *buffer); >+ >+struct xdirent_buf { >+ void __user *xb_buf; >+ size_t xb_count; >+ size_t xb_used; >+}; >+ >+struct xdirent_info { >+ const char *xi_name; >+ int xi_namelen; >+ u64 xi_ino; >+ u64 xi_offset; >+ char xi_type; >+ unsigned int xi_xattr_count; >+ struct kstat xi_stat; >+ struct xattr **xi_xattrs; >+}; >+ > #endif >diff --git a/include/uapi/linux/stat.h b/include/uapi/linux/stat.h >index 2907352..44a5aae 100644 >--- a/include/uapi/linux/stat.h >+++ b/include/uapi/linux/stat.h >@@ -90,6 +90,13 @@ > #define XSTAT_VOLUME_ID 0x00008000U /* want/got st_volume_id */ > #define XSTAT_ALL_STATS 0x0000ffffU /* all supported stats */ > >+/* xattr request flags */ >+#define XSTAT_XATTR_USER 0x00010000U /* user.* xattrs */ >+#define XSTAT_XATTR_SYSTEM 0x00020000U /* system.* xattrs */ >+#define XSTAT_XATTR_SECURITY 0x00040000U /* security.* xattrs */ >+#define XSTAT_XATTR_POSIX_ACL 0x00080000U /* posix acl xattrs */ >+#define XSTAT_ALL_XATTRS 0x00ff0000U /* all xattrs */ >+ > /* > * Extended stat structures > */ >@@ -152,4 +159,29 @@ struct xstat { > #define XSTAT_INFO_SYSTEM 0x00001000U /* File is marked system (DOS+) */ > #define XSTAT_INFO_ARCHIVE 0x00002000U /* File is marked archive (DOS+) */ > >+struct xdirent_xattr { >+ size_t xa_value_len; /* length of value field */ >+ char xa_name_val[1]; /* name/value pair, name is NULL terminated and >+ * value is xa_value_len in length */ >+}; >+ >+/* >+ * xb_blob: first contains NULL terminated name, followed by struct xdirent_xattr >+ * objects packed together. >+ */ >+struct xdirent_blob { >+ unsigned int xb_xattr_count; >+ char xb_blob[1]; /* contains variable length data like >+ * NULL-terminated name, xattrs etc */ >+}; >+ >+struct linux_xdirent { >+ unsigned long xd_ino; >+ char xd_type; >+ unsigned long xd_off; >+ struct xstat xd_stat; >+ unsigned long xd_reclen; >+ struct xdirent_blob xd_blob; >+}; >+ > #endif /* _UAPI_LINUX_STAT_H */
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 850426
:
605960
|
617073
|
625592
|
678841
|
680222
|
680282
|
708389
|
731269
|
731270
|
731272
|
731273
|
731274
|
731278
|
731279
|
732739
|
732740
|
732845
|
834840
|
853502
|
909528
|
909529
|
909534
|
912745
|
917238
|
917239
|
920895
|
920896
|
920897
|
920898
|
920899
|
920902
|
920903
|
947987