*
* 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,
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
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