Whamcloud - gitweb
b=21502 symlink compatibility between 1.6 and 2.0
[fs/lustre-release.git] / lustre / osd / osd_handler.c
index 56b2266..cf60236 100644 (file)
@@ -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