1 | // |
2 | // File-system system calls. |
3 | // Mostly argument checking, since we don't trust |
4 | // user code, and calls into file.c and fs.c. |
5 | // |
6 | |
7 | #include "types.h" |
8 | #include "defs.h" |
9 | #include "param.h" |
10 | #include "stat.h" |
11 | #include "mmu.h" |
12 | #include "proc.h" |
13 | #include "fs.h" |
14 | #include "spinlock.h" |
15 | #include "sleeplock.h" |
16 | #include "file.h" |
17 | #include "fcntl.h" |
18 | |
19 | // Fetch the nth word-sized system call argument as a file descriptor |
20 | // and return both the descriptor and the corresponding struct file. |
21 | static int |
22 | argfd(int n, int *pfd, struct file **pf) |
23 | { |
24 | int fd; |
25 | struct file *f; |
26 | |
27 | if(argint(n, &fd) < 0) |
28 | return -1; |
29 | if(fd < 0 || fd >= NOFILE || (f=myproc()->ofile[fd]) == 0) |
30 | return -1; |
31 | if(pfd) |
32 | *pfd = fd; |
33 | if(pf) |
34 | *pf = f; |
35 | return 0; |
36 | } |
37 | |
38 | // Allocate a file descriptor for the given file. |
39 | // Takes over file reference from caller on success. |
40 | static int |
41 | fdalloc(struct file *f) |
42 | { |
43 | int fd; |
44 | struct proc *curproc = myproc(); |
45 | |
46 | for(fd = 0; fd < NOFILE; fd++){ |
47 | if(curproc->ofile[fd] == 0){ |
48 | curproc->ofile[fd] = f; |
49 | return fd; |
50 | } |
51 | } |
52 | return -1; |
53 | } |
54 | |
55 | int |
56 | sys_dup(void) |
57 | { |
58 | struct file *f; |
59 | int fd; |
60 | |
61 | if(argfd(0, 0, &f) < 0) |
62 | return -1; |
63 | if((fd=fdalloc(f)) < 0) |
64 | return -1; |
65 | filedup(f); |
66 | return fd; |
67 | } |
68 | |
69 | int |
70 | sys_read(void) |
71 | { |
72 | struct file *f; |
73 | int n; |
74 | char *p; |
75 | |
76 | if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) |
77 | return -1; |
78 | return fileread(f, p, n); |
79 | } |
80 | |
81 | int |
82 | sys_write(void) |
83 | { |
84 | struct file *f; |
85 | int n; |
86 | char *p; |
87 | |
88 | if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) |
89 | return -1; |
90 | return filewrite(f, p, n); |
91 | } |
92 | |
93 | int |
94 | sys_close(void) |
95 | { |
96 | int fd; |
97 | struct file *f; |
98 | |
99 | if(argfd(0, &fd, &f) < 0) |
100 | return -1; |
101 | myproc()->ofile[fd] = 0; |
102 | fileclose(f); |
103 | return 0; |
104 | } |
105 | |
106 | int |
107 | sys_fstat(void) |
108 | { |
109 | struct file *f; |
110 | struct stat *st; |
111 | |
112 | if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0) |
113 | return -1; |
114 | return filestat(f, st); |
115 | } |
116 | |
117 | // Create the path new as a link to the same inode as old. |
118 | int |
119 | sys_link(void) |
120 | { |
121 | char name[DIRSIZ], *new, *old; |
122 | struct inode *dp, *ip; |
123 | |
124 | if(argstr(0, &old) < 0 || argstr(1, &new) < 0) |
125 | return -1; |
126 | |
127 | begin_op(); |
128 | if((ip = namei(old)) == 0){ |
129 | end_op(); |
130 | return -1; |
131 | } |
132 | |
133 | ilock(ip); |
134 | if(ip->type == T_DIR){ |
135 | iunlockput(ip); |
136 | end_op(); |
137 | return -1; |
138 | } |
139 | |
140 | ip->nlink++; |
141 | iupdate(ip); |
142 | iunlock(ip); |
143 | |
144 | if((dp = nameiparent(new, name)) == 0) |
145 | goto bad; |
146 | ilock(dp); |
147 | if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){ |
148 | iunlockput(dp); |
149 | goto bad; |
150 | } |
151 | iunlockput(dp); |
152 | iput(ip); |
153 | |
154 | end_op(); |
155 | |
156 | return 0; |
157 | |
158 | bad: |
159 | ilock(ip); |
160 | ip->nlink--; |
161 | iupdate(ip); |
162 | iunlockput(ip); |
163 | end_op(); |
164 | return -1; |
165 | } |
166 | |
167 | // Is the directory dp empty except for "." and ".." ? |
168 | static int |
169 | isdirempty(struct inode *dp) |
170 | { |
171 | int off; |
172 | struct dirent de; |
173 | |
174 | for(off=2*sizeof(de); off<dp->size; off+=sizeof(de)){ |
175 | if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) |
176 | panic("isdirempty: readi" ); |
177 | if(de.inum != 0) |
178 | return 0; |
179 | } |
180 | return 1; |
181 | } |
182 | |
183 | //PAGEBREAK! |
184 | int |
185 | sys_unlink(void) |
186 | { |
187 | struct inode *ip, *dp; |
188 | struct dirent de; |
189 | char name[DIRSIZ], *path; |
190 | uint off; |
191 | |
192 | if(argstr(0, &path) < 0) |
193 | return -1; |
194 | |
195 | begin_op(); |
196 | if((dp = nameiparent(path, name)) == 0){ |
197 | end_op(); |
198 | return -1; |
199 | } |
200 | |
201 | ilock(dp); |
202 | |
203 | // Cannot unlink "." or "..". |
204 | if(namecmp(name, "." ) == 0 || namecmp(name, ".." ) == 0) |
205 | goto bad; |
206 | |
207 | if((ip = dirlookup(dp, name, &off)) == 0) |
208 | goto bad; |
209 | ilock(ip); |
210 | |
211 | if(ip->nlink < 1) |
212 | panic("unlink: nlink < 1" ); |
213 | if(ip->type == T_DIR && !isdirempty(ip)){ |
214 | iunlockput(ip); |
215 | goto bad; |
216 | } |
217 | |
218 | memset(&de, 0, sizeof(de)); |
219 | if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) |
220 | panic("unlink: writei" ); |
221 | if(ip->type == T_DIR){ |
222 | dp->nlink--; |
223 | iupdate(dp); |
224 | } |
225 | iunlockput(dp); |
226 | |
227 | ip->nlink--; |
228 | iupdate(ip); |
229 | iunlockput(ip); |
230 | |
231 | end_op(); |
232 | |
233 | return 0; |
234 | |
235 | bad: |
236 | iunlockput(dp); |
237 | end_op(); |
238 | return -1; |
239 | } |
240 | |
241 | static struct inode* |
242 | create(char *path, short type, short major, short minor) |
243 | { |
244 | uint off; |
245 | struct inode *ip, *dp; |
246 | char name[DIRSIZ]; |
247 | |
248 | if((dp = nameiparent(path, name)) == 0) |
249 | return 0; |
250 | ilock(dp); |
251 | |
252 | if((ip = dirlookup(dp, name, &off)) != 0){ |
253 | iunlockput(dp); |
254 | ilock(ip); |
255 | if(type == T_FILE && ip->type == T_FILE) |
256 | return ip; |
257 | iunlockput(ip); |
258 | return 0; |
259 | } |
260 | |
261 | if((ip = ialloc(dp->dev, type)) == 0) |
262 | panic("create: ialloc" ); |
263 | |
264 | ilock(ip); |
265 | ip->major = major; |
266 | ip->minor = minor; |
267 | ip->nlink = 1; |
268 | iupdate(ip); |
269 | |
270 | if(type == T_DIR){ // Create . and .. entries. |
271 | dp->nlink++; // for ".." |
272 | iupdate(dp); |
273 | // No ip->nlink++ for ".": avoid cyclic ref count. |
274 | if(dirlink(ip, "." , ip->inum) < 0 || dirlink(ip, ".." , dp->inum) < 0) |
275 | panic("create dots" ); |
276 | } |
277 | |
278 | if(dirlink(dp, name, ip->inum) < 0) |
279 | panic("create: dirlink" ); |
280 | |
281 | iunlockput(dp); |
282 | |
283 | return ip; |
284 | } |
285 | |
286 | int |
287 | sys_open(void) |
288 | { |
289 | char *path; |
290 | int fd, omode; |
291 | struct file *f; |
292 | struct inode *ip; |
293 | |
294 | if(argstr(0, &path) < 0 || argint(1, &omode) < 0) |
295 | return -1; |
296 | |
297 | begin_op(); |
298 | |
299 | if(omode & O_CREATE){ |
300 | ip = create(path, T_FILE, 0, 0); |
301 | if(ip == 0){ |
302 | end_op(); |
303 | return -1; |
304 | } |
305 | } else { |
306 | if((ip = namei(path)) == 0){ |
307 | end_op(); |
308 | return -1; |
309 | } |
310 | ilock(ip); |
311 | if(ip->type == T_DIR && omode != O_RDONLY){ |
312 | iunlockput(ip); |
313 | end_op(); |
314 | return -1; |
315 | } |
316 | } |
317 | |
318 | if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){ |
319 | if(f) |
320 | fileclose(f); |
321 | iunlockput(ip); |
322 | end_op(); |
323 | return -1; |
324 | } |
325 | iunlock(ip); |
326 | end_op(); |
327 | |
328 | f->type = FD_INODE; |
329 | f->ip = ip; |
330 | f->off = 0; |
331 | f->readable = !(omode & O_WRONLY); |
332 | f->writable = (omode & O_WRONLY) || (omode & O_RDWR); |
333 | return fd; |
334 | } |
335 | |
336 | int |
337 | sys_mkdir(void) |
338 | { |
339 | char *path; |
340 | struct inode *ip; |
341 | |
342 | begin_op(); |
343 | if(argstr(0, &path) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){ |
344 | end_op(); |
345 | return -1; |
346 | } |
347 | iunlockput(ip); |
348 | end_op(); |
349 | return 0; |
350 | } |
351 | |
352 | int |
353 | sys_mknod(void) |
354 | { |
355 | struct inode *ip; |
356 | char *path; |
357 | int major, minor; |
358 | |
359 | begin_op(); |
360 | if((argstr(0, &path)) < 0 || |
361 | argint(1, &major) < 0 || |
362 | argint(2, &minor) < 0 || |
363 | (ip = create(path, T_DEV, major, minor)) == 0){ |
364 | end_op(); |
365 | return -1; |
366 | } |
367 | iunlockput(ip); |
368 | end_op(); |
369 | return 0; |
370 | } |
371 | |
372 | int |
373 | sys_chdir(void) |
374 | { |
375 | char *path; |
376 | struct inode *ip; |
377 | struct proc *curproc = myproc(); |
378 | |
379 | begin_op(); |
380 | if(argstr(0, &path) < 0 || (ip = namei(path)) == 0){ |
381 | end_op(); |
382 | return -1; |
383 | } |
384 | ilock(ip); |
385 | if(ip->type != T_DIR){ |
386 | iunlockput(ip); |
387 | end_op(); |
388 | return -1; |
389 | } |
390 | iunlock(ip); |
391 | iput(curproc->cwd); |
392 | end_op(); |
393 | curproc->cwd = ip; |
394 | return 0; |
395 | } |
396 | |
397 | int |
398 | sys_exec(void) |
399 | { |
400 | char *path, *argv[MAXARG]; |
401 | int i; |
402 | uint uargv, uarg; |
403 | |
404 | if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){ |
405 | return -1; |
406 | } |
407 | memset(argv, 0, sizeof(argv)); |
408 | for(i=0;; i++){ |
409 | if(i >= NELEM(argv)) |
410 | return -1; |
411 | if(fetchint(uargv+4*i, (int*)&uarg) < 0) |
412 | return -1; |
413 | if(uarg == 0){ |
414 | argv[i] = 0; |
415 | break; |
416 | } |
417 | if(fetchstr(uarg, &argv[i]) < 0) |
418 | return -1; |
419 | } |
420 | return exec(path, argv); |
421 | } |
422 | |
423 | int |
424 | sys_pipe(void) |
425 | { |
426 | int *fd; |
427 | struct file *rf, *wf; |
428 | int fd0, fd1; |
429 | |
430 | if(argptr(0, (void*)&fd, 2*sizeof(fd[0])) < 0) |
431 | return -1; |
432 | if(pipealloc(&rf, &wf) < 0) |
433 | return -1; |
434 | fd0 = -1; |
435 | if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){ |
436 | if(fd0 >= 0) |
437 | myproc()->ofile[fd0] = 0; |
438 | fileclose(rf); |
439 | fileclose(wf); |
440 | return -1; |
441 | } |
442 | fd[0] = fd0; |
443 | fd[1] = fd1; |
444 | return 0; |
445 | } |
446 | |