fs: add support for HFSX Plus file system
[project/mountd.git] / autofs.c
1 #include <sys/types.h>
2 #include <linux/types.h>
3 #include <paths.h>
4 #include <limits.h>
5 #include <time.h>
6
7 #include <stdio.h>
8 #include <signal.h>
9 #include <stdlib.h>
10 #include <fcntl.h>
11
12 #include <errno.h>
13
14 #include <string.h>
15 #include <syslog.h>
16 #include <unistd.h>
17 #include <sys/ioctl.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include <sys/stat.h>
21 #include <sys/time.h>
22 #include <poll.h>
23 #include <linux/auto_fs4.h>
24
25 #include "include/log.h"
26 #include "include/sys.h"
27 #include "include/timer.h"
28 #include "include/mount.h"
29 #include "include/signal.h"
30 #include "include/ucix.h"
31 #include "include/autofs.h"
32
33 static int fdin = 0; /* data coming out of the kernel */
34 static int fdout = 0;/* data going into the kernel */
35 static bool term = false;
36 static dev_t dev;
37
38 static time_t uci_timeout;
39 char uci_path[32];
40
41 static void umount_autofs(void)
42 {
43 system_printf("umount %s 2> /dev/null", "/tmp/run/mountd/");
44 }
45
46 static int mount_autofs(void)
47 {
48 int pipefd[2];
49 struct stat st;
50 log_printf("trying to mount %s as the autofs root\n", "/tmp/run/mountd/");
51 if(is_mounted(0, "/tmp/run/mountd/"))
52 {
53 log_printf("%s is already mounted\n", "/tmp/run/mountd/");
54 return -1;
55 }
56 fdout = fdin = -1;
57 mkdir("/tmp/run/mountd/", 0555);
58 if(pipe(pipefd) < 0)
59 {
60 log_printf("failed to get kernel pipe\n");
61 return -1;
62 }
63 if(system_printf("/bin/mount -t autofs -o fd=%d,pgrp=%u,minproto=5,maxproto=5 \"mountd(pid%u)\" %s",
64 pipefd[1], (unsigned) getpgrp(), getpid(), "/tmp/run/mountd/") != 0)
65 {
66 log_printf("unable to mount autofs on %s\n", "/tmp/run/mountd/");
67 close(pipefd[0]);
68 close(pipefd[1]);
69 return -1;
70 }
71
72 close(pipefd[1]);
73 fdout = pipefd[0];
74
75 fdin = open("/tmp/run/mountd/", O_RDONLY);
76 if(fdin < 0)
77 {
78 umount_autofs();
79 return -1;
80 }
81 stat("/tmp/run/mountd/", &st);
82 return 0;
83 }
84
85 static void send_ready(unsigned int wait_queue_token)
86 {
87 if(ioctl(fdin, AUTOFS_IOC_READY, wait_queue_token) < 0)
88 log_printf("failed to report ready to kernel\n");
89 }
90
91 static void send_fail(unsigned int wait_queue_token)
92 {
93 if(ioctl(fdin, AUTOFS_IOC_FAIL, wait_queue_token) < 0)
94 log_printf("failed to report fail to kernel\n");
95 }
96
97 static int autofs_process_request(const struct autofs_v5_packet *pkt)
98 {
99 struct stat st;
100 log_printf("kernel is requesting a mount -> %s\n", pkt->name);
101 chdir("/tmp/run/mountd/");
102 if (lstat(pkt->name, &st) == -1 || (S_ISDIR(st.st_mode) && st.st_dev == dev)) {
103 if(!mount_new("/tmp/run/mountd/", (char*)pkt->name))
104 {
105 send_ready(pkt->wait_queue_token);
106 } else {
107 send_fail(pkt->wait_queue_token);
108 log_printf("failed to mount %s\n", pkt->name);
109 }
110 } else {
111 send_ready(pkt->wait_queue_token);
112 }
113 chdir("/");
114
115 return 0;
116 }
117
118 static void expire_proc(void)
119 {
120 struct autofs_packet_expire pkt;
121 while(ioctl(fdin, AUTOFS_IOC_EXPIRE, &pkt) == 0)
122 mount_remove("/tmp/run/mountd/", pkt.name);
123 }
124
125 static int fullread(void *ptr, size_t len)
126 {
127 char *buf = (char *) ptr;
128 while(len > 0)
129 {
130 ssize_t r = read(fdout, buf, len);
131 if(r == -1)
132 {
133 if (errno == EINTR)
134 continue;
135 break;
136 }
137 buf += r;
138 len -= r;
139 }
140 return len;
141 }
142
143 static int autofs_in(union autofs_v5_packet_union *pkt)
144 {
145 int res;
146 struct pollfd fds[1];
147
148 fds[0].fd = fdout;
149 fds[0].events = POLLIN;
150
151 while(!term)
152 {
153 res = poll(fds, 1, -1);
154
155 if (res == -1)
156 {
157 if (errno == EINTR)
158 continue;
159 log_printf("failed while trying to read packet from kernel\n");
160 return -1;
161 }
162 else if ((res > 0) && (fds[0].revents & POLLIN))
163 {
164 return fullread(pkt, sizeof(*pkt));
165 }
166 }
167 return 1;
168 }
169
170 pid_t autofs_safe_fork(void)
171 {
172 pid_t pid = fork();
173 if(!pid)
174 {
175 close(fdin);
176 close(fdout);
177 }
178 return pid;
179 }
180
181 static void autofs_cleanup(void)
182 {
183 close(fdin);
184 close(fdout);
185 umount_autofs();
186 }
187
188 static void autofs_end_handler(int sig)
189 {
190 term = true;
191 }
192
193 static void autofs_init(void)
194 {
195 int kproto_version;
196 char *p;
197 struct uci_context *ctx;
198 signal_init(autofs_end_handler);
199 ctx = ucix_init("mountd");
200 uci_timeout = ucix_get_option_int(ctx, "mountd", "mountd", "timeout", 60);
201 p = ucix_get_option(ctx, "mountd", "mountd", "path");
202 ucix_cleanup(ctx);
203 if(p)
204 snprintf(uci_path, 31, "%s", p);
205 else
206 snprintf(uci_path, 31, "/tmp/mounts/");
207 uci_path[31] = '\0';
208 mkdir("/tmp/run/", 0555);
209 mkdir("/tmp/mounts", 0555);
210 system_printf("rm -rf %s*", uci_path);
211 if(uci_timeout < 16)
212 uci_timeout = 16;
213 umount_autofs();
214 mount_init();
215 if(mount_autofs() < 0)
216 {
217 closelog();
218 exit(1);
219 }
220 ioctl(fdin, AUTOFS_IOC_PROTOVER, &kproto_version);
221 if(kproto_version != 5)
222 {
223 log_printf("only kernel protocol version 5 is tested. You have %d.\n",
224 kproto_version);
225 closelog();
226 exit(1);
227 }
228 ioctl(fdin, AUTOFS_IOC_SETTIMEOUT, &uci_timeout);
229 timer_add(expire_proc, 15);
230 }
231
232 int autofs_loop(void)
233 {
234 chdir("/");
235 autofs_init();
236 while(!term)
237 {
238 union autofs_v5_packet_union pkt;
239 if(autofs_in(&pkt))
240 {
241 continue;
242 }
243 log_printf("Got a autofs packet\n");
244 if(pkt.hdr.type == autofs_ptype_missing_indirect)
245 autofs_process_request(&pkt.missing_indirect);
246 else
247 log_printf("unknown packet type %d\n", pkt.hdr.type);
248 poll(0, 0, 200);
249 }
250 autofs_cleanup();
251 log_printf("... quitting\n");
252 closelog();
253 return 0;
254 }