diff options
Diffstat (limited to 'src/fs/namei.c')
-rw-r--r-- | src/fs/namei.c | 778 |
1 files changed, 778 insertions, 0 deletions
diff --git a/src/fs/namei.c b/src/fs/namei.c new file mode 100644 index 0000000..97d0de4 --- /dev/null +++ b/src/fs/namei.c | |||
@@ -0,0 +1,778 @@ | |||
1 | /* | ||
2 | * linux/fs/namei.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * Some corrections by tytso. | ||
9 | */ | ||
10 | |||
11 | #include <linux/sched.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <asm/segment.h> | ||
14 | |||
15 | #include <string.h> | ||
16 | #include <fcntl.h> | ||
17 | #include <errno.h> | ||
18 | #include <const.h> | ||
19 | #include <sys/stat.h> | ||
20 | |||
21 | #define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE]) | ||
22 | |||
23 | /* | ||
24 | * comment out this line if you want names > NAME_LEN chars to be | ||
25 | * truncated. Else they will be disallowed. | ||
26 | */ | ||
27 | /* #define NO_TRUNCATE */ | ||
28 | |||
29 | #define MAY_EXEC 1 | ||
30 | #define MAY_WRITE 2 | ||
31 | #define MAY_READ 4 | ||
32 | |||
33 | /* | ||
34 | * permission() | ||
35 | * | ||
36 | * is used to check for read/write/execute permissions on a file. | ||
37 | * I don't know if we should look at just the euid or both euid and | ||
38 | * uid, but that should be easily changed. | ||
39 | */ | ||
40 | static int permission(struct m_inode * inode,int mask) | ||
41 | { | ||
42 | int mode = inode->i_mode; | ||
43 | |||
44 | /* special case: not even root can read/write a deleted file */ | ||
45 | if (inode->i_dev && !inode->i_nlinks) | ||
46 | return 0; | ||
47 | else if (current->euid==inode->i_uid) | ||
48 | mode >>= 6; | ||
49 | else if (current->egid==inode->i_gid) | ||
50 | mode >>= 3; | ||
51 | if (((mode & mask & 0007) == mask) || suser()) | ||
52 | return 1; | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * ok, we cannot use strncmp, as the name is not in our data space. | ||
58 | * Thus we'll have to use match. No big problem. Match also makes | ||
59 | * some sanity tests. | ||
60 | * | ||
61 | * NOTE! unlike strncmp, match returns 1 for success, 0 for failure. | ||
62 | */ | ||
63 | static int match(int len,const char * name,struct dir_entry * de) | ||
64 | { | ||
65 | register int same ; | ||
66 | |||
67 | if (!de || !de->inode || len > NAME_LEN) | ||
68 | return 0; | ||
69 | if (len < NAME_LEN && de->name[len]) | ||
70 | return 0; | ||
71 | __asm__("cld\n\t" | ||
72 | "fs ; repe ; cmpsb\n\t" | ||
73 | "setz %%al" | ||
74 | :"=a" (same) | ||
75 | :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len) | ||
76 | ); | ||
77 | return same; | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * find_entry() | ||
82 | * | ||
83 | * finds an entry in the specified directory with the wanted name. It | ||
84 | * returns the cache buffer in which the entry was found, and the entry | ||
85 | * itself (as a parameter - res_dir). It does NOT read the inode of the | ||
86 | * entry - you'll have to do that yourself if you want to. | ||
87 | * | ||
88 | * This also takes care of the few special cases due to '..'-traversal | ||
89 | * over a pseudo-root and a mount point. | ||
90 | */ | ||
91 | static struct buffer_head * find_entry(struct m_inode ** dir, | ||
92 | const char * name, int namelen, struct dir_entry ** res_dir) | ||
93 | { | ||
94 | int entries; | ||
95 | int block,i; | ||
96 | struct buffer_head * bh; | ||
97 | struct dir_entry * de; | ||
98 | struct super_block * sb; | ||
99 | |||
100 | #ifdef NO_TRUNCATE | ||
101 | if (namelen > NAME_LEN) | ||
102 | return NULL; | ||
103 | #else | ||
104 | if (namelen > NAME_LEN) | ||
105 | namelen = NAME_LEN; | ||
106 | #endif | ||
107 | entries = (*dir)->i_size / (sizeof (struct dir_entry)); | ||
108 | *res_dir = NULL; | ||
109 | if (!namelen) | ||
110 | return NULL; | ||
111 | /* check for '..', as we might have to do some "magic" for it */ | ||
112 | if (namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') { | ||
113 | /* '..' in a pseudo-root results in a faked '.' (just change namelen) */ | ||
114 | if ((*dir) == current->root) | ||
115 | namelen=1; | ||
116 | else if ((*dir)->i_num == ROOT_INO) { | ||
117 | /* '..' over a mount-point results in 'dir' being exchanged for the mounted | ||
118 | directory-inode. NOTE! We set mounted, so that we can iput the new dir */ | ||
119 | sb=get_super((*dir)->i_dev); | ||
120 | if (sb->s_imount) { | ||
121 | iput(*dir); | ||
122 | (*dir)=sb->s_imount; | ||
123 | (*dir)->i_count++; | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | if (!(block = (*dir)->i_zone[0])) | ||
128 | return NULL; | ||
129 | if (!(bh = bread((*dir)->i_dev,block))) | ||
130 | return NULL; | ||
131 | i = 0; | ||
132 | de = (struct dir_entry *) bh->b_data; | ||
133 | while (i < entries) { | ||
134 | if ((char *)de >= BLOCK_SIZE+bh->b_data) { | ||
135 | brelse(bh); | ||
136 | bh = NULL; | ||
137 | if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) || | ||
138 | !(bh = bread((*dir)->i_dev,block))) { | ||
139 | i += DIR_ENTRIES_PER_BLOCK; | ||
140 | continue; | ||
141 | } | ||
142 | de = (struct dir_entry *) bh->b_data; | ||
143 | } | ||
144 | if (match(namelen,name,de)) { | ||
145 | *res_dir = de; | ||
146 | return bh; | ||
147 | } | ||
148 | de++; | ||
149 | i++; | ||
150 | } | ||
151 | brelse(bh); | ||
152 | return NULL; | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * add_entry() | ||
157 | * | ||
158 | * adds a file entry to the specified directory, using the same | ||
159 | * semantics as find_entry(). It returns NULL if it failed. | ||
160 | * | ||
161 | * NOTE!! The inode part of 'de' is left at 0 - which means you | ||
162 | * may not sleep between calling this and putting something into | ||
163 | * the entry, as someone else might have used it while you slept. | ||
164 | */ | ||
165 | static struct buffer_head * add_entry(struct m_inode * dir, | ||
166 | const char * name, int namelen, struct dir_entry ** res_dir) | ||
167 | { | ||
168 | int block,i; | ||
169 | struct buffer_head * bh; | ||
170 | struct dir_entry * de; | ||
171 | |||
172 | *res_dir = NULL; | ||
173 | #ifdef NO_TRUNCATE | ||
174 | if (namelen > NAME_LEN) | ||
175 | return NULL; | ||
176 | #else | ||
177 | if (namelen > NAME_LEN) | ||
178 | namelen = NAME_LEN; | ||
179 | #endif | ||
180 | if (!namelen) | ||
181 | return NULL; | ||
182 | if (!(block = dir->i_zone[0])) | ||
183 | return NULL; | ||
184 | if (!(bh = bread(dir->i_dev,block))) | ||
185 | return NULL; | ||
186 | i = 0; | ||
187 | de = (struct dir_entry *) bh->b_data; | ||
188 | while (1) { | ||
189 | if ((char *)de >= BLOCK_SIZE+bh->b_data) { | ||
190 | brelse(bh); | ||
191 | bh = NULL; | ||
192 | block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK); | ||
193 | if (!block) | ||
194 | return NULL; | ||
195 | if (!(bh = bread(dir->i_dev,block))) { | ||
196 | i += DIR_ENTRIES_PER_BLOCK; | ||
197 | continue; | ||
198 | } | ||
199 | de = (struct dir_entry *) bh->b_data; | ||
200 | } | ||
201 | if (i*sizeof(struct dir_entry) >= dir->i_size) { | ||
202 | de->inode=0; | ||
203 | dir->i_size = (i+1)*sizeof(struct dir_entry); | ||
204 | dir->i_dirt = 1; | ||
205 | dir->i_ctime = CURRENT_TIME; | ||
206 | } | ||
207 | if (!de->inode) { | ||
208 | dir->i_mtime = CURRENT_TIME; | ||
209 | for (i=0; i < NAME_LEN ; i++) | ||
210 | de->name[i]=(i<namelen)?get_fs_byte(name+i):0; | ||
211 | bh->b_dirt = 1; | ||
212 | *res_dir = de; | ||
213 | return bh; | ||
214 | } | ||
215 | de++; | ||
216 | i++; | ||
217 | } | ||
218 | brelse(bh); | ||
219 | return NULL; | ||
220 | } | ||
221 | |||
222 | /* | ||
223 | * get_dir() | ||
224 | * | ||
225 | * Getdir traverses the pathname until it hits the topmost directory. | ||
226 | * It returns NULL on failure. | ||
227 | */ | ||
228 | static struct m_inode * get_dir(const char * pathname) | ||
229 | { | ||
230 | char c; | ||
231 | const char * thisname; | ||
232 | struct m_inode * inode; | ||
233 | struct buffer_head * bh; | ||
234 | int namelen,inr,idev; | ||
235 | struct dir_entry * de; | ||
236 | |||
237 | if (!current->root || !current->root->i_count) | ||
238 | panic("No root inode"); | ||
239 | if (!current->pwd || !current->pwd->i_count) | ||
240 | panic("No cwd inode"); | ||
241 | if ((c=get_fs_byte(pathname))=='/') { | ||
242 | inode = current->root; | ||
243 | pathname++; | ||
244 | } else if (c) | ||
245 | inode = current->pwd; | ||
246 | else | ||
247 | return NULL; /* empty name is bad */ | ||
248 | inode->i_count++; | ||
249 | while (1) { | ||
250 | thisname = pathname; | ||
251 | if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) { | ||
252 | iput(inode); | ||
253 | return NULL; | ||
254 | } | ||
255 | for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++) | ||
256 | /* nothing */ ; | ||
257 | if (!c) | ||
258 | return inode; | ||
259 | if (!(bh = find_entry(&inode,thisname,namelen,&de))) { | ||
260 | iput(inode); | ||
261 | return NULL; | ||
262 | } | ||
263 | inr = de->inode; | ||
264 | idev = inode->i_dev; | ||
265 | brelse(bh); | ||
266 | iput(inode); | ||
267 | if (!(inode = iget(idev,inr))) | ||
268 | return NULL; | ||
269 | } | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | * dir_namei() | ||
274 | * | ||
275 | * dir_namei() returns the inode of the directory of the | ||
276 | * specified name, and the name within that directory. | ||
277 | */ | ||
278 | static struct m_inode * dir_namei(const char * pathname, | ||
279 | int * namelen, const char ** name) | ||
280 | { | ||
281 | char c; | ||
282 | const char * basename; | ||
283 | struct m_inode * dir; | ||
284 | |||
285 | if (!(dir = get_dir(pathname))) | ||
286 | return NULL; | ||
287 | basename = pathname; | ||
288 | while ((c=get_fs_byte(pathname++))) | ||
289 | if (c=='/') | ||
290 | basename=pathname; | ||
291 | *namelen = pathname-basename-1; | ||
292 | *name = basename; | ||
293 | return dir; | ||
294 | } | ||
295 | |||
296 | /* | ||
297 | * namei() | ||
298 | * | ||
299 | * is used by most simple commands to get the inode of a specified name. | ||
300 | * Open, link etc use their own routines, but this is enough for things | ||
301 | * like 'chmod' etc. | ||
302 | */ | ||
303 | struct m_inode * namei(const char * pathname) | ||
304 | { | ||
305 | const char * basename; | ||
306 | int inr,dev,namelen; | ||
307 | struct m_inode * dir; | ||
308 | struct buffer_head * bh; | ||
309 | struct dir_entry * de; | ||
310 | |||
311 | if (!(dir = dir_namei(pathname,&namelen,&basename))) | ||
312 | return NULL; | ||
313 | if (!namelen) /* special case: '/usr/' etc */ | ||
314 | return dir; | ||
315 | bh = find_entry(&dir,basename,namelen,&de); | ||
316 | if (!bh) { | ||
317 | iput(dir); | ||
318 | return NULL; | ||
319 | } | ||
320 | inr = de->inode; | ||
321 | dev = dir->i_dev; | ||
322 | brelse(bh); | ||
323 | iput(dir); | ||
324 | dir=iget(dev,inr); | ||
325 | if (dir) { | ||
326 | dir->i_atime=CURRENT_TIME; | ||
327 | dir->i_dirt=1; | ||
328 | } | ||
329 | return dir; | ||
330 | } | ||
331 | |||
332 | /* | ||
333 | * open_namei() | ||
334 | * | ||
335 | * namei for open - this is in fact almost the whole open-routine. | ||
336 | */ | ||
337 | int open_namei(const char * pathname, int flag, int mode, | ||
338 | struct m_inode ** res_inode) | ||
339 | { | ||
340 | const char * basename; | ||
341 | int inr,dev,namelen; | ||
342 | struct m_inode * dir, *inode; | ||
343 | struct buffer_head * bh; | ||
344 | struct dir_entry * de; | ||
345 | |||
346 | if ((flag & O_TRUNC) && !(flag & O_ACCMODE)) | ||
347 | flag |= O_WRONLY; | ||
348 | mode &= 0777 & ~current->umask; | ||
349 | mode |= I_REGULAR; | ||
350 | if (!(dir = dir_namei(pathname,&namelen,&basename))) | ||
351 | return -ENOENT; | ||
352 | if (!namelen) { /* special case: '/usr/' etc */ | ||
353 | if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) { | ||
354 | *res_inode=dir; | ||
355 | return 0; | ||
356 | } | ||
357 | iput(dir); | ||
358 | return -EISDIR; | ||
359 | } | ||
360 | bh = find_entry(&dir,basename,namelen,&de); | ||
361 | if (!bh) { | ||
362 | if (!(flag & O_CREAT)) { | ||
363 | iput(dir); | ||
364 | return -ENOENT; | ||
365 | } | ||
366 | if (!permission(dir,MAY_WRITE)) { | ||
367 | iput(dir); | ||
368 | return -EACCES; | ||
369 | } | ||
370 | inode = new_inode(dir->i_dev); | ||
371 | if (!inode) { | ||
372 | iput(dir); | ||
373 | return -ENOSPC; | ||
374 | } | ||
375 | inode->i_uid = current->euid; | ||
376 | inode->i_mode = mode; | ||
377 | inode->i_dirt = 1; | ||
378 | bh = add_entry(dir,basename,namelen,&de); | ||
379 | if (!bh) { | ||
380 | inode->i_nlinks--; | ||
381 | iput(inode); | ||
382 | iput(dir); | ||
383 | return -ENOSPC; | ||
384 | } | ||
385 | de->inode = inode->i_num; | ||
386 | bh->b_dirt = 1; | ||
387 | brelse(bh); | ||
388 | iput(dir); | ||
389 | *res_inode = inode; | ||
390 | return 0; | ||
391 | } | ||
392 | inr = de->inode; | ||
393 | dev = dir->i_dev; | ||
394 | brelse(bh); | ||
395 | iput(dir); | ||
396 | if (flag & O_EXCL) | ||
397 | return -EEXIST; | ||
398 | if (!(inode=iget(dev,inr))) | ||
399 | return -EACCES; | ||
400 | if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) || | ||
401 | !permission(inode,ACC_MODE(flag))) { | ||
402 | iput(inode); | ||
403 | return -EPERM; | ||
404 | } | ||
405 | inode->i_atime = CURRENT_TIME; | ||
406 | if (flag & O_TRUNC) | ||
407 | truncate(inode); | ||
408 | *res_inode = inode; | ||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | int sys_mknod(const char * filename, int mode, int dev) | ||
413 | { | ||
414 | const char * basename; | ||
415 | int namelen; | ||
416 | struct m_inode * dir, * inode; | ||
417 | struct buffer_head * bh; | ||
418 | struct dir_entry * de; | ||
419 | |||
420 | if (!suser()) | ||
421 | return -EPERM; | ||
422 | if (!(dir = dir_namei(filename,&namelen,&basename))) | ||
423 | return -ENOENT; | ||
424 | if (!namelen) { | ||
425 | iput(dir); | ||
426 | return -ENOENT; | ||
427 | } | ||
428 | if (!permission(dir,MAY_WRITE)) { | ||
429 | iput(dir); | ||
430 | return -EPERM; | ||
431 | } | ||
432 | bh = find_entry(&dir,basename,namelen,&de); | ||
433 | if (bh) { | ||
434 | brelse(bh); | ||
435 | iput(dir); | ||
436 | return -EEXIST; | ||
437 | } | ||
438 | inode = new_inode(dir->i_dev); | ||
439 | if (!inode) { | ||
440 | iput(dir); | ||
441 | return -ENOSPC; | ||
442 | } | ||
443 | inode->i_mode = mode; | ||
444 | if (S_ISBLK(mode) || S_ISCHR(mode)) | ||
445 | inode->i_zone[0] = dev; | ||
446 | inode->i_mtime = inode->i_atime = CURRENT_TIME; | ||
447 | inode->i_dirt = 1; | ||
448 | bh = add_entry(dir,basename,namelen,&de); | ||
449 | if (!bh) { | ||
450 | iput(dir); | ||
451 | inode->i_nlinks=0; | ||
452 | iput(inode); | ||
453 | return -ENOSPC; | ||
454 | } | ||
455 | de->inode = inode->i_num; | ||
456 | bh->b_dirt = 1; | ||
457 | iput(dir); | ||
458 | iput(inode); | ||
459 | brelse(bh); | ||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | int sys_mkdir(const char * pathname, int mode) | ||
464 | { | ||
465 | const char * basename; | ||
466 | int namelen; | ||
467 | struct m_inode * dir, * inode; | ||
468 | struct buffer_head * bh, *dir_block; | ||
469 | struct dir_entry * de; | ||
470 | |||
471 | if (!suser()) | ||
472 | return -EPERM; | ||
473 | if (!(dir = dir_namei(pathname,&namelen,&basename))) | ||
474 | return -ENOENT; | ||
475 | if (!namelen) { | ||
476 | iput(dir); | ||
477 | return -ENOENT; | ||
478 | } | ||
479 | if (!permission(dir,MAY_WRITE)) { | ||
480 | iput(dir); | ||
481 | return -EPERM; | ||
482 | } | ||
483 | bh = find_entry(&dir,basename,namelen,&de); | ||
484 | if (bh) { | ||
485 | brelse(bh); | ||
486 | iput(dir); | ||
487 | return -EEXIST; | ||
488 | } | ||
489 | inode = new_inode(dir->i_dev); | ||
490 | if (!inode) { | ||
491 | iput(dir); | ||
492 | return -ENOSPC; | ||
493 | } | ||
494 | inode->i_size = 32; | ||
495 | inode->i_dirt = 1; | ||
496 | inode->i_mtime = inode->i_atime = CURRENT_TIME; | ||
497 | if (!(inode->i_zone[0]=new_block(inode->i_dev))) { | ||
498 | iput(dir); | ||
499 | inode->i_nlinks--; | ||
500 | iput(inode); | ||
501 | return -ENOSPC; | ||
502 | } | ||
503 | inode->i_dirt = 1; | ||
504 | if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) { | ||
505 | iput(dir); | ||
506 | free_block(inode->i_dev,inode->i_zone[0]); | ||
507 | inode->i_nlinks--; | ||
508 | iput(inode); | ||
509 | return -ERROR; | ||
510 | } | ||
511 | de = (struct dir_entry *) dir_block->b_data; | ||
512 | de->inode=inode->i_num; | ||
513 | strcpy(de->name,"."); | ||
514 | de++; | ||
515 | de->inode = dir->i_num; | ||
516 | strcpy(de->name,".."); | ||
517 | inode->i_nlinks = 2; | ||
518 | dir_block->b_dirt = 1; | ||
519 | brelse(dir_block); | ||
520 | inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask); | ||
521 | inode->i_dirt = 1; | ||
522 | bh = add_entry(dir,basename,namelen,&de); | ||
523 | if (!bh) { | ||
524 | iput(dir); | ||
525 | free_block(inode->i_dev,inode->i_zone[0]); | ||
526 | inode->i_nlinks=0; | ||
527 | iput(inode); | ||
528 | return -ENOSPC; | ||
529 | } | ||
530 | de->inode = inode->i_num; | ||
531 | bh->b_dirt = 1; | ||
532 | dir->i_nlinks++; | ||
533 | dir->i_dirt = 1; | ||
534 | iput(dir); | ||
535 | iput(inode); | ||
536 | brelse(bh); | ||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | /* | ||
541 | * routine to check that the specified directory is empty (for rmdir) | ||
542 | */ | ||
543 | static int empty_dir(struct m_inode * inode) | ||
544 | { | ||
545 | int nr,block; | ||
546 | int len; | ||
547 | struct buffer_head * bh; | ||
548 | struct dir_entry * de; | ||
549 | |||
550 | len = inode->i_size / sizeof (struct dir_entry); | ||
551 | if (len<2 || !inode->i_zone[0] || | ||
552 | !(bh=bread(inode->i_dev,inode->i_zone[0]))) { | ||
553 | printk("warning - bad directory on dev %04x\n",inode->i_dev); | ||
554 | return 0; | ||
555 | } | ||
556 | de = (struct dir_entry *) bh->b_data; | ||
557 | if (de[0].inode != inode->i_num || !de[1].inode || | ||
558 | strcmp(".",de[0].name) || strcmp("..",de[1].name)) { | ||
559 | printk("warning - bad directory on dev %04x\n",inode->i_dev); | ||
560 | return 0; | ||
561 | } | ||
562 | nr = 2; | ||
563 | de += 2; | ||
564 | while (nr<len) { | ||
565 | if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) { | ||
566 | brelse(bh); | ||
567 | block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK); | ||
568 | if (!block) { | ||
569 | nr += DIR_ENTRIES_PER_BLOCK; | ||
570 | continue; | ||
571 | } | ||
572 | if (!(bh=bread(inode->i_dev,block))) | ||
573 | return 0; | ||
574 | de = (struct dir_entry *) bh->b_data; | ||
575 | } | ||
576 | if (de->inode) { | ||
577 | brelse(bh); | ||
578 | return 0; | ||
579 | } | ||
580 | de++; | ||
581 | nr++; | ||
582 | } | ||
583 | brelse(bh); | ||
584 | return 1; | ||
585 | } | ||
586 | |||
587 | int sys_rmdir(const char * name) | ||
588 | { | ||
589 | const char * basename; | ||
590 | int namelen; | ||
591 | struct m_inode * dir, * inode; | ||
592 | struct buffer_head * bh; | ||
593 | struct dir_entry * de; | ||
594 | |||
595 | if (!suser()) | ||
596 | return -EPERM; | ||
597 | if (!(dir = dir_namei(name,&namelen,&basename))) | ||
598 | return -ENOENT; | ||
599 | if (!namelen) { | ||
600 | iput(dir); | ||
601 | return -ENOENT; | ||
602 | } | ||
603 | if (!permission(dir,MAY_WRITE)) { | ||
604 | iput(dir); | ||
605 | return -EPERM; | ||
606 | } | ||
607 | bh = find_entry(&dir,basename,namelen,&de); | ||
608 | if (!bh) { | ||
609 | iput(dir); | ||
610 | return -ENOENT; | ||
611 | } | ||
612 | if (!(inode = iget(dir->i_dev, de->inode))) { | ||
613 | iput(dir); | ||
614 | brelse(bh); | ||
615 | return -EPERM; | ||
616 | } | ||
617 | if ((dir->i_mode & S_ISVTX) && current->euid && | ||
618 | inode->i_uid != current->euid) { | ||
619 | iput(dir); | ||
620 | iput(inode); | ||
621 | brelse(bh); | ||
622 | return -EPERM; | ||
623 | } | ||
624 | if (inode->i_dev != dir->i_dev || inode->i_count>1) { | ||
625 | iput(dir); | ||
626 | iput(inode); | ||
627 | brelse(bh); | ||
628 | return -EPERM; | ||
629 | } | ||
630 | if (inode == dir) { /* we may not delete ".", but "../dir" is ok */ | ||
631 | iput(inode); | ||
632 | iput(dir); | ||
633 | brelse(bh); | ||
634 | return -EPERM; | ||
635 | } | ||
636 | if (!S_ISDIR(inode->i_mode)) { | ||
637 | iput(inode); | ||
638 | iput(dir); | ||
639 | brelse(bh); | ||
640 | return -ENOTDIR; | ||
641 | } | ||
642 | if (!empty_dir(inode)) { | ||
643 | iput(inode); | ||
644 | iput(dir); | ||
645 | brelse(bh); | ||
646 | return -ENOTEMPTY; | ||
647 | } | ||
648 | if (inode->i_nlinks != 2) | ||
649 | printk("empty directory has nlink!=2 (%d)",inode->i_nlinks); | ||
650 | de->inode = 0; | ||
651 | bh->b_dirt = 1; | ||
652 | brelse(bh); | ||
653 | inode->i_nlinks=0; | ||
654 | inode->i_dirt=1; | ||
655 | dir->i_nlinks--; | ||
656 | dir->i_ctime = dir->i_mtime = CURRENT_TIME; | ||
657 | dir->i_dirt=1; | ||
658 | iput(dir); | ||
659 | iput(inode); | ||
660 | return 0; | ||
661 | } | ||
662 | |||
663 | int sys_unlink(const char * name) | ||
664 | { | ||
665 | const char * basename; | ||
666 | int namelen; | ||
667 | struct m_inode * dir, * inode; | ||
668 | struct buffer_head * bh; | ||
669 | struct dir_entry * de; | ||
670 | |||
671 | if (!(dir = dir_namei(name,&namelen,&basename))) | ||
672 | return -ENOENT; | ||
673 | if (!namelen) { | ||
674 | iput(dir); | ||
675 | return -ENOENT; | ||
676 | } | ||
677 | if (!permission(dir,MAY_WRITE)) { | ||
678 | iput(dir); | ||
679 | return -EPERM; | ||
680 | } | ||
681 | bh = find_entry(&dir,basename,namelen,&de); | ||
682 | if (!bh) { | ||
683 | iput(dir); | ||
684 | return -ENOENT; | ||
685 | } | ||
686 | if (!(inode = iget(dir->i_dev, de->inode))) { | ||
687 | iput(dir); | ||
688 | brelse(bh); | ||
689 | return -ENOENT; | ||
690 | } | ||
691 | if ((dir->i_mode & S_ISVTX) && !suser() && | ||
692 | current->euid != inode->i_uid && | ||
693 | current->euid != dir->i_uid) { | ||
694 | iput(dir); | ||
695 | iput(inode); | ||
696 | brelse(bh); | ||
697 | return -EPERM; | ||
698 | } | ||
699 | if (S_ISDIR(inode->i_mode)) { | ||
700 | iput(inode); | ||
701 | iput(dir); | ||
702 | brelse(bh); | ||
703 | return -EPERM; | ||
704 | } | ||
705 | if (!inode->i_nlinks) { | ||
706 | printk("Deleting nonexistent file (%04x:%d), %d\n", | ||
707 | inode->i_dev,inode->i_num,inode->i_nlinks); | ||
708 | inode->i_nlinks=1; | ||
709 | } | ||
710 | de->inode = 0; | ||
711 | bh->b_dirt = 1; | ||
712 | brelse(bh); | ||
713 | inode->i_nlinks--; | ||
714 | inode->i_dirt = 1; | ||
715 | inode->i_ctime = CURRENT_TIME; | ||
716 | iput(inode); | ||
717 | iput(dir); | ||
718 | return 0; | ||
719 | } | ||
720 | |||
721 | int sys_link(const char * oldname, const char * newname) | ||
722 | { | ||
723 | struct dir_entry * de; | ||
724 | struct m_inode * oldinode, * dir; | ||
725 | struct buffer_head * bh; | ||
726 | const char * basename; | ||
727 | int namelen; | ||
728 | |||
729 | oldinode=namei(oldname); | ||
730 | if (!oldinode) | ||
731 | return -ENOENT; | ||
732 | if (S_ISDIR(oldinode->i_mode)) { | ||
733 | iput(oldinode); | ||
734 | return -EPERM; | ||
735 | } | ||
736 | dir = dir_namei(newname,&namelen,&basename); | ||
737 | if (!dir) { | ||
738 | iput(oldinode); | ||
739 | return -EACCES; | ||
740 | } | ||
741 | if (!namelen) { | ||
742 | iput(oldinode); | ||
743 | iput(dir); | ||
744 | return -EPERM; | ||
745 | } | ||
746 | if (dir->i_dev != oldinode->i_dev) { | ||
747 | iput(dir); | ||
748 | iput(oldinode); | ||
749 | return -EXDEV; | ||
750 | } | ||
751 | if (!permission(dir,MAY_WRITE)) { | ||
752 | iput(dir); | ||
753 | iput(oldinode); | ||
754 | return -EACCES; | ||
755 | } | ||
756 | bh = find_entry(&dir,basename,namelen,&de); | ||
757 | if (bh) { | ||
758 | brelse(bh); | ||
759 | iput(dir); | ||
760 | iput(oldinode); | ||
761 | return -EEXIST; | ||
762 | } | ||
763 | bh = add_entry(dir,basename,namelen,&de); | ||
764 | if (!bh) { | ||
765 | iput(dir); | ||
766 | iput(oldinode); | ||
767 | return -ENOSPC; | ||
768 | } | ||
769 | de->inode = oldinode->i_num; | ||
770 | bh->b_dirt = 1; | ||
771 | brelse(bh); | ||
772 | iput(dir); | ||
773 | oldinode->i_nlinks++; | ||
774 | oldinode->i_ctime = CURRENT_TIME; | ||
775 | oldinode->i_dirt = 1; | ||
776 | iput(oldinode); | ||
777 | return 0; | ||
778 | } | ||