/* Check a mount point for busyness */ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) { struct dentry *top = dentry; struct path path = {.mnt = mnt, .dentry = dentry}; int status = 1; DPRINTK("dentry %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); path_get(&path); if (!follow_down_one(&path)) goto done; if (is_autofs4_dentry(path.dentry)) { struct autofs_sb_info *sbi = autofs4_sbi(path.dentry->d_sb); /* This is an autofs submount, we can't expire it */ if (autofs_type_indirect(sbi->type)) goto done; } /* Update the expiry counter if fs is busy */ if (!may_umount_tree(path.mnt)) { struct autofs_info *ino = autofs4_dentry_ino(top); ino->last_used = jiffies; goto done; } status = 0; done: DPRINTK("returning = %d", status); path_put(&path); return status; }
/* * NOTE! * * Normal filesystems would do a "d_delete()" to tell the VFS dcache * that the file no longer exists. However, doing that means that the * VFS layer can turn the dentry into a negative dentry. We don't want * this, because since the unlink is probably the result of an expire. * We simply d_drop it, which allows the dentry lookup to remount it * if necessary. * * If a process is blocked on the dentry waiting for the expire to finish, * it will invalidate the dentry and try to mount with a new one. * * Also see autofs_dir_rmdir().. */ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); /* This allows root to remove symlinks */ lock_kernel(); if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) { unlock_kernel(); return -EACCES; } dput(ino->dentry); dentry->d_inode->i_size = 0; dentry->d_inode->i_nlink = 0; dir->i_mtime = CURRENT_TIME; d_drop(dentry); unlock_kernel(); return 0; }
int autofs4_expire_wait(struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); int status; spin_lock(&sbi->fs_lock); if (ino->flags & AUTOFS_INF_EXPIRING) { spin_unlock(&sbi->fs_lock); DPRINTK("waiting for expire %p name=%.*s", dentry, dentry->d_name.len, dentry->d_name.name); status = autofs4_wait(sbi, dentry, NFY_NONE); wait_for_completion(&ino->expire_complete); DPRINTK("expire done status=%d", status); if (d_unhashed(dentry)) return -EAGAIN; return status; } spin_unlock(&sbi->fs_lock); return 0; }
static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) { struct autofs_sb_info *sbi = autofs4_sbi(mnt->mnt_sb); struct inode *root_inode = mnt->mnt_sb->s_root->d_inode; if (!sbi) return 0; seq_printf(m, ",fd=%d", sbi->pipefd); if (root_inode->i_uid != 0) seq_printf(m, ",uid=%u", root_inode->i_uid); if (root_inode->i_gid != 0) seq_printf(m, ",gid=%u", root_inode->i_gid); seq_printf(m, ",pgrp=%d", sbi->oz_pgrp); seq_printf(m, ",timeout=%lu", sbi->exp_timeout/HZ); seq_printf(m, ",minproto=%d", sbi->min_proto); seq_printf(m, ",maxproto=%d", sbi->max_proto); if (autofs_type_offset(sbi->type)) seq_printf(m, ",offset"); else if (autofs_type_direct(sbi->type)) seq_printf(m, ",direct"); else seq_printf(m, ",indirect"); return 0; }
static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) { struct dentry *dentry = file->f_dentry; struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); int status; DPRINTK("file=%p dentry=%p %.*s", file, dentry, dentry->d_name.len, dentry->d_name.name); if (autofs4_oz_mode(sbi)) goto out; if (autofs4_ispending(dentry)) { DPRINTK("dentry busy"); return -EBUSY; } if (d_mountpoint(dentry)) { struct file *fp = file->private_data; if (!fp) return -ENOENT; if (!fp->f_op || !fp->f_op->readdir) goto out; status = vfs_readdir(fp, filldir, dirent); file->f_pos = fp->f_pos; if (status) autofs4_copy_atime(file, fp); return status; } out: return autofs4_dcache_readdir(file, dirent, filldir); }
static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); struct inode *inode; if ( !autofs4_oz_mode(sbi) ) return -EACCES; DPRINTK("dentry %p, creating %.*s", dentry, dentry->d_name.len, dentry->d_name.name); ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); if (ino == NULL) return -ENOSPC; inode = autofs4_get_inode(dir->i_sb, ino); d_instantiate(dentry, inode); if (dir == dir->i_sb->s_root->d_inode) dentry->d_op = &autofs4_root_dentry_operations; else dentry->d_op = &autofs4_dentry_operations; dentry->d_fsdata = ino; ino->dentry = dget(dentry); ino->inode = inode; dir->i_nlink++; dir->i_mtime = CURRENT_TIME; return 0; }
static int autofs4_dir_close(struct inode *inode, struct file *file) { struct dentry *dentry = file->f_dentry; struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); DPRINTK("file=%p dentry=%p %.*s", file, dentry, dentry->d_name.len, dentry->d_name.name); if (autofs4_oz_mode(sbi)) goto out; if (autofs4_ispending(dentry)) { DPRINTK("dentry busy"); return -EBUSY; } if (d_mountpoint(dentry)) { struct file *fp = file->private_data; if (!fp) return -ENOENT; filp_close(fp, current->files); file->private_data = NULL; } out: return 0; }
/* Lookups in the root directory */ static struct dentry *autofs4_root_lookup(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi; int oz_mode; DPRINTK(("autofs_root_lookup: name = %.*s\n", dentry->d_name.len, dentry->d_name.name)); if (dentry->d_name.len > NAME_MAX) return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ sbi = autofs4_sbi(dir->i_sb); oz_mode = autofs4_oz_mode(sbi); DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", current->pid, current->pgrp, sbi->catatonic, oz_mode)); /* * Mark the dentry incomplete, but add it. This is needed so * that the VFS layer knows about the dentry, and we can count * on catching any lookups through the revalidate. * * Let all the hard work be done by the revalidate function that * needs to be able to do this anyway.. * * We need to do this before we release the directory semaphore. */ dentry->d_op = &autofs4_root_dentry_operations; if (!oz_mode) dentry->d_flags |= DCACHE_AUTOFS_PENDING; dentry->d_fsdata = NULL; d_add(dentry, NULL); if (dentry->d_op && dentry->d_op->d_revalidate) { up(&dir->i_sem); (dentry->d_op->d_revalidate)(dentry, 0); down(&dir->i_sem); } /* * If we are still pending, check if we had to handle * a signal. If so we can force a restart.. */ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) { if (signal_pending(current)) return ERR_PTR(-ERESTARTNOINTR); } /* * If this dentry is unhashed, then we shouldn't honour this * lookup even if the dentry is positive. Returning ENOENT here * doesn't do the right thing for all system calls, but it should * be OK for the operations we permit from an autofs. */ if ( dentry->d_inode && d_unhashed(dentry) ) return ERR_PTR(-ENOENT); return NULL; }
void autofs4_kill_sb(struct super_block *sb) { struct autofs_sb_info *sbi = autofs4_sbi(sb); /* * In the event of a failure in get_sb_nodev the superblock * info is not present so nothing else has been setup, so * just call kill_anon_super when we are called from * deactivate_super. */ if (!sbi) goto out_kill_sb; /* Free wait queues, close pipe */ autofs4_catatonic_mode(sbi); /* Clean up and release dangling references */ autofs4_force_release(sbi); sb->s_fs_info = NULL; kfree(sbi); out_kill_sb: DPRINTK("shutting down"); kill_anon_super(sb); }
static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); lock_kernel(); if (!autofs4_oz_mode(sbi)) { unlock_kernel(); return -EACCES; } spin_lock(&dcache_lock); if (!list_empty(&dentry->d_subdirs)) { spin_unlock(&dcache_lock); unlock_kernel(); return -ENOTEMPTY; } __d_drop(dentry); spin_unlock(&dcache_lock); dput(ino->dentry); dentry->d_inode->i_size = 0; dentry->d_inode->i_nlink = 0; if (dir->i_nlink) dir->i_nlink--; unlock_kernel(); return 0; }
/* * Calculate and dget next entry in top down tree traversal. */ static struct dentry *get_next_positive_dentry(struct dentry *prev, struct dentry *root) { struct autofs_sb_info *sbi = autofs4_sbi(root->d_sb); struct list_head *next; struct dentry *p, *ret; if (prev == NULL) return dget(root); spin_lock(&sbi->lookup_lock); relock: p = prev; spin_lock(&p->d_lock); again: next = p->d_subdirs.next; if (next == &p->d_subdirs) { while (1) { struct dentry *parent; if (p == root) { spin_unlock(&p->d_lock); spin_unlock(&sbi->lookup_lock); dput(prev); return NULL; } parent = p->d_parent; if (!spin_trylock(&parent->d_lock)) { spin_unlock(&p->d_lock); cpu_relax(); goto relock; } spin_unlock(&p->d_lock); next = p->d_child.next; p = parent; if (next != &parent->d_subdirs) break; } } ret = list_entry(next, struct dentry, d_child); spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED); /* Negative dentry - try next */ if (!simple_positive(ret)) { spin_unlock(&p->d_lock); lock_set_subclass(&ret->d_lock.dep_map, 0, _RET_IP_); p = ret; goto again; } dget_dlock(ret); spin_unlock(&ret->d_lock); spin_unlock(&p->d_lock); spin_unlock(&sbi->lookup_lock); dput(prev); return ret; }
static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) { struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); if (!autofs4_oz_mode(sbi)) autofs4_update_usage(dentry); return 1; }
static int autofs4_dir_open(struct inode *inode, struct file *file) { struct dentry *dentry = file->f_dentry; struct vfsmount *mnt = file->f_vfsmnt; struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); int status; DPRINTK("file=%p dentry=%p %.*s", file, dentry, dentry->d_name.len, dentry->d_name.name); if (autofs4_oz_mode(sbi)) goto out; if (autofs4_ispending(dentry)) { DPRINTK("dentry busy"); return -EBUSY; } if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { struct nameidata nd; int empty; /* In case there are stale directory dentrys from a failed mount */ spin_lock(&dcache_lock); empty = list_empty(&dentry->d_subdirs); spin_unlock(&dcache_lock); if (!empty) d_invalidate(dentry); nd.flags = LOOKUP_DIRECTORY; status = (dentry->d_op->d_revalidate)(dentry, &nd); if (!status) return -ENOENT; } if (d_mountpoint(dentry)) { struct file *fp = NULL; struct vfsmount *fp_mnt = mntget(mnt); struct dentry *fp_dentry = dget(dentry); while (follow_down(&fp_mnt, &fp_dentry) && d_mountpoint(fp_dentry)); fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); status = PTR_ERR(fp); if (IS_ERR(fp)) { file->private_data = NULL; return status; } file->private_data = fp; } out: return 0; }
/* * Get the autofs super block info struct from the file opened on * the autofs mount point. */ static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) { struct autofs_sb_info *sbi = NULL; struct inode *inode; if (f) { inode = f->f_path.dentry->d_inode; sbi = autofs4_sbi(inode->i_sb); } return sbi; }
/* * Get the autofs super block info struct from the file opened on * the autofs mount point. */ static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) { struct autofs_sb_info *sbi = NULL; struct inode *inode; if (f) { inode = file_inode(f); sbi = autofs4_sbi(inode->i_sb); } return sbi; }
static int autofs4_dir_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); struct inode *inode; char *cp; DPRINTK(("autofs_dir_symlink: %s <- %.*s\n", symname, dentry->d_name.len, dentry->d_name.name)); lock_kernel(); if (!autofs4_oz_mode(sbi)) { unlock_kernel(); return -EACCES; } ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); if (ino == NULL) { unlock_kernel(); return -ENOSPC; } ino->size = strlen(symname); ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); if (cp == NULL) { kfree(ino); unlock_kernel(); return -ENOSPC; } strcpy(cp, symname); inode = autofs4_get_inode(dir->i_sb, ino); d_instantiate(dentry, inode); if (dir == dir->i_sb->s_root->d_inode) dentry->d_op = &autofs4_root_dentry_operations; else dentry->d_op = &autofs4_dentry_operations; dentry->d_fsdata = ino; ino->dentry = dget(dentry); ino->inode = inode; dir->i_mtime = CURRENT_TIME; unlock_kernel(); return 0; }
static void autofs4_put_super(struct super_block *sb) { struct autofs_sb_info *sbi = autofs4_sbi(sb); sb->u.generic_sbp = NULL; if ( !sbi->catatonic ) autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ kfree(sbi); DPRINTK(("autofs: shutting down\n")); }
/* * ioctl()'s on the root directory is the chief method for the daemon to * generate kernel reactions */ static int autofs4_root_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct autofs_sb_info *sbi = autofs4_sbi(inode->i_sb); void __user *p = (void __user *)arg; DPRINTK("cmd = 0x%08x, arg = 0x%08lx, sbi = %p, pgrp = %u", cmd,arg,sbi,process_group(current)); if ( _IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) || _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT ) return -ENOTTY; if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) return -EPERM; switch(cmd) { case AUTOFS_IOC_READY: /* Wait queue: go ahead and retry */ return autofs4_wait_release(sbi,(autofs_wqt_t)arg,0); case AUTOFS_IOC_FAIL: /* Wait queue: fail with ENOENT */ return autofs4_wait_release(sbi,(autofs_wqt_t)arg,-ENOENT); case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */ autofs4_catatonic_mode(sbi); return 0; case AUTOFS_IOC_PROTOVER: /* Get protocol version */ return autofs4_get_protover(sbi, p); case AUTOFS_IOC_PROTOSUBVER: /* Get protocol sub version */ return autofs4_get_protosubver(sbi, p); case AUTOFS_IOC_SETTIMEOUT: return autofs4_get_set_timeout(sbi, p); case AUTOFS_IOC_TOGGLEREGHOST: return autofs4_toggle_reghost(sbi, p); case AUTOFS_IOC_ASKREGHOST: return autofs4_ask_reghost(sbi, p); case AUTOFS_IOC_ASKUMOUNT: return autofs4_ask_umount(filp->f_vfsmnt, p); /* return a single thing to expire */ case AUTOFS_IOC_EXPIRE: return autofs4_expire_run(inode->i_sb,filp->f_vfsmnt,sbi, p); /* same as above, but can send multiple expires through pipe */ case AUTOFS_IOC_EXPIRE_MULTI: return autofs4_expire_multi(inode->i_sb,filp->f_vfsmnt,sbi, p); default: return -ENOSYS; } }
static const char *autofs4_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) { struct autofs_sb_info *sbi; struct autofs_info *ino; if (!dentry) return ERR_PTR(-ECHILD); sbi = autofs4_sbi(dentry->d_sb); ino = autofs4_dentry_ino(dentry); if (ino && !autofs4_oz_mode(sbi)) ino->last_used = jiffies; return d_inode(dentry)->i_private; }
static struct dentry *get_next_positive_subdir(struct dentry *prev, struct dentry *root) { struct autofs_sb_info *sbi = autofs4_sbi(root->d_sb); struct list_head *next; struct dentry *p, *q; spin_lock(&sbi->lookup_lock); if (prev == NULL) { spin_lock(&root->d_lock); prev = dget_dlock(root); next = prev->d_subdirs.next; p = prev; goto start; } p = prev; spin_lock(&p->d_lock); again: next = p->d_u.d_child.next; start: if (next == &root->d_subdirs) { spin_unlock(&p->d_lock); spin_unlock(&sbi->lookup_lock); dput(prev); return NULL; } q = list_entry(next, struct dentry, d_u.d_child); spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED); if (!simple_positive(q)) { spin_unlock(&p->d_lock); lock_set_subclass(&q->d_lock.dep_map, 0, _RET_IP_); p = q; goto again; } dget_dlock(q); spin_unlock(&q->d_lock); spin_unlock(&p->d_lock); spin_unlock(&sbi->lookup_lock); dput(prev); return q; }
int autofs4_expire_wait(struct dentry *dentry, int rcu_walk) { struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); int status; int state; /* Block on any pending expire */ if (!(ino->flags & AUTOFS_INF_WANT_EXPIRE)) return 0; if (rcu_walk) return -ECHILD; retry: spin_lock(&sbi->fs_lock); state = ino->flags & (AUTOFS_INF_WANT_EXPIRE | AUTOFS_INF_EXPIRING); if (state == AUTOFS_INF_WANT_EXPIRE) { spin_unlock(&sbi->fs_lock); /* * Possibly being selected for expire, wait until * it's selected or not. */ schedule_timeout_uninterruptible(HZ/10); goto retry; } if (state & AUTOFS_INF_EXPIRING) { spin_unlock(&sbi->fs_lock); pr_debug("waiting for expire %p name=%pd\n", dentry, dentry); status = autofs4_wait(sbi, dentry, NFY_NONE); wait_for_completion(&ino->expire_complete); pr_debug("expire done status=%d\n", status); if (d_unhashed(dentry)) return -EAGAIN; return status; } spin_unlock(&sbi->fs_lock); return 0; }
/* * Calculate and dget next entry in the subdirs list under root. */ static struct dentry *get_next_positive_subdir(struct dentry *prev, struct dentry *root) { struct autofs_sb_info *sbi = autofs4_sbi(root->d_sb); struct list_head *next; struct dentry *q; spin_lock(&sbi->lookup_lock); spin_lock(&root->d_lock); if (prev) next = prev->d_child.next; else { prev = dget_dlock(root); next = prev->d_subdirs.next; } cont: if (next == &root->d_subdirs) { spin_unlock(&root->d_lock); spin_unlock(&sbi->lookup_lock); dput(prev); return NULL; } q = list_entry(next, struct dentry, d_child); spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED); /* Already gone or negative dentry (under construction) - try next */ if (!d_count(q) || !simple_positive(q)) { spin_unlock(&q->d_lock); next = q->d_child.next; goto cont; } dget_dlock(q); spin_unlock(&q->d_lock); spin_unlock(&root->d_lock); spin_unlock(&sbi->lookup_lock); dput(prev); return q; }
/* Check a mount point for busyness */ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) { struct dentry *top = dentry; int status = 1; DPRINTK("dentry %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); mntget(mnt); dget(dentry); if (!follow_down(&mnt, &dentry)) goto done; if (is_autofs4_dentry(dentry)) { struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); /* This is an autofs submount, we can't expire it */ if (autofs_type_indirect(sbi->type)) goto done; /* * Otherwise it's an offset mount and we need to check * if we can umount its mount, if there is one. */ if (!d_mountpoint(dentry)) goto done; } /* Update the expiry counter if fs is busy */ if (!may_umount_tree(mnt)) { struct autofs_info *ino = autofs4_dentry_ino(top); ino->last_used = jiffies; goto done; } status = 0; done: DPRINTK("returning = %d", status); dput(dentry); mntput(mnt); return status; }
/* * Revalidate is called on every cache lookup. Some of those * cache lookups may actually happen while the dentry is not * yet completely filled in, and revalidate has to delay such * lookups.. */ static int autofs4_root_revalidate(struct dentry * dentry, int flags) { struct inode * dir = dentry->d_parent->d_inode; struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino; int oz_mode = autofs4_oz_mode(sbi); /* Pending dentry */ if (autofs4_ispending(dentry)) { if (autofs4_oz_mode(sbi)) return 1; else return try_to_fill_dentry(dentry, dir->i_sb, sbi); } /* Negative dentry.. invalidate if "old" */ if (dentry->d_inode == NULL) return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); ino = autofs4_dentry_ino(dentry); /* Check for a non-mountpoint directory with no contents */ spin_lock(&dcache_lock); if (S_ISDIR(dentry->d_inode->i_mode) && !d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { DPRINTK(("autofs_root_revalidate: dentry=%p %.*s, emptydir\n", dentry, dentry->d_name.len, dentry->d_name.name)); spin_unlock(&dcache_lock); if (oz_mode) return 1; else return try_to_fill_dentry(dentry, dir->i_sb, sbi); } spin_unlock(&dcache_lock); /* Update the usage list */ if (!oz_mode) autofs4_update_usage(dentry); return 1; }
static int autofs4_root_readdir(struct file *file, void *dirent, filldir_t filldir) { struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); int oz_mode = autofs4_oz_mode(sbi); DPRINTK("called, filp->f_pos = %lld", file->f_pos); /* * Don't set reghost flag if: * 1) f_pos is larger than zero -- we've already been here. * 2) we haven't even enabled reghosting in the 1st place. * 3) this is the daemon doing a readdir */ if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) sbi->needs_reghost = 1; DPRINTK("needs_reghost = %d", sbi->needs_reghost); return autofs4_dcache_readdir(file, dirent, filldir); }
static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) { struct autofs_sb_info *sbi = autofs4_sbi(mnt->mnt_sb); if (!sbi) return 0; seq_printf(m, ",fd=%d", sbi->pipefd); seq_printf(m, ",pgrp=%d", sbi->oz_pgrp); seq_printf(m, ",timeout=%lu", sbi->exp_timeout/HZ); seq_printf(m, ",minproto=%d", sbi->min_proto); seq_printf(m, ",maxproto=%d", sbi->max_proto); if (sbi->type & AUTOFS_TYPE_OFFSET) seq_printf(m, ",offset"); else if (sbi->type & AUTOFS_TYPE_DIRECT) seq_printf(m, ",direct"); else seq_printf(m, ",indirect"); return 0; }