+
+ssize_t ll_copy_user_md(const struct lov_user_md __user *md,
+ struct lov_user_md **kbuf)
+{
+ struct lov_user_md lum;
+ ssize_t lum_size;
+ ENTRY;
+
+ if (copy_from_user(&lum, md, sizeof(lum)))
+ RETURN(-EFAULT);
+
+ lum_size = ll_lov_user_md_size(&lum);
+ if (lum_size < 0)
+ RETURN(lum_size);
+
+ OBD_ALLOC(*kbuf, lum_size);
+ if (*kbuf == NULL)
+ RETURN(-ENOMEM);
+
+ if (copy_from_user(*kbuf, md, lum_size) != 0) {
+ OBD_FREE(*kbuf, lum_size);
+ RETURN(-EFAULT);
+ }
+
+ RETURN(lum_size);
+}
+
+/*
+ * Compute llite root squash state after a change of root squash
+ * configuration setting or add/remove of a lnet nid
+ */
+void ll_compute_rootsquash_state(struct ll_sb_info *sbi)
+{
+ struct root_squash_info *squash = &sbi->ll_squash;
+ int i;
+ bool matched;
+ lnet_process_id_t id;
+
+ /* Update norootsquash flag */
+ down_write(&squash->rsi_sem);
+ if (list_empty(&squash->rsi_nosquash_nids))
+ sbi->ll_flags &= ~LL_SBI_NOROOTSQUASH;
+ else {
+ /* Do not apply root squash as soon as one of our NIDs is
+ * in the nosquash_nids list */
+ matched = false;
+ i = 0;
+ while (LNetGetId(i++, &id) != -ENOENT) {
+ if (LNET_NETTYP(LNET_NIDNET(id.nid)) == LOLND)
+ continue;
+ if (cfs_match_nid(id.nid, &squash->rsi_nosquash_nids)) {
+ matched = true;
+ break;
+ }
+ }
+ if (matched)
+ sbi->ll_flags |= LL_SBI_NOROOTSQUASH;
+ else
+ sbi->ll_flags &= ~LL_SBI_NOROOTSQUASH;
+ }
+ up_write(&squash->rsi_sem);
+}
+
+/**
+ * Parse linkea content to extract information about a given hardlink
+ *
+ * \param[in] ldata - Initialized linkea data
+ * \param[in] linkno - Link identifier
+ * \param[out] gpout - Destination structure to fill with linkno,
+ * parent FID and entry name
+ * \param[in] size - Size of the gp_name buffer in gpout
+ *
+ * \retval 0 on success
+ * \retval Appropriate negative error code on failure
+ */
+static int ll_linkea_decode(struct linkea_data *ldata, unsigned int linkno,
+ struct getparent *gpout, size_t name_size)
+{
+ unsigned int idx;
+ struct lu_name ln;
+ int rc;
+ ENTRY;
+
+ rc = linkea_init(ldata);
+ if (rc < 0)
+ RETURN(rc);
+
+ if (linkno >= ldata->ld_leh->leh_reccount)
+ /* beyond last link */
+ RETURN(-ENODATA);
+
+ linkea_first_entry(ldata);
+ idx = 0;
+ while (ldata->ld_lee != NULL) {
+ linkea_entry_unpack(ldata->ld_lee, &ldata->ld_reclen, &ln,
+ &gpout->gp_fid);
+ if (idx == linkno)
+ break;
+
+ linkea_next_entry(ldata);
+ idx++;
+ }
+
+ if (idx < linkno)
+ RETURN(-ENODATA);
+
+ if (ln.ln_namelen >= name_size)
+ RETURN(-EOVERFLOW);
+
+ gpout->gp_linkno = linkno;
+ strlcpy(gpout->gp_name, ln.ln_name, name_size);
+ RETURN(0);
+}
+
+/**
+ * Get parent FID and name of an identified link. Operation is performed for
+ * a given link number, letting the caller iterate over linkno to list one or
+ * all links of an entry.
+ *
+ * \param[in] file - File descriptor against which to perform the operation
+ * \param[in,out] arg - User-filled structure containing the linkno to operate
+ * on and the available size. It is eventually filled with
+ * the requested information or left untouched on error
+ *
+ * \retval - 0 on success
+ * \retval - Appropriate negative error code on failure
+ */
+int ll_getparent(struct file *file, struct getparent __user *arg)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = file->f_dentry->d_inode;
+ struct linkea_data *ldata;
+ struct lu_buf buf = LU_BUF_NULL;
+ struct getparent *gpout;
+ __u32 linkno;
+ __u32 name_size;
+ size_t out_size;
+ int rc;
+
+ ENTRY;
+
+ if (!cfs_capable(CFS_CAP_DAC_READ_SEARCH) &&
+ !(ll_i2sbi(inode)->ll_flags & LL_SBI_USER_FID2PATH))
+ RETURN(-EPERM);
+
+ if (get_user(name_size, &arg->gp_name_size))
+ RETURN(-EFAULT);
+
+ if (get_user(linkno, &arg->gp_linkno))
+ RETURN(-EFAULT);
+
+ if (name_size > PATH_MAX)
+ RETURN(-EINVAL);
+
+ OBD_ALLOC(ldata, sizeof(*ldata));
+ if (ldata == NULL)
+ RETURN(-ENOMEM);
+
+ rc = linkea_data_new(ldata, &buf);
+ if (rc < 0)
+ GOTO(ldata_free, rc);
+
+ out_size = sizeof(*gpout) + name_size;
+ OBD_ALLOC(gpout, out_size);
+ if (gpout == NULL)
+ GOTO(lb_free, rc = -ENOMEM);
+
+ if (copy_from_user(gpout, arg, sizeof(*gpout)))
+ GOTO(gp_free, rc = -EFAULT);
+
+ rc = ll_getxattr(dentry, XATTR_NAME_LINK, buf.lb_buf, buf.lb_len);
+ if (rc < 0)
+ GOTO(gp_free, rc);
+
+ rc = ll_linkea_decode(ldata, linkno, gpout, name_size);
+ if (rc < 0)
+ GOTO(gp_free, rc);
+
+ if (copy_to_user(arg, gpout, out_size))
+ GOTO(gp_free, rc = -EFAULT);
+
+gp_free:
+ OBD_FREE(gpout, out_size);
+lb_free:
+ lu_buf_free(&buf);
+ldata_free:
+ OBD_FREE(ldata, sizeof(*ldata));
+
+ RETURN(rc);
+}