aboutsummaryrefslogtreecommitdiffstats
path: root/kernel4/ioctl.c
blob: 728a6db8a3edf445323b7dcd77e6538d634897f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/* -*- linux-c -*- --------------------------------------------------------- *
 *
 * linux/fs/autofs/ioctl.c
 *
 *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
 *
 * This file is part of the Linux kernel and is made available under
 * the terms of the GNU General Public License, version 2, or at your
 * option, any later version, incorporated herein by reference.
 *
 * ------------------------------------------------------------------------- */

#include "autofs_i.h"

/* Get/set timeout ioctl() operation */
static inline int autofs_get_set_timeout(struct autofs_sb_info *sbi,
					 unsigned long *p)
{
	int rv;
	unsigned long ntimeout;

	if ( (rv = get_user(ntimeout, p)) ||
	     (rv = put_user(sbi->exp_timeout/HZ, p)) )
		return rv;

	if ( ntimeout > ULONG_MAX/HZ )
		sbi->exp_timeout = 0;
	else
		sbi->exp_timeout = ntimeout * HZ;

	return 0;
}

/* Return protocol major version */
static inline int autofs_get_protover(int *p)
{
	return put_user(AUTOFS_PROTO_MAJOR_VERSION, p);
}

/* Return extended protocol information */
static inline int autofs_get_protoinfo(struct autofs_proto_info *p)
{
	static struct autofs_proto_info proto_info = {
		AUTOFS_PROTO_MAJOR_VERSION,
		AUTOFS_PROTO_MINOR_VERSION,
		AUTOFS_PROTO_FEATURES
	};

	return copy_to_user(&proto_info, arg, sizeof(struct autofs_proto_info))
		? -EFAULT : 0;
}

/* Perform an expiry operation */
static inline int autofs_expire_run(struct autofs_sb_info *sbi,
				    struct autofs_packet_expire *pkt_p)
{
	struct autofs_dir_ent *ent;
	struct autofs_packet_expire pkt;
	struct autofs_dirhash *dh = &(sbi->dirhash);
	
	memset(&pkt,0,sizeof pkt);

	pkt.hdr.body_len      = sizeof(struct autofs_packet_expire_body);
	pkt.hdr.type          = autofs_ptype_expire;

	if ( !sbi->exp_timeout ||
	     !(ent = autofs_expire(dh,sbi->exp_timeout)) )
		return -EAGAIN;

	pkt.body.len = ent->len;
	memcpy(pkt.body.name, ent->name, pkt.body.len);

	if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) )
		return -EFAULT;
	
	autofs_update_usage(dh,ent);

	return 0;
}

/*
 * Set the mode on a directory.  Since the directory may very well be
 * hidden under a mount point, we use an ioctl() on the root directory
 * like everything else for this; passing the inode number down.
 */
static inline int autofs_setdirmode(struct super_block *sb,
				    struct autofs_set_dir_mode *mode) {
	struct autofs_set_dir_mode dm;
	struct dentry *dent;
	struct inode  *inode;
	enum autofs_dir_mode oldmode;

	if ( copy_from_user(&dm,mode,sizeof(struct autofs_set_dir_mode))
	     != sizeof(struct autofs_set_dir_mode) )
		return -EFAULT;

	if ( (unsigned)dm.mode >= AUTOFS_DIR_MODECOUNT )
		return -EINVARG;

	if ( !(dent = autofs_ino_hash_lookup(sb->u.autofs_sb.inode_hash)) ||
	     !(inode = dent->d_inode) )
		return -ENOENT;

	if ( ! S_ISDIR(inode->i_mode) )
		return -ENOTDIR;

	oldmode = inode->u.autofs_i.dir_mode;
	inode->u.autofs_i.dir_mode = dm.mode;
	inode->u.autofs_i.dir_cookie = dm.cookie;

	if ( oldmode == dm.mode )
		return 0;

	if ( oldmode == AUTOFS_DIR_LOCKED )
		wake_up(&inode->u.autofs_i.dir_queue);

	return 0;
}

/*
 * ioctl()'s on the root directory is the chief method for the daemon to
 * generate kernel actions.  We allow the ioctl's on any directory, but the
 * actions are filesystem, not inode, specific.
 */
int autofs_dir_ioctl(struct inode *inode, struct file *filp,
		     unsigned int cmd, unsigned long arg)
{
	struct super_block *sb = inode->i_sb;

	if ( _IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) ||
	     _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT )
		return -ENOTTY;
	
	if ( !autofs_oz_mode(sb) && !fsuser() )
		return -EPERM;
	
	switch(cmd) {
	case AUTOFS_IOC_READY:		/* Wait queue: go ahead and retry */
		return autofs_wait_release(sb, arg, 0);
	case AUTOFS_IOC_FAIL:		/* Wait queue: fail with ENOENT */
		return autofs_wait_release(sb, arg, -ENOENT);
	case AUTOFS_IOC_CATATONIC:	/* Enter catatonic mode */
		autofs_catatonic_mode(sb);
		return 0;
	case AUTOFS_IOC_PROTOVER:	/* Get protocol version */
		return autofs_get_protover((int *)arg);
	case AUTOFS_IOC_SETTIMEOUT:
		return autofs_get_set_timeout(sb, (unsigned long *)arg);
	case AUTOFS_IOC_EXPIRE:
		return autofs_expire_run(sb, (struct autofs_packet_expire *)arg);
	case AUTOFS_IOC_PROTOINFO:
		return autofs_get_protoinfo((struct autofs_proto_info *)arg);
	case AUTOFS_IOC_ENABLE:
		/* Currently no "features" supported, so don't enable any */
		return arg ? -EINVARG : 0;
	case AUTOFS_IOC_SETDIRMODE:
		return autofs_setdirmode(sb, (struct autofs_set_dir_mode *)arg);
	default:
		return -ENOSYS;
	}
}