Skip to content
Snippets Groups Projects
  • huanghua's avatar
    fd908da9
    b=14149 · fd908da9
    huanghua authored
    i=yong.fan
    i=rahul.deshmukh
    i=nikita.danilov
    
    - use req_capsule interface for client.
    - add some interoperability support on server side.
    fd908da9
    History
    b=14149
    huanghua authored
    i=yong.fan
    i=rahul.deshmukh
    i=nikita.danilov
    
    - use req_capsule interface for client.
    - add some interoperability support on server side.
symlink.c 6.70 KiB
/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
 * vim:expandtab:shiftwidth=8:tabstop=8:
 *
 *  Copyright (c) 2002 Cluster File Systems, Inc.
 *
 *   This file is part of Lustre, http://www.lustre.org.
 *
 *   Lustre is free software; you can redistribute it and/or
 *   modify it under the terms of version 2 of the GNU General Public
 *   License as published by the Free Software Foundation.
 *
 *   Lustre is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with Lustre; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/stat.h>
#include <linux/smp_lock.h>
#include <linux/version.h>
#define DEBUG_SUBSYSTEM S_LLITE

#include <lustre_lite.h>
#include "llite_internal.h"

static int ll_readlink_internal(struct inode *inode,
                                struct ptlrpc_request **request, char **symname)
{
        struct ll_inode_info *lli = ll_i2info(inode);
        struct ll_sb_info *sbi = ll_i2sbi(inode);
        int rc, symlen = i_size_read(inode) + 1;
        struct mdt_body *body;
        struct obd_capa *oc;
        ENTRY;

        *request = NULL;

        if (lli->lli_symlink_name) {
                *symname = lli->lli_symlink_name;
                CDEBUG(D_INODE, "using cached symlink %s\n", *symname);
                RETURN(0);
        }

        oc = ll_mdscapa_get(inode);
        rc = md_getattr(sbi->ll_md_exp, ll_inode2fid(inode), oc,
                        OBD_MD_LINKNAME, symlen, request);
        capa_put(oc);
        if (rc) {
                if (rc != -ENOENT)
                        CERROR("inode %lu: rc = %d\n", inode->i_ino, rc);
                GOTO (failed, rc);
        }

        body = req_capsule_server_get(&(*request)->rq_pill, &RMF_MDT_BODY);
        LASSERT(body != NULL);
        if ((body->valid & OBD_MD_LINKNAME) == 0) {
                CERROR("OBD_MD_LINKNAME not set on reply\n");
                GOTO(failed, rc = -EPROTO);
        }
        
        LASSERT(symlen != 0);
        if (body->eadatasize != symlen) {
                CERROR("inode %lu: symlink length %d not expected %d\n",
                        inode->i_ino, body->eadatasize - 1, symlen - 1);
                GOTO(failed, rc = -EPROTO);
        }

        *symname = req_capsule_server_get(&(*request)->rq_pill, &RMF_MDT_MD);
        if (*symname == NULL ||
            strnlen(*symname, symlen) != symlen - 1) {
                /* not full/NULL terminated */
                CERROR("inode %lu: symlink not NULL terminated string"
                        "of length %d\n", inode->i_ino, symlen - 1);
                GOTO(failed, rc = -EPROTO);
        }

        OBD_ALLOC(lli->lli_symlink_name, symlen);
        /* do not return an error if we cannot cache the symlink locally */
        if (lli->lli_symlink_name) {
                memcpy(lli->lli_symlink_name, *symname, symlen);
                ptlrpc_req_finished (*request);
                *request = NULL;
                *symname = lli->lli_symlink_name;
        }

        RETURN(0);

 failed:
        ptlrpc_req_finished (*request);
        RETURN (rc);
}

static int ll_readlink(struct dentry *dentry, char *buffer, int buflen)
{
        struct inode *inode = dentry->d_inode;
        struct ll_inode_info *lli = ll_i2info(inode);
        struct ptlrpc_request *request;
        char *symname;
        int rc;
        ENTRY;

        CDEBUG(D_VFSTRACE, "VFS Op\n");
        /* on symlinks lli_open_sem protects lli_symlink_name allocation/data */
        down(&lli->lli_size_sem);
        rc = ll_readlink_internal(inode, &request, &symname);
        if (rc)
                GOTO(out, rc);

        rc = vfs_readlink(dentry, buffer, buflen, symname);
        ptlrpc_req_finished(request);
 out:
        up(&lli->lli_size_sem);
        RETURN(rc);
}

#ifdef HAVE_COOKIE_FOLLOW_LINK
# define LL_FOLLOW_LINK_RETURN_TYPE void *
#else
# define LL_FOLLOW_LINK_RETURN_TYPE int
#endif

static LL_FOLLOW_LINK_RETURN_TYPE ll_follow_link(struct dentry *dentry,
                                                 struct nameidata *nd)
{
        struct inode *inode = dentry->d_inode;
        struct ll_inode_info *lli = ll_i2info(inode);
#ifdef HAVE_VFS_INTENT_PATCHES
        struct lookup_intent *it = ll_nd2it(nd);
#endif
        struct ptlrpc_request *request;
        int rc;
        char *symname;
        ENTRY;
#ifdef HAVE_VFS_INTENT_PATCHES
        if (it != NULL) {
                int op = it->it_op;
                int mode = it->it_create_mode;

                ll_intent_release(it);
                it->it_op = op;
                it->it_create_mode = mode;
        }
#endif

        CDEBUG(D_VFSTRACE, "VFS Op\n");
        down(&lli->lli_size_sem);
        rc = ll_readlink_internal(inode, &request, &symname);
        up(&lli->lli_size_sem);
        if (rc) {
                path_release(nd); /* Kernel assumes that ->follow_link()
                                     releases nameidata on error */
                GOTO(out, rc);
        }

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8))
        rc = vfs_follow_link(nd, symname);
#else
#ifdef HAVE_COOKIE_FOLLOW_LINK
        nd_set_link(nd, symname);
        /* @symname may contain a pointer to the request message buffer,
           we delay request releasing until ll_put_link then. */
        RETURN(request);
#else
        if (request != NULL) {
                /* falling back to recursive follow link if the request
                 * needs to be cleaned up still. */
        rc = vfs_follow_link(nd, symname);
                GOTO(out, rc);
        }
        nd_set_link(nd, symname);
        RETURN(0);
#endif
#endif
out:
        ptlrpc_req_finished(request);
#ifdef HAVE_COOKIE_FOLLOW_LINK
        RETURN(ERR_PTR(rc));
#else
        RETURN(rc);
#endif
}

#ifdef HAVE_COOKIE_FOLLOW_LINK
static void ll_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
{
        ptlrpc_req_finished(cookie);
}
#endif

struct inode_operations ll_fast_symlink_inode_operations = {
        .readlink       = ll_readlink,
        .setattr        = ll_setattr,
#ifdef HAVE_VFS_INTENT_PATCHES
        .setattr_raw    = ll_setattr_raw,
#endif
        .follow_link    = ll_follow_link,
#ifdef HAVE_COOKIE_FOLLOW_LINK
        .put_link       = ll_put_link,
#endif
        .getattr        = ll_getattr,
        .permission     = ll_inode_permission,
        .setxattr       = ll_setxattr,
        .getxattr       = ll_getxattr,
        .listxattr      = ll_listxattr,
        .removexattr    = ll_removexattr,
};