* [Ocfs2-devel] [PATCH 1/2] Ocfs2: Add new ioctls prototype and corresponding data structure to ocfs2 header. @ 2009-11-27 8:24 Tristan Ye 2009-11-27 8:24 ` [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2 Tristan Ye 2009-11-30 22:57 ` [Ocfs2-devel] [PATCH 1/2] Ocfs2: Add new ioctls prototype and corresponding data structure to ocfs2 header Sunil Mushran 0 siblings, 2 replies; 14+ messages in thread From: Tristan Ye @ 2009-11-27 8:24 UTC (permalink / raw) To: ocfs2-devel We use OCFS2_IOC_INFO to manipulate the new ioctl, the requests can be stored in a array of (struct ocfs2_info_request), which should be NULL-terminated, kernel therefore can recoginze and fill them one by one. The reason why we need these ioctls is to offer the none-privileged end-user a possibility to get filesys info gathering. Idea here is to make the spearated request small enough to guarantee a better backward&forward compatibility since a small piece of request would be less likely to be broken if filesys on raw disk get changed. Currently, the first version include following request type: OCFS2_INFO_CLUSTERSIZE, OCFS2_INFO_BLOCKSIZE, OCFS2_INFO_SLOTNUM, OCFS2_INFO_LABEL, OCFS2_INFO_UUID, OCFS2_INFO_FS_FEATURES It may be grown from time to time:) Signed-off-by: Tristan Ye <tristan.ye@oracle.com> --- fs/ocfs2/ocfs2_fs.h | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 69 insertions(+), 0 deletions(-) diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index e9431e4..dac7f4c 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h @@ -309,6 +309,75 @@ struct reflink_arguments { }; #define OCFS2_IOC_REFLINK _IOW('o', 4, struct reflink_arguments) +#define OCFS2_UUID_LEN 16 +#define OCFS2_MAX_LABEL_LEN 64 + +/* + * Used to query fs information for none-privileged + * users by using OCFS2_IOC_INFO ioctols, a series of + * following requests need to be NULL-terminated. + * + * Always try to separate info request into small pieces to + * guarantee the backward&forward compatibility. + */ +struct ocfs2_info_request { + __u32 ir_code; /* Info request code */ + __u32 ir_flags; /* Request flags */ +}; + +struct ocfs2_info_request_clustersize { + struct ocfs2_info_request ir_request; + __u32 ir_clustersize; +}; + +struct ocfs2_info_request_blocksize { + struct ocfs2_info_request ir_request; + __u32 ir_blocksize; +}; + +struct ocfs2_info_request_slotnum { + struct ocfs2_info_request ir_request; + __u16 ir_slotnum; +}; + +struct ocfs2_info_request_label { + struct ocfs2_info_request ir_request; + __u8 ir_label[OCFS2_MAX_LABEL_LEN]; +}; + +struct ocfs2_info_request_uuid { + struct ocfs2_info_request ir_request; + __u8 ir_uuid[OCFS2_UUID_LEN]; +}; + +struct ocfs2_info_request_fs_features { + struct ocfs2_info_request ir_quest; + __u32 ir_compat_features; + __u32 ir_incompat_features; + __u32 ir_ro_compat_features; +}; + +/* Codes for cfs2_info_request */ + +#define OCFS2_INFO_CLUSTERSIZE 0x00000001 +#define OCFS2_INFO_BLOCKSIZE 0x00000002 +#define OCFS2_INFO_SLOTNUM 0x00000004 +#define OCFS2_INFO_LABEL 0x00000008 +#define OCFS2_INFO_UUID 0x00000010 +#define OCFS2_INFO_FS_FEATURES 0x00000020 + +/* Flags for struct ocfs2_info_request */ +/* Filled by the caller */ +#define OCFS2_INFO_FL_NON_COHERENT 0x00000001 /* Cluster coherency not required. + This is a hint. It is up to + ocfs2 whether the request can + be fulfilled without locking. */ +/* Filled by ocfs2 */ +#define OCFS2_INFO_FL_FILLED 0x80000000 /* Filesystem understood this + request and filled in the + answer */ + +#define OCFS2_IOC_INFO _IOR('o', 5, struct ocfs2_info_request) /* * Journal Flags (ocfs2_dinode.id1.journal1.i_flags) -- 1.5.5 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2. 2009-11-27 8:24 [Ocfs2-devel] [PATCH 1/2] Ocfs2: Add new ioctls prototype and corresponding data structure to ocfs2 header Tristan Ye @ 2009-11-27 8:24 ` Tristan Ye 2009-11-27 20:23 ` Andi Kleen 2009-11-30 23:49 ` Sunil Mushran 2009-11-30 22:57 ` [Ocfs2-devel] [PATCH 1/2] Ocfs2: Add new ioctls prototype and corresponding data structure to ocfs2 header Sunil Mushran 1 sibling, 2 replies; 14+ messages in thread From: Tristan Ye @ 2009-11-27 8:24 UTC (permalink / raw) To: ocfs2-devel We implement the request as simple as possible to avoid a possible extending if disk format get changed. Completenss of a request include the info required and a flag(FL_FILLED) filled being returned to the end-user. Note: the requests in a series from userspace should be NULL-terminated. Signed-off-by: Tristan Ye <tristan.ye@oracle.com> --- fs/ocfs2/ioctl.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 184 insertions(+), 0 deletions(-) diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c index 31fbb06..0ac5218 100644 --- a/fs/ocfs2/ioctl.c +++ b/fs/ocfs2/ioctl.c @@ -108,6 +108,188 @@ bail: return status; } +static int ocfs2_get_request_info(struct inode *inode, + unsigned long arg) +{ + + int ret = 0, i = 0, num_reqs = 0; + + struct ocfs2_info_request req, *preq = NULL; + struct ocfs2_info_request **reqs; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + typedef struct ocfs2_info_request OIR, *POIR; + typedef struct ocfs2_info_request_clustersize OIRC, *POIRC; + typedef struct ocfs2_info_request_blocksize OIRB, *POIRB; + typedef struct ocfs2_info_request_slotnum OIRS, *POIRS; + typedef struct ocfs2_info_request_label OIRL, *POIRL; + typedef struct ocfs2_info_request_uuid OIRU, *POIRU; + typedef struct ocfs2_info_request_fs_features OIRF, *POIRF; + + /* + * The requests series from userspace need to be NULL-terminated. + */ + do { + preq = *((POIR *)((char *)arg + i * sizeof(POIR))); + if (!preq) + break; + i++; + + } while (preq); + + num_reqs = i; + + reqs = kmalloc(sizeof(POIR) * num_reqs, GFP_KERNEL); + if (!reqs) { + ret = -ENOMEM; + goto bail; + } + + ret = ocfs2_inode_lock(inode, NULL, 0); + if (ret < 0) { + mlog_errno(ret); + goto bail; + } + + for (i = 0; i < num_reqs; i++) { + + reqs[i] = NULL; + + preq = *((POIR *)((char *)arg + i * sizeof(POIR))); + + if (copy_from_user(&req, preq, sizeof(req))) { + ret = -EFAULT; + goto bail; + } + + switch (req.ir_code) { + case OCFS2_INFO_CLUSTERSIZE: + reqs[i] = kmalloc(sizeof(OIRC), GFP_KERNEL); + + if (copy_from_user(reqs[i], preq, sizeof(OIRC))) { + ret = -EFAULT; + goto bail; + } + + ((POIRC)(reqs[i]))->ir_clustersize = osb->s_clustersize; + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; + + if (copy_to_user(preq, reqs[i], sizeof(OIRC))) { + ret = EFAULT; + goto bail; + } + + break; + case OCFS2_INFO_BLOCKSIZE: + reqs[i] = kmalloc(sizeof(OIRB), GFP_KERNEL); + + if (copy_from_user(reqs[i], preq, sizeof(OIRB))) { + ret = -EFAULT; + goto bail; + } + + ((POIRB)(reqs[i]))->ir_blocksize = + inode->i_sb->s_blocksize; + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; + + if (copy_to_user(preq, reqs[i], sizeof(OIRB))) { + ret = -EFAULT; + goto bail; + } + + break; + case OCFS2_INFO_SLOTNUM: + reqs[i] = kmalloc(sizeof(OIRS), GFP_KERNEL); + + if (copy_from_user(reqs[i], preq, sizeof(OIRS))) { + ret = -EFAULT; + goto bail; + } + + ((POIRS)(reqs[i]))->ir_slotnum = osb->max_slots; + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; + + if (copy_to_user(preq, reqs[i], sizeof(OIRS))) { + ret = -EFAULT; + goto bail; + } + + break; + case OCFS2_INFO_LABEL: + reqs[i] = kmalloc(sizeof(OIRL), GFP_KERNEL); + + if (copy_from_user(reqs[i], preq, sizeof(OIRL))) { + ret = -EFAULT; + goto bail; + } + + memmove(((POIRL)(reqs[i]))->ir_label, osb->vol_label, + OCFS2_MAX_VOL_LABEL_LEN); + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; + + if (copy_to_user(preq, reqs[i], sizeof(OIRL))) { + ret = -EFAULT; + goto bail; + } + + break; + case OCFS2_INFO_UUID: + reqs[i] = kmalloc(sizeof(OIRU), GFP_KERNEL); + + if (copy_from_user(reqs[i], preq, sizeof(OIRU))) { + ret = -EFAULT; + goto bail; + } + + memmove(((POIRU)(reqs[i]))->ir_uuid, osb->uuid, + OCFS2_VOL_UUID_LEN); + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; + + if (copy_to_user(preq, reqs[i], sizeof(OIRU))) { + ret = -EFAULT; + goto bail; + } + + break; + case OCFS2_INFO_FS_FEATURES: + reqs[i] = kmalloc(sizeof(OIRF), GFP_KERNEL); + + if (copy_from_user(reqs[i], preq, sizeof(OIRF))) { + ret = -EFAULT; + goto bail; + } + + ((POIRF)(reqs[i]))->ir_compat_features = + osb->s_feature_compat; + ((POIRF)(reqs[i]))->ir_incompat_features = + osb->s_feature_incompat; + ((POIRF)(reqs[i]))->ir_ro_compat_features = + osb->s_feature_ro_compat; + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; + + if (copy_to_user(preq, reqs[i], sizeof(OIRF))) { + ret = -EFAULT; + goto bail; + } + + break; + default: + break; + } + + } +bail: + for (i = 0; i < num_reqs; i++) + kfree(reqs[i]); + + kfree(reqs); + + ocfs2_inode_unlock(inode, 0); + + mlog_exit(ret); + return ret; +} + long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_path.dentry->d_inode; @@ -173,6 +355,8 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) preserve = (args.preserve != 0); return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve); + case OCFS2_IOC_INFO: + return ocfs2_get_request_info(inode, arg); default: return -ENOTTY; } -- 1.5.5 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2. 2009-11-27 8:24 ` [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2 Tristan Ye @ 2009-11-27 20:23 ` Andi Kleen 2009-11-28 2:49 ` Tristan 2009-11-30 9:36 ` Joel Becker 2009-11-30 23:49 ` Sunil Mushran 1 sibling, 2 replies; 14+ messages in thread From: Andi Kleen @ 2009-11-27 20:23 UTC (permalink / raw) To: ocfs2-devel Tristan Ye <tristan.ye@oracle.com> writes: > + > + /* > + * The requests series from userspace need to be NULL-terminated. > + */ > + do { > + preq = *((POIR *)((char *)arg + i * sizeof(POIR))); > + if (!preq) > + break; > + i++; That's the first security leak. Can be used to probe arbitary memory. You always need to use *_user for any user space access. > + > + } while (preq); > + > + num_reqs = i; > + > + reqs = kmalloc(sizeof(POIR) * num_reqs, GFP_KERNEL); This is next root exploit. Think what happens when the user passes a very large number for num_reqs that overflows the multiplication. If anything use kcalloc(). And limit the maximum size. It's unclear why you just can't use separate ioctls for each request. -Andi -- ak at linux.intel.com -- Speaking for myself only. ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2. 2009-11-27 20:23 ` Andi Kleen @ 2009-11-28 2:49 ` Tristan 2009-11-30 9:36 ` Joel Becker 1 sibling, 0 replies; 14+ messages in thread From: Tristan @ 2009-11-28 2:49 UTC (permalink / raw) To: ocfs2-devel Andi Kleen wrote: > Tristan Ye <tristan.ye@oracle.com> writes: > >> + >> + /* >> + * The requests series from userspace need to be NULL-terminated. >> + */ >> + do { >> + preq = *((POIR *)((char *)arg + i * sizeof(POIR))); >> + if (!preq) >> + break; >> + i++; >> > > That's the first security leak. Can be used to probe arbitary memory. > You always need to use *_user for any user space access. > Thank you for pointing this out, I'm now still learning how to hack the kernel codes:-) > >> + >> + } while (preq); >> + >> + num_reqs = i; >> + >> + reqs = kmalloc(sizeof(POIR) * num_reqs, GFP_KERNEL); >> > > This is next root exploit. Think what happens when the user passes a very > large number for num_reqs that overflows the multiplication. > > If anything use kcalloc(). And limit the maximum size. > Good point, I've checked the code, there is no need to allocate a pointer array for reqs. > It's unclear why you just can't use separate ioctls for each request. > A simple reason why we used NULL-terminated array to combine request pieces is to avoid a mass propagation of these ioctls, you know we may add such separated ioctols much frequently, end-user also I guess would be glad to tie all their requsets of this kind to one series, and deliver them only once. > -Andi > Tristan. ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2. 2009-11-27 20:23 ` Andi Kleen 2009-11-28 2:49 ` Tristan @ 2009-11-30 9:36 ` Joel Becker 1 sibling, 0 replies; 14+ messages in thread From: Joel Becker @ 2009-11-30 9:36 UTC (permalink / raw) To: ocfs2-devel On Fri, Nov 27, 2009 at 09:23:36PM +0100, Andi Kleen wrote: > It's unclear why you just can't use separate ioctls for each request. Because we're going to be adding many info requests, with more over time. Much nicer to pass in a list of requests than have a hundred or more ioctls. Plus we can do fun versioning. Joel -- "Sometimes I think the surest sign intelligent life exists elsewhere in the universe is that none of it has tried to contact us." -Calvin & Hobbes Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2. 2009-11-27 8:24 ` [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2 Tristan Ye 2009-11-27 20:23 ` Andi Kleen @ 2009-11-30 23:49 ` Sunil Mushran 2009-12-01 1:25 ` Tristan 2009-12-01 3:20 ` Tristan 1 sibling, 2 replies; 14+ messages in thread From: Sunil Mushran @ 2009-11-30 23:49 UTC (permalink / raw) To: ocfs2-devel Tristan Ye wrote: > We implement the request as simple as possible to avoid > a possible extending if disk format get changed. > > Completenss of a request include the info required and a flag(FL_FILLED) > filled being returned to the end-user. > > Note: the requests in a series from userspace should be NULL-terminated. Pass in the buffer length. Expecting null-termination is asking for trouble. > > Signed-off-by: Tristan Ye <tristan.ye@oracle.com> > --- > fs/ocfs2/ioctl.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 files changed, 184 insertions(+), 0 deletions(-) > > diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c > index 31fbb06..0ac5218 100644 > --- a/fs/ocfs2/ioctl.c > +++ b/fs/ocfs2/ioctl.c > @@ -108,6 +108,188 @@ bail: > return status; > } > > +static int ocfs2_get_request_info(struct inode *inode, > + unsigned long arg) > +{ > + > + int ret = 0, i = 0, num_reqs = 0; > + > + struct ocfs2_info_request req, *preq = NULL; > + struct ocfs2_info_request **reqs; > + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); > + > + typedef struct ocfs2_info_request OIR, *POIR; > + typedef struct ocfs2_info_request_clustersize OIRC, *POIRC; > + typedef struct ocfs2_info_request_blocksize OIRB, *POIRB; > + typedef struct ocfs2_info_request_slotnum OIRS, *POIRS; > + typedef struct ocfs2_info_request_label OIRL, *POIRL; > + typedef struct ocfs2_info_request_uuid OIRU, *POIRU; > + typedef struct ocfs2_info_request_fs_features OIRF, *POIRF; > + Three no-nos. 1. Do not typedef structures. 2. Use all-caps only for #defines. 3. Do not pre-pend type to the variable name. For more refer to the kernel coding style. http://lxr.linux.no/source/Documentation/CodingStyle > + /* > + * The requests series from userspace need to be NULL-terminated. > + */ > + do { > + preq = *((POIR *)((char *)arg + i * sizeof(POIR))); > + if (!preq) > + break; > + i++; > + > + } while (preq); > + > + num_reqs = i; If we pass in the length of the buffer, then num_reqs = len / sizeof(struct ...); > + > + reqs = kmalloc(sizeof(POIR) * num_reqs, GFP_KERNEL); > + if (!reqs) { > + ret = -ENOMEM; > + goto bail; > + } We can then copy_from_user the buffer in one go. The remaining code have to change accordingly. > + > + ret = ocfs2_inode_lock(inode, NULL, 0); > + if (ret < 0) { > + mlog_errno(ret); > + goto bail; > + } The superblock lock is special. See ocfs2_super_lock(). You'll need to traverse to the osb to use super_lock. struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); Also, make use of OCFS2_INFO_FL_NON_COHERENT. > + > + for (i = 0; i < num_reqs; i++) { > + > + reqs[i] = NULL; > + > + preq = *((POIR *)((char *)arg + i * sizeof(POIR))); > + > + if (copy_from_user(&req, preq, sizeof(req))) { > + ret = -EFAULT; > + goto bail; > + } > + > + switch (req.ir_code) { > + case OCFS2_INFO_CLUSTERSIZE: > + reqs[i] = kmalloc(sizeof(OIRC), GFP_KERNEL); > + > + if (copy_from_user(reqs[i], preq, sizeof(OIRC))) { > + ret = -EFAULT; > + goto bail; > + } > + > + ((POIRC)(reqs[i]))->ir_clustersize = osb->s_clustersize; > + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; > + > + if (copy_to_user(preq, reqs[i], sizeof(OIRC))) { > + ret = EFAULT; -EFAULT. > + goto bail; > + } > + > + break; > + case OCFS2_INFO_BLOCKSIZE: > + reqs[i] = kmalloc(sizeof(OIRB), GFP_KERNEL); > + > + if (copy_from_user(reqs[i], preq, sizeof(OIRB))) { > + ret = -EFAULT; > + goto bail; > + } > + > + ((POIRB)(reqs[i]))->ir_blocksize = > + inode->i_sb->s_blocksize; > + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; > + > + if (copy_to_user(preq, reqs[i], sizeof(OIRB))) { > + ret = -EFAULT; > + goto bail; > + } > + > + break; > + case OCFS2_INFO_SLOTNUM: > + reqs[i] = kmalloc(sizeof(OIRS), GFP_KERNEL); > + > + if (copy_from_user(reqs[i], preq, sizeof(OIRS))) { > + ret = -EFAULT; > + goto bail; > + } > + > + ((POIRS)(reqs[i]))->ir_slotnum = osb->max_slots; > + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; > + > + if (copy_to_user(preq, reqs[i], sizeof(OIRS))) { > + ret = -EFAULT; > + goto bail; > + } > + > + break; > + case OCFS2_INFO_LABEL: > + reqs[i] = kmalloc(sizeof(OIRL), GFP_KERNEL); > + > + if (copy_from_user(reqs[i], preq, sizeof(OIRL))) { > + ret = -EFAULT; > + goto bail; > + } > + > + memmove(((POIRL)(reqs[i]))->ir_label, osb->vol_label, > + OCFS2_MAX_VOL_LABEL_LEN); Why memmove() when memcpy() will do? > + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; > + > + if (copy_to_user(preq, reqs[i], sizeof(OIRL))) { > + ret = -EFAULT; > + goto bail; > + } > + > + break; > + case OCFS2_INFO_UUID: > + reqs[i] = kmalloc(sizeof(OIRU), GFP_KERNEL); > + > + if (copy_from_user(reqs[i], preq, sizeof(OIRU))) { > + ret = -EFAULT; > + goto bail; > + } > + > + memmove(((POIRU)(reqs[i]))->ir_uuid, osb->uuid, > + OCFS2_VOL_UUID_LEN); > + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; > + > + if (copy_to_user(preq, reqs[i], sizeof(OIRU))) { > + ret = -EFAULT; > + goto bail; > + } > + > + break; > + case OCFS2_INFO_FS_FEATURES: > + reqs[i] = kmalloc(sizeof(OIRF), GFP_KERNEL); > + > + if (copy_from_user(reqs[i], preq, sizeof(OIRF))) { > + ret = -EFAULT; > + goto bail; > + } > + > + ((POIRF)(reqs[i]))->ir_compat_features = > + osb->s_feature_compat; > + ((POIRF)(reqs[i]))->ir_incompat_features = > + osb->s_feature_incompat; > + ((POIRF)(reqs[i]))->ir_ro_compat_features = > + osb->s_feature_ro_compat; > + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; > + > + if (copy_to_user(preq, reqs[i], sizeof(OIRF))) { > + ret = -EFAULT; > + goto bail; > + } > + > + break; > + default: > + break; > + } > + > + } > +bail: > + for (i = 0; i < num_reqs; i++) > + kfree(reqs[i]); > + > + kfree(reqs); > + > + ocfs2_inode_unlock(inode, 0); > + > + mlog_exit(ret); > + return ret; > +} > + > long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) > { > struct inode *inode = filp->f_path.dentry->d_inode; > @@ -173,6 +355,8 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) > preserve = (args.preserve != 0); > > return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve); > + case OCFS2_IOC_INFO: > + return ocfs2_get_request_info(inode, arg); > default: > return -ENOTTY; > } ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2. 2009-11-30 23:49 ` Sunil Mushran @ 2009-12-01 1:25 ` Tristan 2009-12-02 0:16 ` Sunil Mushran 2009-12-01 3:20 ` Tristan 1 sibling, 1 reply; 14+ messages in thread From: Tristan @ 2009-12-01 1:25 UTC (permalink / raw) To: ocfs2-devel Sunil Mushran wrote: > Tristan Ye wrote: >> We implement the request as simple as possible to avoid >> a possible extending if disk format get changed. >> >> Completenss of a request include the info required and a flag(FL_FILLED) >> filled being returned to the end-user. >> >> Note: the requests in a series from userspace should be NULL-terminated. > > Pass in the buffer length. Expecting null-termination is asking for > trouble. I agree, may discuss with joel later, a null-terminated flavor may become a security leak... Actually, maybe we could take single request into account, which mean end-user deliver one request for one info each time. For users, just a matter of call ioctl() mutiple times. For kernel, means simpler to handle... > >> >> Signed-off-by: Tristan Ye <tristan.ye@oracle.com> >> --- >> fs/ocfs2/ioctl.c | 184 >> ++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> 1 files changed, 184 insertions(+), 0 deletions(-) >> >> diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c >> index 31fbb06..0ac5218 100644 >> --- a/fs/ocfs2/ioctl.c >> +++ b/fs/ocfs2/ioctl.c >> @@ -108,6 +108,188 @@ bail: >> return status; >> } >> >> +static int ocfs2_get_request_info(struct inode *inode, >> + unsigned long arg) >> +{ >> + >> + int ret = 0, i = 0, num_reqs = 0; >> + >> + struct ocfs2_info_request req, *preq = NULL; >> + struct ocfs2_info_request **reqs; >> + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); >> + >> + typedef struct ocfs2_info_request OIR, *POIR; >> + typedef struct ocfs2_info_request_clustersize OIRC, *POIRC; >> + typedef struct ocfs2_info_request_blocksize OIRB, *POIRB; >> + typedef struct ocfs2_info_request_slotnum OIRS, *POIRS; >> + typedef struct ocfs2_info_request_label OIRL, *POIRL; >> + typedef struct ocfs2_info_request_uuid OIRU, *POIRU; >> + typedef struct ocfs2_info_request_fs_features OIRF, *POIRF; >> + > > Three no-nos. > 1. Do not typedef structures. > 2. Use all-caps only for #defines. > 3. Do not pre-pend type to the variable name. > > For more refer to the kernel coding style. > http://lxr.linux.no/source/Documentation/CodingStyle Thank you for pointing this out. > >> + /* >> + * The requests series from userspace need to be NULL-terminated. >> + */ >> + do { >> + preq = *((POIR *)((char *)arg + i * sizeof(POIR))); >> + if (!preq) >> + break; >> + i++; >> + >> + } while (preq); >> + >> + num_reqs = i; > > If we pass in the length of the buffer, then num_reqs = len / > sizeof(struct ...); > >> + >> + reqs = kmalloc(sizeof(POIR) * num_reqs, GFP_KERNEL); >> + if (!reqs) { >> + ret = -ENOMEM; >> + goto bail; >> + } > > We can then copy_from_user the buffer in one go. The remaining > code have to change accordingly. I also noticed that, we actually don't need the pointer array at all. > >> + >> + ret = ocfs2_inode_lock(inode, NULL, 0); >> + if (ret < 0) { >> + mlog_errno(ret); >> + goto bail; >> + } > > The superblock lock is special. See ocfs2_super_lock(). > > You'll need to traverse to the osb to use super_lock. > struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); > > Also, make use of OCFS2_INFO_FL_NON_COHERENT. Yes, that's my fault. > >> + >> + for (i = 0; i < num_reqs; i++) { >> + >> + reqs[i] = NULL; >> + >> + preq = *((POIR *)((char *)arg + i * sizeof(POIR))); >> + >> + if (copy_from_user(&req, preq, sizeof(req))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + switch (req.ir_code) { >> + case OCFS2_INFO_CLUSTERSIZE: >> + reqs[i] = kmalloc(sizeof(OIRC), GFP_KERNEL); >> + >> + if (copy_from_user(reqs[i], preq, sizeof(OIRC))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + ((POIRC)(reqs[i]))->ir_clustersize = osb->s_clustersize; >> + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; >> + >> + if (copy_to_user(preq, reqs[i], sizeof(OIRC))) { >> + ret = EFAULT; > > -EFAULT. > >> + goto bail; >> + } >> + >> + break; >> + case OCFS2_INFO_BLOCKSIZE: >> + reqs[i] = kmalloc(sizeof(OIRB), GFP_KERNEL); >> + >> + if (copy_from_user(reqs[i], preq, sizeof(OIRB))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + ((POIRB)(reqs[i]))->ir_blocksize = >> + inode->i_sb->s_blocksize; >> + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; >> + >> + if (copy_to_user(preq, reqs[i], sizeof(OIRB))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + break; >> + case OCFS2_INFO_SLOTNUM: >> + reqs[i] = kmalloc(sizeof(OIRS), GFP_KERNEL); >> + >> + if (copy_from_user(reqs[i], preq, sizeof(OIRS))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + ((POIRS)(reqs[i]))->ir_slotnum = osb->max_slots; >> + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; >> + >> + if (copy_to_user(preq, reqs[i], sizeof(OIRS))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + break; >> + case OCFS2_INFO_LABEL: >> + reqs[i] = kmalloc(sizeof(OIRL), GFP_KERNEL); >> + >> + if (copy_from_user(reqs[i], preq, sizeof(OIRL))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + memmove(((POIRL)(reqs[i]))->ir_label, osb->vol_label, >> + OCFS2_MAX_VOL_LABEL_LEN); > > Why memmove() when memcpy() will do? I always have a thought that memmove handled thing more gracefully than memcpy when overlap happens. > >> + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; >> + >> + if (copy_to_user(preq, reqs[i], sizeof(OIRL))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + break; >> + case OCFS2_INFO_UUID: >> + reqs[i] = kmalloc(sizeof(OIRU), GFP_KERNEL); >> + >> + if (copy_from_user(reqs[i], preq, sizeof(OIRU))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + memmove(((POIRU)(reqs[i]))->ir_uuid, osb->uuid, >> + OCFS2_VOL_UUID_LEN); >> + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; >> + >> + if (copy_to_user(preq, reqs[i], sizeof(OIRU))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + break; >> + case OCFS2_INFO_FS_FEATURES: >> + reqs[i] = kmalloc(sizeof(OIRF), GFP_KERNEL); >> + >> + if (copy_from_user(reqs[i], preq, sizeof(OIRF))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + ((POIRF)(reqs[i]))->ir_compat_features = >> + osb->s_feature_compat; >> + ((POIRF)(reqs[i]))->ir_incompat_features = >> + osb->s_feature_incompat; >> + ((POIRF)(reqs[i]))->ir_ro_compat_features = >> + osb->s_feature_ro_compat; >> + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; >> + >> + if (copy_to_user(preq, reqs[i], sizeof(OIRF))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + break; >> + default: >> + break; >> + } >> + >> + } >> +bail: >> + for (i = 0; i < num_reqs; i++) >> + kfree(reqs[i]); >> + >> + kfree(reqs); >> + >> + ocfs2_inode_unlock(inode, 0); >> + >> + mlog_exit(ret); >> + return ret; >> +} >> + >> long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) >> { >> struct inode *inode = filp->f_path.dentry->d_inode; >> @@ -173,6 +355,8 @@ long ocfs2_ioctl(struct file *filp, unsigned int >> cmd, unsigned long arg) >> preserve = (args.preserve != 0); >> >> return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve); >> + case OCFS2_IOC_INFO: >> + return ocfs2_get_request_info(inode, arg); >> default: >> return -ENOTTY; >> } > ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2. 2009-12-01 1:25 ` Tristan @ 2009-12-02 0:16 ` Sunil Mushran 0 siblings, 0 replies; 14+ messages in thread From: Sunil Mushran @ 2009-12-02 0:16 UTC (permalink / raw) To: ocfs2-devel Tristan wrote: > I always have a thought that memmove handled thing more gracefully > than memcpy when overlap happens. Yes and hence we should to use it when there is even a small chance of buffer overlap. But that is not the case here. ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2. 2009-11-30 23:49 ` Sunil Mushran 2009-12-01 1:25 ` Tristan @ 2009-12-01 3:20 ` Tristan 2009-12-02 0:35 ` Joel Becker 1 sibling, 1 reply; 14+ messages in thread From: Tristan @ 2009-12-01 3:20 UTC (permalink / raw) To: ocfs2-devel Sunil Mushran wrote: > Tristan Ye wrote: >> We implement the request as simple as possible to avoid >> a possible extending if disk format get changed. >> >> Completenss of a request include the info required and a flag(FL_FILLED) >> filled being returned to the end-user. >> >> Note: the requests in a series from userspace should be NULL-terminated. > > Pass in the buffer length. Expecting null-termination is asking for > trouble. > >> >> Signed-off-by: Tristan Ye <tristan.ye@oracle.com> >> --- >> fs/ocfs2/ioctl.c | 184 >> ++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> 1 files changed, 184 insertions(+), 0 deletions(-) >> >> diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c >> index 31fbb06..0ac5218 100644 >> --- a/fs/ocfs2/ioctl.c >> +++ b/fs/ocfs2/ioctl.c >> @@ -108,6 +108,188 @@ bail: >> return status; >> } >> >> +static int ocfs2_get_request_info(struct inode *inode, >> + unsigned long arg) >> +{ >> + >> + int ret = 0, i = 0, num_reqs = 0; >> + >> + struct ocfs2_info_request req, *preq = NULL; >> + struct ocfs2_info_request **reqs; >> + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); >> + >> + typedef struct ocfs2_info_request OIR, *POIR; >> + typedef struct ocfs2_info_request_clustersize OIRC, *POIRC; >> + typedef struct ocfs2_info_request_blocksize OIRB, *POIRB; >> + typedef struct ocfs2_info_request_slotnum OIRS, *POIRS; >> + typedef struct ocfs2_info_request_label OIRL, *POIRL; >> + typedef struct ocfs2_info_request_uuid OIRU, *POIRU; >> + typedef struct ocfs2_info_request_fs_features OIRF, *POIRF; >> + > > Three no-nos. > 1. Do not typedef structures. > 2. Use all-caps only for #defines. > 3. Do not pre-pend type to the variable name. > > For more refer to the kernel coding style. > http://lxr.linux.no/source/Documentation/CodingStyle > >> + /* >> + * The requests series from userspace need to be NULL-terminated. >> + */ >> + do { >> + preq = *((POIR *)((char *)arg + i * sizeof(POIR))); >> + if (!preq) >> + break; >> + i++; >> + >> + } while (preq); >> + >> + num_reqs = i; > > If we pass in the length of the buffer, then num_reqs = len / > sizeof(struct ...); Size of each request may differ from type to type, think about: struct ocfs2_info_label { struct ocfs2_info ir_request; __u8 ir_label[OCFS2_MAX_VOL_LABEL_LEN]; }; struct ocfs2_info_uuid { struct ocfs2_info ir_request; __u8 ir_uuid_str[OCFS2_VOL_UUIDSTR_LEN]; }; struct ocfs2_info_fs_features { struct ocfs2_info ir_quest; __u32 ir_compat_features; __u32 ir_incompat_features; __u32 ir_ro_compat_features; }; So a len/sizeof() way may not provide the real number of requests, to pass a count of request from userspace to kernel may make more sense. but it was more like a workaround anyway, The most graceful way I guess is to treat request one by one, end-users deliver just one request each time, it's just a matter of calling ioctl() repeatly for users, and we still use just one ioctl type for all of the requests(OCFS2_IOC_INFO), different requests can be recognized by ir_code as we specified. Tristan. > >> + >> + reqs = kmalloc(sizeof(POIR) * num_reqs, GFP_KERNEL); >> + if (!reqs) { >> + ret = -ENOMEM; >> + goto bail; >> + } > > We can then copy_from_user the buffer in one go. The remaining > code have to change accordingly. > >> + >> + ret = ocfs2_inode_lock(inode, NULL, 0); >> + if (ret < 0) { >> + mlog_errno(ret); >> + goto bail; >> + } > > The superblock lock is special. See ocfs2_super_lock(). > > You'll need to traverse to the osb to use super_lock. > struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); > > Also, make use of OCFS2_INFO_FL_NON_COHERENT. > >> + >> + for (i = 0; i < num_reqs; i++) { >> + >> + reqs[i] = NULL; >> + >> + preq = *((POIR *)((char *)arg + i * sizeof(POIR))); >> + >> + if (copy_from_user(&req, preq, sizeof(req))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + switch (req.ir_code) { >> + case OCFS2_INFO_CLUSTERSIZE: >> + reqs[i] = kmalloc(sizeof(OIRC), GFP_KERNEL); >> + >> + if (copy_from_user(reqs[i], preq, sizeof(OIRC))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + ((POIRC)(reqs[i]))->ir_clustersize = osb->s_clustersize; >> + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; >> + >> + if (copy_to_user(preq, reqs[i], sizeof(OIRC))) { >> + ret = EFAULT; > > -EFAULT. > >> + goto bail; >> + } >> + >> + break; >> + case OCFS2_INFO_BLOCKSIZE: >> + reqs[i] = kmalloc(sizeof(OIRB), GFP_KERNEL); >> + >> + if (copy_from_user(reqs[i], preq, sizeof(OIRB))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + ((POIRB)(reqs[i]))->ir_blocksize = >> + inode->i_sb->s_blocksize; >> + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; >> + >> + if (copy_to_user(preq, reqs[i], sizeof(OIRB))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + break; >> + case OCFS2_INFO_SLOTNUM: >> + reqs[i] = kmalloc(sizeof(OIRS), GFP_KERNEL); >> + >> + if (copy_from_user(reqs[i], preq, sizeof(OIRS))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + ((POIRS)(reqs[i]))->ir_slotnum = osb->max_slots; >> + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; >> + >> + if (copy_to_user(preq, reqs[i], sizeof(OIRS))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + break; >> + case OCFS2_INFO_LABEL: >> + reqs[i] = kmalloc(sizeof(OIRL), GFP_KERNEL); >> + >> + if (copy_from_user(reqs[i], preq, sizeof(OIRL))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + memmove(((POIRL)(reqs[i]))->ir_label, osb->vol_label, >> + OCFS2_MAX_VOL_LABEL_LEN); > > Why memmove() when memcpy() will do? > >> + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; >> + >> + if (copy_to_user(preq, reqs[i], sizeof(OIRL))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + break; >> + case OCFS2_INFO_UUID: >> + reqs[i] = kmalloc(sizeof(OIRU), GFP_KERNEL); >> + >> + if (copy_from_user(reqs[i], preq, sizeof(OIRU))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + memmove(((POIRU)(reqs[i]))->ir_uuid, osb->uuid, >> + OCFS2_VOL_UUID_LEN); >> + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; >> + >> + if (copy_to_user(preq, reqs[i], sizeof(OIRU))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + break; >> + case OCFS2_INFO_FS_FEATURES: >> + reqs[i] = kmalloc(sizeof(OIRF), GFP_KERNEL); >> + >> + if (copy_from_user(reqs[i], preq, sizeof(OIRF))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + ((POIRF)(reqs[i]))->ir_compat_features = >> + osb->s_feature_compat; >> + ((POIRF)(reqs[i]))->ir_incompat_features = >> + osb->s_feature_incompat; >> + ((POIRF)(reqs[i]))->ir_ro_compat_features = >> + osb->s_feature_ro_compat; >> + reqs[i]->ir_flags |= OCFS2_INFO_FL_FILLED; >> + >> + if (copy_to_user(preq, reqs[i], sizeof(OIRF))) { >> + ret = -EFAULT; >> + goto bail; >> + } >> + >> + break; >> + default: >> + break; >> + } >> + >> + } >> +bail: >> + for (i = 0; i < num_reqs; i++) >> + kfree(reqs[i]); >> + >> + kfree(reqs); >> + >> + ocfs2_inode_unlock(inode, 0); >> + >> + mlog_exit(ret); >> + return ret; >> +} >> + >> long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) >> { >> struct inode *inode = filp->f_path.dentry->d_inode; >> @@ -173,6 +355,8 @@ long ocfs2_ioctl(struct file *filp, unsigned int >> cmd, unsigned long arg) >> preserve = (args.preserve != 0); >> >> return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve); >> + case OCFS2_IOC_INFO: >> + return ocfs2_get_request_info(inode, arg); >> default: >> return -ENOTTY; >> } > ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2. 2009-12-01 3:20 ` Tristan @ 2009-12-02 0:35 ` Joel Becker 2009-12-02 2:29 ` Tristan 0 siblings, 1 reply; 14+ messages in thread From: Joel Becker @ 2009-12-02 0:35 UTC (permalink / raw) To: ocfs2-devel On Tue, Dec 01, 2009 at 11:20:25AM +0800, Tristan wrote: > Size of each request may differ from type to type, think about: Yes, but you are not passing an array of structures, you are passing an array of pointers. Andi's right, of course, that NULL termination is rife for abuse. Let's pass it via a wrapper structure: --------------------------------------------------------------------------- struct ocfs2_info { __u64 info_requests; /* Array of __u64 pointers to our requests */ __u32 info_count; /* Number of requests in info_requests */ }; --------------------------------------------------------------------------- Why is info_requests a __u64? Because we have to handle 32bit binaries on 64bit systems. In fact, info_requests is an array of 64bit values. See the example below. I've also modified struct ocfs2_info_request to have a magic number and a size: --------------------------------------------------------------------------- /* Magic number of all requests */ #define OCFS2_INFO_MAGIC 0x4F32494E /* Magic number for requests */ struct ocfs2_info_request { /*00*/ __u32 ir_magic; /* Magic number */ __u32 ir_code; /* Info request code */ __u32 ir_size; /* Size of request */ __u32 ir_flags; /* Request flags */ /*10*/ /* Request specific fields */ }; --------------------------------------------------------------------------- A user now does: --------------------------------------------------------------------------- get_info(fd) { struct ocfs2_info_request_clustersize crq = { .ir_magic = OCFS2_INFO_MAGIC, .ir_code = OCFS2_INFO_REQUEST_CLUSTERSIZE, .ir_size = sizeof(struct ocfs2Info_request_clustersize), }; struct ocfs2_info_request_blocksize brq = { .ir_magic = OCFS2_INFO_MAGIC, .ir_code = OCFS2_INFO_REQUEST_BLOCKSIZE, .ir_size = sizeof(struct ocfs2Info_request_blocksize), }; uint64_t info_array[2] = {(unsigned long)&crq, (unsigned long)&brq}; struct ocfs2_info info = { .info_requests = info_array, .info_count = 2, }; rc = ioctl(fd, OCFS2_IOC_INFO, &info); ... } --------------------------------------------------------------------------- o2info can create convenience wrappers for setting the magic number, code, and size, of course. Note that we cast the request pointers to unsigned longs so that the promotion from ulong to uint64_t is handled by C. This code works on 32bit and 64bit. How does the kernel handle this? One by one, of course. First we copy_from_user() the ocfs2_info structure. We check that info_count is within some sane boundary (let's say 50 requests) so that someone doesn't pass one million. We also check that info_requests is not NULL. Both allow us to return -EINVAL. Then, for each request in the array, we grab the header, validate it, and call a handler function: -------------------------------------------------------------------------- u64 req_addr; struct ocfs2_info_request __user *reqp; for (i = 0; i < info->info_count; i++) { ret = -EFAULT; if (get_user(&req_addr, (unsigned long)((u64**)(info->info_requests) + i))) break; reqp = (struct ocfs2_info_request *)(unsigned long)req_addr; ret = ocfs2_info_handle(file, reqp); if (ret) break; } -------------------------------------------------------------------------- Now we have a pointer to the *userspace* memory, we need to get the header into kernelspace: -------------------------------------------------------------------------- int ocfs2_info_handle(file, struct ocfs2_info_request __user *reqp) { int ret = 0; struct ocfs2_info_request req; if (copy_from_user(&req, reqp, sizeof(struct ocfs2_info_request))) return -EFAULT; if (req.ir_magic != OCFS2_INFO_MAGIC) return -EINVAL; switch (req.ir_code) { case OCFS2_INFO_REQUEST_CLUSTERSIZE: if (req.ir_size != sizeof(struct ocfs2_info_request_clustersize)) { ret = -EINVAL; break; } ret = ocfs2_info_handle_clustersize(file, reqp, &req); break; ... default: /* Unknown code */ ret = -EINVAL; break; } return ret; } -------------------------------------------------------------------------- We can probably wrap the switch into a lookup table, but let's not worry about that for now. As you can see, we've validated the info structure, then we validate each request as we get it. We check the magic, the code, and the size to make sure userspace is doing it right. Only then do we actually call the handler function. Some handler functions will need information from the extra portion of the request. Simple ones will not, and need not copy anything else from userspace. The clustersize handler is an example. It needs no more information from the passed request. It just needs to fill in the result to userspace. -------------------------------------------------------------------------- int ocfs2_info_handle_clustersize(struct file *file, struct ocfs2_info_request __user *user_req, struct ocfs2_info_request *req) { struct ocfs2_info_request_clustersize result; memcpy(&result, req, sizeof(struct ocfs2_info_request)); result.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED; result.ir_clustersize = OCFS2_SB(file->f_dentry->d_inode->i_sb)->s_clutsersize; if (copy_to_user((struct ocfs2_info_request_clustersize __user *)user_req, &result, sizeof(struct ocfs2_info_request_clustersize))) return -EFAULT; return 0; } -------------------------------------------------------------------------- Done right, we can switch between a single ioctl with an array of requests, a single ioctl with one request per call, and even multiple ioctls, all without changing the handler functions. Whee! Joel -- Life's Little Instruction Book #511 "Call your mother." Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2. 2009-12-02 0:35 ` Joel Becker @ 2009-12-02 2:29 ` Tristan 2009-12-02 18:28 ` Joel Becker 0 siblings, 1 reply; 14+ messages in thread From: Tristan @ 2009-12-02 2:29 UTC (permalink / raw) To: ocfs2-devel Joel Becker wrote: > On Tue, Dec 01, 2009 at 11:20:25AM +0800, Tristan wrote: > >> Size of each request may differ from type to type, think about: >> > > Yes, but you are not passing an array of structures, you are > passing an array of pointers. Andi's right, of course, that NULL > termination is rife for abuse. Let's pass it via a wrapper structure: > > --------------------------------------------------------------------------- > struct ocfs2_info { > __u64 info_requests; /* Array of __u64 pointers to our requests */ > __u32 info_count; /* Number of requests in info_requests */ > }; > --------------------------------------------------------------------------- > > Why is info_requests a __u64? Because we have to handle 32bit > binaries on 64bit systems. In fact, info_requests is an array of 64bit > values. See the example below. > I've also modified struct ocfs2_info_request to have a magic > number and a size: > > --------------------------------------------------------------------------- > /* Magic number of all requests */ > #define OCFS2_INFO_MAGIC 0x4F32494E /* Magic number for requests */ > > struct ocfs2_info_request { > /*00*/ __u32 ir_magic; /* Magic number */ > Why we need a magic here, just to improve the security? > __u32 ir_code; /* Info request code */ > __u32 ir_size; /* Size of request */ > __u32 ir_flags; /* Request flags */ > /*10*/ /* Request specific fields */ > }; > --------------------------------------------------------------------------- > > A user now does: > > --------------------------------------------------------------------------- > get_info(fd) > { > struct ocfs2_info_request_clustersize crq = { > .ir_magic = OCFS2_INFO_MAGIC, > .ir_code = OCFS2_INFO_REQUEST_CLUSTERSIZE, > .ir_size = sizeof(struct ocfs2Info_request_clustersize), > }; > struct ocfs2_info_request_blocksize brq = { > .ir_magic = OCFS2_INFO_MAGIC, > .ir_code = OCFS2_INFO_REQUEST_BLOCKSIZE, > .ir_size = sizeof(struct ocfs2Info_request_blocksize), > }; > uint64_t info_array[2] = {(unsigned long)&crq, (unsigned long)&brq}; > struct ocfs2_info info = { > .info_requests = info_array, > .info_count = 2, > }; > > rc = ioctl(fd, OCFS2_IOC_INFO, &info); > ... > } > --------------------------------------------------------------------------- > > o2info can create convenience wrappers for setting the magic > number, code, and size, of course. Note that we cast the request > pointers to unsigned longs so that the promotion from ulong to uint64_t > is handled by C. This code works on 32bit and 64bit. > Just a confusion here, uint64_t should be 'unsigned long long' on 32bits arch, and 'unsigned long' on 64bits arch, why we not use: uint64_t info_array[2] = {(unsigned long long)&crq, (unsigned long long)&brq}; > How does the kernel handle this? One by one, of course. First > we copy_from_user() the ocfs2_info structure. We check that info_count > is within some sane boundary (let's say 50 requests) so that someone > doesn't pass one million. We also check that info_requests is not > NULL. Both allow us to return -EINVAL. > Then, for each request in the array, we grab the header, > validate it, and call a handler function: > > -------------------------------------------------------------------------- > u64 req_addr; > struct ocfs2_info_request __user *reqp; > > for (i = 0; i < info->info_count; i++) { > ret = -EFAULT; > if (get_user(&req_addr, > (unsigned long)((u64**)(info->info_requests) + i))) > break; > reqp = (struct ocfs2_info_request *)(unsigned long)req_addr; > ret = ocfs2_info_handle(file, reqp); > if (ret) > break; > } > -------------------------------------------------------------------------- > How to avoid accessing an arbitrary mem address, such as, userspace passing a wrong info_count(correct_info_count + 1), so the last entry for pointer should be invalid, how to avoid to access that? > Now we have a pointer to the *userspace* memory, we need to get > the header into kernelspace: > > -------------------------------------------------------------------------- > int ocfs2_info_handle(file, struct ocfs2_info_request __user *reqp) > { > int ret = 0; > struct ocfs2_info_request req; > > if (copy_from_user(&req, reqp, sizeof(struct ocfs2_info_request))) > return -EFAULT; > > if (req.ir_magic != OCFS2_INFO_MAGIC) > return -EINVAL; > switch (req.ir_code) { > case OCFS2_INFO_REQUEST_CLUSTERSIZE: > if (req.ir_size != sizeof(struct ocfs2_info_request_clustersize)) { > ret = -EINVAL; > break; > } > ret = ocfs2_info_handle_clustersize(file, reqp, &req); > break; > > ... > > default: > /* Unknown code */ > ret = -EINVAL; > break; > } > > return ret; > } > -------------------------------------------------------------------------- > > We can probably wrap the switch into a lookup table, but let's > not worry about that for now. > As you can see, we've validated the info structure, then we > validate each request as we get it. We check the magic, the code, and > the size to make sure userspace is doing it right. Only then do we > actually call the handler function. > Some handler functions will need information from the extra > portion of the request. Simple ones will not, and need not copy > anything else from userspace. The clustersize handler is an example. > It needs no more information from the passed request. It just needs to > fill in the result to userspace. > > -------------------------------------------------------------------------- > int ocfs2_info_handle_clustersize(struct file *file, > struct ocfs2_info_request __user *user_req, > struct ocfs2_info_request *req) > { > struct ocfs2_info_request_clustersize result; > > memcpy(&result, req, sizeof(struct ocfs2_info_request)); > result.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED; > result.ir_clustersize = > OCFS2_SB(file->f_dentry->d_inode->i_sb)->s_clutsersize; > > if (copy_to_user((struct ocfs2_info_request_clustersize __user *)user_req, > &result, > sizeof(struct ocfs2_info_request_clustersize))) > return -EFAULT; > return 0; > } > -------------------------------------------------------------------------- > > Done right, we can switch between a single ioctl with an array > of requests, a single ioctl with one request per call, and even multiple > ioctls, all without changing the handler functions. Whee! > The scheme above was fine to me:-) > Joel > > ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2. 2009-12-02 2:29 ` Tristan @ 2009-12-02 18:28 ` Joel Becker 0 siblings, 0 replies; 14+ messages in thread From: Joel Becker @ 2009-12-02 18:28 UTC (permalink / raw) To: ocfs2-devel On Wed, Dec 02, 2009 at 10:29:28AM +0800, Tristan wrote: > Joel Becker wrote: > > struct ocfs2_info_request { > > /*00*/ __u32 ir_magic; /* Magic number */ > > > > Why we need a magic here, just to improve the security? Not for security - a malicious user can always fake the magic. We need to solve security anyway. The magic number is for convenience. It catches stupid errors quickly. > > --------------------------------------------------------------------------- > > get_info(fd) > > { > > struct ocfs2_info_request_clustersize crq = { > > .ir_magic = OCFS2_INFO_MAGIC, > > .ir_code = OCFS2_INFO_REQUEST_CLUSTERSIZE, > > .ir_size = sizeof(struct ocfs2Info_request_clustersize), > > }; > > struct ocfs2_info_request_blocksize brq = { > > .ir_magic = OCFS2_INFO_MAGIC, > > .ir_code = OCFS2_INFO_REQUEST_BLOCKSIZE, > > .ir_size = sizeof(struct ocfs2Info_request_blocksize), > > }; > > uint64_t info_array[2] = {(unsigned long)&crq, (unsigned long)&brq}; > > struct ocfs2_info info = { > > .info_requests = info_array, > > .info_count = 2, > > }; > > > > rc = ioctl(fd, OCFS2_IOC_INFO, &info); > > ... > > } > > --------------------------------------------------------------------------- > > > > o2info can create convenience wrappers for setting the magic > > number, code, and size, of course. Note that we cast the request > > pointers to unsigned longs so that the promotion from ulong to uint64_t > > is handled by C. This code works on 32bit and 64bit. > > > > Just a confusion here, uint64_t should be 'unsigned long long' on 32bits > arch, and 'unsigned long' on 64bits arch, why we not use: > > > uint64_t info_array[2] = {(unsigned long long)&crq, (unsigned long long)&brq}; We're casting pointers. A pointer is always the size of an unsigned long. By casting the pointers to unsigned long, we change them from pointer types to integer types. On 32bit this will be a 32bit value, on 64bit a 64bit one. That's all the casting does. The C compiler will notice we're assigning to uint64_t and promote any 32bit value for us. > > -------------------------------------------------------------------------- > > u64 req_addr; > > struct ocfs2_info_request __user *reqp; > > > > for (i = 0; i < info->info_count; i++) { > > ret = -EFAULT; > > if (get_user(&req_addr, > > (unsigned long)((u64**)(info->info_requests) + i))) > > break; > > reqp = (struct ocfs2_info_request *)(unsigned long)req_addr; > > ret = ocfs2_info_handle(file, reqp); > > if (ret) > > break; > > } > > -------------------------------------------------------------------------- > > > > How to avoid accessing an arbitrary mem address, such as, userspace > passing a wrong info_count(correct_info_count + 1), so the last entry > for pointer should be invalid, how to avoid to access that? get_user() will return non-zero if the address is not part of the program's valid memory. We then break and return -EFAULT, causing a segfault. Note that we're checking two things. In this function, we copy the userspace pointer into reqp. We haven't actually accessed the memory at reqp, we've just copied the pointer from info->info_requests. So here we've checked that info->info_requests+i is a valid memory location. In ocfs2_info_handle(), we then copy the actual request from reqp. There copy_from_user() is checking that reqp points to valid memory before copying it. Btw, the above function should be checking that reqp is not NULL ;-) Joel -- "Vote early and vote often." - Al Capone Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Ocfs2-devel] [PATCH 1/2] Ocfs2: Add new ioctls prototype and corresponding data structure to ocfs2 header. 2009-11-27 8:24 [Ocfs2-devel] [PATCH 1/2] Ocfs2: Add new ioctls prototype and corresponding data structure to ocfs2 header Tristan Ye 2009-11-27 8:24 ` [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2 Tristan Ye @ 2009-11-30 22:57 ` Sunil Mushran 2009-12-01 1:15 ` Tristan 1 sibling, 1 reply; 14+ messages in thread From: Sunil Mushran @ 2009-11-30 22:57 UTC (permalink / raw) To: ocfs2-devel Tristan Ye wrote: > We use OCFS2_IOC_INFO to manipulate the new ioctl, the requests can > be stored in a array of (struct ocfs2_info_request), which should be > NULL-terminated, kernel therefore can recoginze and fill them one by > one. > > The reason why we need these ioctls is to offer the none-privileged > end-user a possibility to get filesys info gathering. > > Idea here is to make the spearated request small enough to guarantee > a better backward&forward compatibility since a small piece of request > would be less likely to be broken if filesys on raw disk get changed. > > Currently, the first version include following request type: > > OCFS2_INFO_CLUSTERSIZE, > OCFS2_INFO_BLOCKSIZE, > OCFS2_INFO_SLOTNUM, > OCFS2_INFO_LABEL, > OCFS2_INFO_UUID, > OCFS2_INFO_FS_FEATURES > > It may be grown from time to time:) > > Signed-off-by: Tristan Ye <tristan.ye@oracle.com> > --- > fs/ocfs2/ocfs2_fs.h | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 files changed, 69 insertions(+), 0 deletions(-) > > diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h > index e9431e4..dac7f4c 100644 > --- a/fs/ocfs2/ocfs2_fs.h > +++ b/fs/ocfs2/ocfs2_fs.h > @@ -309,6 +309,75 @@ struct reflink_arguments { > }; > #define OCFS2_IOC_REFLINK _IOW('o', 4, struct reflink_arguments) > > +#define OCFS2_UUID_LEN 16 > +#define OCFS2_MAX_LABEL_LEN 64 > What's wrong with the existing OCFS2_MAX_VOL_LABEL_LEN? For UUID, maybe define: #define OCFS2_VOL_UUIDSTR_LEN (OCFS2_VOL_UUID_LEN * 2 + 1) > + > +/* > + * Used to query fs information for none-privileged > + * users by using OCFS2_IOC_INFO ioctols, a series of > + * following requests need to be NULL-terminated. > + * > + * Always try to separate info request into small pieces to > + * guarantee the backward&forward compatibility. > + */ > +struct ocfs2_info_request { > + __u32 ir_code; /* Info request code */ > + __u32 ir_flags; /* Request flags */ > +}; > + Rename it ocfs2_info. Then you have ocfs2_info_clustersize. In the next patch you have typedef-ed these structures presumably because the structure name is long. Instead, shorten the name and use it as is. BTW, typedef-ing struct is strongly discouraged in the kernel coding standards. For good reason. > +struct ocfs2_info_request_clustersize { > + struct ocfs2_info_request ir_request; > + __u32 ir_clustersize; > +}; > + > +struct ocfs2_info_request_blocksize { > + struct ocfs2_info_request ir_request; > + __u32 ir_blocksize; > +}; > + > +struct ocfs2_info_request_slotnum { > + struct ocfs2_info_request ir_request; > + __u16 ir_slotnum; > +}; > + > +struct ocfs2_info_request_label { > + struct ocfs2_info_request ir_request; > + __u8 ir_label[OCFS2_MAX_LABEL_LEN]; > +}; > + > +struct ocfs2_info_request_uuid { > + struct ocfs2_info_request ir_request; > + __u8 ir_uuid[OCFS2_UUID_LEN]; > +}; > Return the uuid string as that is what we use everywhere. > + > +struct ocfs2_info_request_fs_features { > + struct ocfs2_info_request ir_quest; > + __u32 ir_compat_features; > + __u32 ir_incompat_features; > + __u32 ir_ro_compat_features; > +}; > + > +/* Codes for cfs2_info_request */ > + > +#define OCFS2_INFO_CLUSTERSIZE 0x00000001 > +#define OCFS2_INFO_BLOCKSIZE 0x00000002 > +#define OCFS2_INFO_SLOTNUM 0x00000004 > +#define OCFS2_INFO_LABEL 0x00000008 > +#define OCFS2_INFO_UUID 0x00000010 > +#define OCFS2_INFO_FS_FEATURES 0x00000020 > Fix typos, white-spaces, etc. Make use of tabs. > + > +/* Flags for struct ocfs2_info_request */ > +/* Filled by the caller */ > +#define OCFS2_INFO_FL_NON_COHERENT 0x00000001 /* Cluster coherency not required. > + This is a hint. It is up to > + ocfs2 whether the request can > + be fulfilled without locking. */ > +/* Filled by ocfs2 */ > +#define OCFS2_INFO_FL_FILLED 0x80000000 /* Filesystem understood this > + request and filled in the > + answer */ > + > +#define OCFS2_IOC_INFO _IOR('o', 5, struct ocfs2_info_request) > > /* > * Journal Flags (ocfs2_dinode.id1.journal1.i_flags) > ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Ocfs2-devel] [PATCH 1/2] Ocfs2: Add new ioctls prototype and corresponding data structure to ocfs2 header. 2009-11-30 22:57 ` [Ocfs2-devel] [PATCH 1/2] Ocfs2: Add new ioctls prototype and corresponding data structure to ocfs2 header Sunil Mushran @ 2009-12-01 1:15 ` Tristan 0 siblings, 0 replies; 14+ messages in thread From: Tristan @ 2009-12-01 1:15 UTC (permalink / raw) To: ocfs2-devel Sunil Mushran wrote: > Tristan Ye wrote: >> We use OCFS2_IOC_INFO to manipulate the new ioctl, the requests can >> be stored in a array of (struct ocfs2_info_request), which should be >> NULL-terminated, kernel therefore can recoginze and fill them one by >> one. >> >> The reason why we need these ioctls is to offer the none-privileged >> end-user a possibility to get filesys info gathering. >> >> Idea here is to make the spearated request small enough to guarantee >> a better backward&forward compatibility since a small piece of request >> would be less likely to be broken if filesys on raw disk get changed. >> >> Currently, the first version include following request type: >> >> OCFS2_INFO_CLUSTERSIZE, >> OCFS2_INFO_BLOCKSIZE, >> OCFS2_INFO_SLOTNUM, >> OCFS2_INFO_LABEL, >> OCFS2_INFO_UUID, >> OCFS2_INFO_FS_FEATURES >> >> It may be grown from time to time:) >> >> Signed-off-by: Tristan Ye <tristan.ye@oracle.com> >> --- >> fs/ocfs2/ocfs2_fs.h | 69 >> +++++++++++++++++++++++++++++++++++++++++++++++++++ >> 1 files changed, 69 insertions(+), 0 deletions(-) >> >> diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h >> index e9431e4..dac7f4c 100644 >> --- a/fs/ocfs2/ocfs2_fs.h >> +++ b/fs/ocfs2/ocfs2_fs.h >> @@ -309,6 +309,75 @@ struct reflink_arguments { >> }; >> #define OCFS2_IOC_REFLINK _IOW('o', 4, struct reflink_arguments) >> >> +#define OCFS2_UUID_LEN 16 >> +#define OCFS2_MAX_LABEL_LEN 64 > > What's wrong with the existing OCFS2_MAX_VOL_LABEL_LEN? > For UUID, maybe define: > #define OCFS2_VOL_UUIDSTR_LEN (OCFS2_VOL_UUID_LEN * 2 + 1) The existing OCFS2_MAX_VOL_LABEL_LEN macro was placed after definitions of ioctls, and I just want all of the ioctls to gather in the same place. Anyway, I'll move these ioctls properly. > >> + >> +/* >> + * Used to query fs information for none-privileged + * users by >> using OCFS2_IOC_INFO ioctols, a series of >> + * following requests need to be NULL-terminated. >> + * >> + * Always try to separate info request into small pieces to >> + * guarantee the backward&forward compatibility. >> + */ >> +struct ocfs2_info_request { >> + __u32 ir_code; /* Info request code */ >> + __u32 ir_flags; /* Request flags */ >> +}; >> + > > Rename it ocfs2_info. Then you have ocfs2_info_clustersize. In the next > patch you have typedef-ed these structures presumably because the > structure > name is long. Instead, shorten the name and use it as is. BTW, > typedef-ing > struct is strongly discouraged in the kernel coding standards. For > good reason. Good idea, I was always being annoyed by the length of these structures. > >> +struct ocfs2_info_request_clustersize { >> + struct ocfs2_info_request ir_request; >> + __u32 ir_clustersize; >> +}; >> + >> +struct ocfs2_info_request_blocksize { >> + struct ocfs2_info_request ir_request; >> + __u32 ir_blocksize; >> +}; >> + >> +struct ocfs2_info_request_slotnum { >> + struct ocfs2_info_request ir_request; >> + __u16 ir_slotnum; >> +}; >> + >> +struct ocfs2_info_request_label { >> + struct ocfs2_info_request ir_request; >> + __u8 ir_label[OCFS2_MAX_LABEL_LEN]; >> +}; >> + >> +struct ocfs2_info_request_uuid { >> + struct ocfs2_info_request ir_request; >> + __u8 ir_uuid[OCFS2_UUID_LEN]; >> +}; > > Return the uuid string as that is what we use everywhere. Not quite sure why we want to keep the 32bytes uuid_str instead of 16btyes uuid? > >> + >> +struct ocfs2_info_request_fs_features { >> + struct ocfs2_info_request ir_quest; >> + __u32 ir_compat_features; >> + __u32 ir_incompat_features; >> + __u32 ir_ro_compat_features; >> +}; >> + >> +/* Codes for cfs2_info_request */ >> + >> +#define OCFS2_INFO_CLUSTERSIZE 0x00000001 >> +#define OCFS2_INFO_BLOCKSIZE 0x00000002 >> +#define OCFS2_INFO_SLOTNUM 0x00000004 >> +#define OCFS2_INFO_LABEL 0x00000008 >> +#define OCFS2_INFO_UUID 0x00000010 >> +#define OCFS2_INFO_FS_FEATURES 0x00000020 > > Fix typos, white-spaces, etc. Make use of tabs. > >> + >> +/* Flags for struct ocfs2_info_request */ >> +/* Filled by the caller */ >> +#define OCFS2_INFO_FL_NON_COHERENT 0x00000001 /* Cluster coherency >> not required. >> + This is a hint. It is up to >> + ocfs2 whether the request can >> + be fulfilled without locking. */ >> +/* Filled by ocfs2 */ >> +#define OCFS2_INFO_FL_FILLED 0x80000000 /* Filesystem understood this >> + request and filled in the >> + answer */ >> + >> +#define OCFS2_IOC_INFO _IOR('o', 5, struct ocfs2_info_request) >> >> /* >> * Journal Flags (ocfs2_dinode.id1.journal1.i_flags) > ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2009-12-02 18:28 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2009-11-27 8:24 [Ocfs2-devel] [PATCH 1/2] Ocfs2: Add new ioctls prototype and corresponding data structure to ocfs2 header Tristan Ye 2009-11-27 8:24 ` [Ocfs2-devel] [PATCH 2/2] Ocfs2: Implement new OCFS2_IOC_INFO ioctl for ocfs2 Tristan Ye 2009-11-27 20:23 ` Andi Kleen 2009-11-28 2:49 ` Tristan 2009-11-30 9:36 ` Joel Becker 2009-11-30 23:49 ` Sunil Mushran 2009-12-01 1:25 ` Tristan 2009-12-02 0:16 ` Sunil Mushran 2009-12-01 3:20 ` Tristan 2009-12-02 0:35 ` Joel Becker 2009-12-02 2:29 ` Tristan 2009-12-02 18:28 ` Joel Becker 2009-11-30 22:57 ` [Ocfs2-devel] [PATCH 1/2] Ocfs2: Add new ioctls prototype and corresponding data structure to ocfs2 header Sunil Mushran 2009-12-01 1:15 ` Tristan
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.