RETURN(-ETIME);
}
+/*
+ * sending a signal known to be ignored to cause restarting syscall if GNS mount
+ * function returns -ERESTARTSYS.
+ */
+static void
+ll_gns_send_signal(void)
+{
+ struct task_struct *task = current;
+ int signal = SIGCONT;
+
+ read_lock(&tasklist_lock);
+ spin_lock_irq(&task->sighand->siglock);
+ sigaddset(&task->pending.signal, signal);
+ spin_unlock_irq(&task->sighand->siglock);
+ read_unlock(&tasklist_lock);
+ set_tsk_thread_flag(task, TIF_SIGPENDING);
+}
+
/*
* tries to mount the mount object under passed @dentry. In the case of success
* @dentry will become mount point and 0 will be returned. Error code will be
int
ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
{
- struct ll_dentry_data *lld = dentry->d_fsdata;
char *path, *pathpage, *datapage, *argv[4];
struct file *mntinfo_fd = NULL;
int cleanup_phase = 0, rc = 0;
struct dentry *dchild;
ENTRY;
+ LASSERT(dentry->d_inode != NULL);
+
+ if (!S_ISDIR(dentry->d_inode->i_mode))
+ RETURN(-EINVAL);
+
+ sbi = ll_i2sbi(dentry->d_inode);
+ LASSERT(sbi != NULL);
+
+ spin_lock(&sbi->ll_gns_lock);
+
+ if (sbi->ll_gns_state == LL_GNS_DISABLED) {
+ spin_unlock(&sbi->ll_gns_lock);
+ RETURN(-EINVAL);
+ }
+
if (mnt == NULL) {
CERROR("suid directory found, but no "
"vfsmount available.\n");
RETURN(-EINVAL);
}
- CDEBUG(D_INODE, "mounting dentry %p\n", dentry);
-
- LASSERT(dentry->d_inode != NULL);
- LASSERT(S_ISDIR(dentry->d_inode->i_mode));
- LASSERT(lld != NULL);
-
- sbi = ll_i2sbi(dentry->d_inode);
- LASSERT(sbi != NULL);
-
/*
* another thead is in progress or just finished mounting the
* dentry. Handling that.
*/
- spin_lock(&sbi->ll_gns_lock);
if (sbi->ll_gns_state == LL_GNS_MOUNTING ||
sbi->ll_gns_state == LL_GNS_FINISHED) {
+ /*
+ * another thread is trying to mount GNS dentry. We'd like to
+ * handling that.
+ */
spin_unlock(&sbi->ll_gns_lock);
- CDEBUG(D_INODE, "GNS is in progress now, throwing "
- "-ERESTARTSYS to restart syscall and let "
- "it finish.\n");
+
+ /*
+ * check if dentry is mount point already, if so, do not restart
+ * syscal.
+ */
+ if (d_mountpoint(dentry))
+ RETURN(0);
+
+ /*
+ * causing syscall to restart and find this dentry already
+ * mounted.
+ */
+ ll_gns_send_signal();
RETURN(-ERESTARTSYS);
+
+#if 0
+ wait_for_completion(&sbi->ll_gns_mount_finished);
+ if (d_mountpoint(dentry))
+ RETURN(0);
+#endif
}
LASSERT(sbi->ll_gns_state == LL_GNS_IDLE);
- spin_lock(&dentry->d_lock);
- dentry->d_flags |= DCACHE_GNS_MOUNTING;
- spin_unlock(&dentry->d_lock);
-
+ CDEBUG(D_INODE, "mounting dentry %p\n", dentry);
+
/* mounting started */
sbi->ll_gns_state = LL_GNS_MOUNTING;
spin_unlock(&sbi->ll_gns_lock);
*/
/*
- * FIXME: lookup_one_len() requires dentry->d_inode->i_sem to be locked,
- * but we can't use ll_lookup_one_len() as this function is called from
- * different contol paths and some of them take dentry->d_inode->i_sem
- * and others do not.
+ * recursive lookup with trying to mount SUID bit marked directories on
+ * the way is not possible here, as lookup_one_len() does not pass @nd
+ * to ->lookup() and this is checked in ll_lookup_it(). So, do not
+ * handle possible -EAGAIN here.
*/
- dchild = lookup_one_len(sbi->ll_gns_oname, dentry,
- strlen(sbi->ll_gns_oname));
+ dchild = ll_lookup_one_len(sbi->ll_gns_oname, dentry,
+ strlen(sbi->ll_gns_oname));
up(&sbi->ll_gns_sem);
- if (!dchild)
- GOTO(cleanup, rc = -ENOENT);
+ cleanup_phase = 2;
if (IS_ERR(dchild)) {
rc = PTR_ERR(dchild);
-
- if (rc == -ERESTARTSYS) {
- CDEBUG(D_INODE, "possible endless loop is detected "
- "due to mount object is directory marked by "
- "SUID bit.\n");
- GOTO(cleanup, rc = -ELOOP);
- }
-
CERROR("can't find mount object %*s/%*s err = %d.\n",
(int)dentry->d_name.len, dentry->d_name.name,
- (int)dchild->d_name.len, dchild->d_name.name,
+ strlen(sbi->ll_gns_oname), sbi->ll_gns_oname,
rc);
GOTO(cleanup, rc);
}
if (IS_ERR(mntinfo_fd)) {
CERROR("can't open mount object %*s/%*s err = %d.\n",
(int)dentry->d_name.len, dentry->d_name.name,
- (int)dchild->d_name.len, dchild->d_name.name,
+ strlen(sbi->ll_gns_oname), sbi->ll_gns_oname,
(int)PTR_ERR(mntinfo_fd));
- dput(dchild);
mntput(mnt);
GOTO(cleanup, rc = PTR_ERR(mntinfo_fd));
}
- cleanup_phase = 2;
+ cleanup_phase = 3;
- if (mntinfo_fd->f_dentry->d_inode->i_size > PAGE_SIZE) {
+ if (mntinfo_fd->f_dentry->d_inode->i_size > PAGE_SIZE - 1) {
CERROR("mount object %*s/%*s is too big (%Ld)\n",
(int)dentry->d_name.len, dentry->d_name.name,
- (int)dchild->d_name.len, dchild->d_name.name,
+ strlen(sbi->ll_gns_oname), sbi->ll_gns_oname,
mntinfo_fd->f_dentry->d_inode->i_size);
GOTO(cleanup, rc = -EFBIG);
}
if (!datapage)
GOTO(cleanup, rc = -ENOMEM);
- cleanup_phase = 3;
+ cleanup_phase = 4;
/* read data from mount object. */
- rc = kernel_read(mntinfo_fd, 0, datapage, PAGE_SIZE);
+ rc = kernel_read(mntinfo_fd, 0, datapage, PAGE_SIZE - 1);
if (rc < 0) {
CERROR("can't read mount object %*s/%*s data, err %d\n",
(int)dentry->d_name.len, dentry->d_name.name,
- (int)dchild->d_name.len, dchild->d_name.name,
+ strlen(sbi->ll_gns_oname), sbi->ll_gns_oname,
rc);
GOTO(cleanup, rc);
}
- datapage[PAGE_SIZE - 1] = '\0';
+ /* no data in mount object? */
+ if (rc == 0) {
+ CERROR("mount object %*s/%*s is empty?\n",
+ (int)dentry->d_name.len, dentry->d_name.name,
+ strlen(sbi->ll_gns_oname), sbi->ll_gns_oname);
+ GOTO(cleanup, rc);
+ }
+ datapage[rc] = '\0';
fput(mntinfo_fd);
mntinfo_fd = NULL;
+ dchild = NULL;
/* synchronizing with possible /proc/fs/...write */
down(&sbi->ll_gns_sem);
up(&sbi->ll_gns_sem);
- rc = USERMODEHELPER(argv[0], argv, NULL);
+ /* do not wait for helper complete here. */
+ rc = call_usermodehelper(argv[0], argv, NULL, 0);
if (rc) {
CERROR("failed to call GNS upcall %s, err = %d\n",
sbi->ll_gns_upcall, rc);
}
/*
- * wait for mount completion. This is actually not need, because
- * USERMODEHELPER() returns only when usermode process finishes. But we
- * doing this just for case USERMODEHELPER() semantics will be changed
- * or usermode upcall program will start mounting in backgound and
- * return instantly. --umka
+ * waiting for dentry become mount point GNS_WAIT_ATTEMPTS times by 1
+ * second.
*/
-
rc = ll_gns_wait_for_mount(dentry, 1, GNS_WAIT_ATTEMPTS);
complete_all(&sbi->ll_gns_mount_finished);
if (rc == 0) {
mntput(mnt);
dput(dentry);
}
- spin_lock(&dentry->d_lock);
- dentry->d_flags &= ~DCACHE_GNS_PENDING;
- spin_unlock(&dentry->d_lock);
} else {
CERROR("usermode upcall %s failed to mount %s, err %d\n",
sbi->ll_gns_upcall, path, rc);
EXIT;
cleanup:
switch (cleanup_phase) {
- case 3:
+ case 4:
free_page((unsigned long)datapage);
- case 2:
+ case 3:
if (mntinfo_fd != NULL)
fput(mntinfo_fd);
+ case 2:
+ if (dchild != NULL)
+ dput(dchild);
case 1:
free_page((unsigned long)pathpage);
-
- /*
- * waking up all waiters after gns state is set to
- * LL_GNS_MOUNTING
- */
complete_all(&sbi->ll_gns_mount_finished);
case 0:
spin_lock(&sbi->ll_gns_lock);
sbi->ll_gns_state = LL_GNS_IDLE;
spin_unlock(&sbi->ll_gns_lock);
-
- spin_lock(&dentry->d_lock);
- dentry->d_flags &= ~DCACHE_GNS_MOUNTING;
- spin_unlock(&dentry->d_lock);
}
return rc;
}