From 19a76a56775630855a75508ad837aedaf9f6bdcd Mon Sep 17 00:00:00 2001 From: Rahul Deshmukh Date: Mon, 5 Apr 2010 11:50:40 -0700 Subject: [PATCH] b=21502 symlink compatibility between 1.6 and 2.0 Fixed the symlink compatibility problem between 1.6/1.8 and 2.0 related to short symlinks. i=bzzz i=andreas i=pravin --- lustre/osd/osd_handler.c | 169 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 160 insertions(+), 9 deletions(-) diff --git a/lustre/osd/osd_handler.c b/lustre/osd/osd_handler.c index 56b2266..cf60236 100644 --- a/lustre/osd/osd_handler.c +++ b/lustre/osd/osd_handler.c @@ -2346,20 +2346,162 @@ static const struct dt_object_operations osd_obj_ea_ops = { * * which doesn't work for globally shared files like /last-received. */ -int fsfilt_ldiskfs_read(struct inode *inode, void *buf, int size, loff_t *offs); -int fsfilt_ldiskfs_write_handle(struct inode *inode, void *buf, int bufsize, - loff_t *offs, handle_t *handle); +static int osd_ldiskfs_readlink(struct inode *inode, char *buffer, int buflen) +{ + struct ldiskfs_inode_info *ei = LDISKFS_I(inode); + + memcpy(buffer, (char*)ei->i_data, buflen); + + return buflen; +} + +static int osd_ldiskfs_read(struct inode *inode, void *buf, int size, + loff_t *offs) +{ + struct buffer_head *bh; + unsigned long block; + int osize = size; + int blocksize; + int csize; + int boffs; + int err; + + /* prevent reading after eof */ + spin_lock(&inode->i_lock); + if (i_size_read(inode) < *offs + size) { + size = i_size_read(inode) - *offs; + spin_unlock(&inode->i_lock); + if (size < 0) { + CDEBUG(D_EXT2, "size %llu is too short to read @%llu\n", + i_size_read(inode), *offs); + return -EBADR; + } else if (size == 0) { + return 0; + } + } else { + spin_unlock(&inode->i_lock); + } + + blocksize = 1 << inode->i_blkbits; + + while (size > 0) { + block = *offs >> inode->i_blkbits; + boffs = *offs & (blocksize - 1); + csize = min(blocksize - boffs, size); + bh = ldiskfs_bread(NULL, inode, block, 0, &err); + if (!bh) { + CERROR("can't read block: %d\n", err); + return err; + } + + memcpy(buf, bh->b_data + boffs, csize); + brelse(bh); + + *offs += csize; + buf += csize; + size -= csize; + } + return osize; +} static ssize_t osd_read(const struct lu_env *env, struct dt_object *dt, struct lu_buf *buf, loff_t *pos, struct lustre_capa *capa) { - struct inode *inode = osd_dt_obj(dt)->oo_inode; + struct osd_object *obj = osd_dt_obj(dt); + struct inode *inode = obj->oo_inode; + int rc; if (osd_object_auth(env, dt, capa, CAPA_OPC_BODY_READ)) RETURN(-EACCES); - return fsfilt_ldiskfs_read(inode, buf->lb_buf, buf->lb_len, pos); + /* Read small symlink from inode body as we need to maintain correct + * on-disk symlinks for ldiskfs. + */ + if (S_ISLNK(obj->oo_dt.do_lu.lo_header->loh_attr) && + (buf->lb_len <= sizeof (LDISKFS_I(inode)->i_data))) + rc = osd_ldiskfs_readlink(inode, buf->lb_buf, buf->lb_len); + else + rc = osd_ldiskfs_read(inode, buf->lb_buf, buf->lb_len, pos); + + return rc; +} + +static int osd_ldiskfs_writelink(struct inode *inode, char *buffer, int buflen) +{ + + memcpy((char*)&LDISKFS_I(inode)->i_data, (char *)buffer, + buflen); + LDISKFS_I(inode)->i_disksize = buflen; + i_size_write(inode, buflen); + inode->i_sb->s_op->dirty_inode(inode); + + return 0; +} + +static int osd_ldiskfs_write_record(struct inode *inode, void *buf, int bufsize, + loff_t *offs, handle_t *handle) +{ + struct buffer_head *bh = NULL; + loff_t offset = *offs; + loff_t new_size = i_size_read(inode); + unsigned long block; + int blocksize = 1 << inode->i_blkbits; + int err = 0; + int size; + int boffs; + + while (bufsize > 0) { + if (bh != NULL) + brelse(bh); + + block = offset >> inode->i_blkbits; + boffs = offset & (blocksize - 1); + size = min(blocksize - boffs, bufsize); + bh = ldiskfs_bread(handle, inode, block, 1, &err); + if (!bh) { + CERROR("can't read/create block: %d\n", err); + break; + } + + err = ldiskfs_journal_get_write_access(handle, bh); + if (err) { + CERROR("journal_get_write_access() returned error %d\n", + err); + break; + } + LASSERTF(boffs + size <= bh->b_size, + "boffs %d size %d bh->b_size %lu", + boffs, size, (unsigned long)bh->b_size); + memcpy(bh->b_data + boffs, buf, size); + err = ldiskfs_journal_dirty_metadata(handle, bh); + if (err) + break; + + if (offset + size > new_size) + new_size = offset + size; + offset += size; + bufsize -= size; + buf += size; + } + if (bh) + brelse(bh); + + /* correct in-core and on-disk sizes */ + if (new_size > i_size_read(inode)) { + spin_lock(&inode->i_lock); + if (new_size > i_size_read(inode)) + i_size_write(inode, new_size); + if (i_size_read(inode) > LDISKFS_I(inode)->i_disksize) { + LDISKFS_I(inode)->i_disksize = i_size_read(inode); + inode->i_sb->s_op->dirty_inode(inode); + } + spin_unlock(&inode->i_lock); + } + + if (err == 0) + *offs = offset; + return err; } static ssize_t osd_write(const struct lu_env *env, struct dt_object *dt, @@ -2367,9 +2509,10 @@ static ssize_t osd_write(const struct lu_env *env, struct dt_object *dt, struct thandle *handle, struct lustre_capa *capa, int ignore_quota) { - struct inode *inode = osd_dt_obj(dt)->oo_inode; + struct osd_object *obj = osd_dt_obj(dt); + struct inode *inode = obj->oo_inode; struct osd_thandle *oh; - ssize_t result; + ssize_t result = 0; #ifdef HAVE_QUOTA_SUPPORT cfs_cap_t save = current->cap_effective; #endif @@ -2387,8 +2530,16 @@ static ssize_t osd_write(const struct lu_env *env, struct dt_object *dt, else current->cap_effective &= ~CFS_CAP_SYS_RESOURCE_MASK; #endif - result = fsfilt_ldiskfs_write_handle(inode, buf->lb_buf, buf->lb_len, - pos, oh->ot_handle); + /* Write small symlink to inode body as we need to maintain correct + * on-disk symlinks for ldiskfs. + */ + if(S_ISLNK(obj->oo_dt.do_lu.lo_header->loh_attr) && + (buf->lb_len < sizeof (LDISKFS_I(inode)->i_data))) + result = osd_ldiskfs_writelink(inode, buf->lb_buf, buf->lb_len); + else + result = osd_ldiskfs_write_record(inode, buf->lb_buf, + buf->lb_len, pos, + oh->ot_handle); #ifdef HAVE_QUOTA_SUPPORT current->cap_effective = save; #endif -- 1.8.3.1