0
|
1 // File system implementation. Five layers:
|
|
2 // + Blocks: allocator for raw disk blocks.
|
|
3 // + Log: crash recovery for multi-step updates.
|
|
4 // + Files: inode allocator, reading, writing, metadata.
|
|
5 // + Directories: inode with special contents (list of other inodes!)
|
|
6 // + Names: paths like /usr/rtm/xv6/fs.c for convenient naming.
|
|
7 //
|
|
8 // This file contains the low-level file system manipulation
|
|
9 // routines. The (higher-level) system call implementations
|
|
10 // are in sysfile.c.
|
|
11
|
|
12 #include "types.h"
|
|
13 #include "defs.h"
|
|
14 #include "param.h"
|
|
15 #include "stat.h"
|
|
16 #include "mmu.h"
|
|
17 #include "proc.h"
|
|
18 #include "spinlock.h"
|
|
19 #include "buf.h"
|
|
20 #include "fs.h"
|
|
21 #include "file.h"
|
|
22
|
|
23 #define min(a, b) ((a) < (b) ? (a) : (b))
|
|
24 static void itrunc (struct inode*);
|
|
25
|
|
26 // Read the super block.
|
|
27 void readsb (int dev, struct superblock *sb)
|
|
28 {
|
|
29 struct buf *bp;
|
|
30
|
|
31 bp = bread(dev, 1);
|
|
32 memmove(sb, bp->data, sizeof(*sb));
|
|
33 brelse(bp);
|
|
34 }
|
|
35
|
|
36 // Zero a block.
|
|
37 static void bzero (int dev, int bno)
|
|
38 {
|
|
39 struct buf *bp;
|
|
40
|
|
41 bp = bread(dev, bno);
|
|
42 memset(bp->data, 0, BSIZE);
|
|
43 log_write(bp);
|
|
44 brelse(bp);
|
|
45 }
|
|
46
|
|
47 // Blocks.
|
|
48
|
|
49 // Allocate a zeroed disk block.
|
|
50 static uint balloc (uint dev)
|
|
51 {
|
|
52 int b, bi, m;
|
|
53 struct buf *bp;
|
|
54 struct superblock sb;
|
|
55
|
|
56 bp = 0;
|
|
57 readsb(dev, &sb);
|
|
58
|
|
59 for (b = 0; b < sb.size; b += BPB) {
|
|
60 bp = bread(dev, BBLOCK(b, sb.ninodes));
|
|
61
|
|
62 for (bi = 0; bi < BPB && b + bi < sb.size; bi++) {
|
|
63 m = 1 << (bi % 8);
|
|
64
|
|
65 if ((bp->data[bi / 8] & m) == 0) { // Is block free?
|
|
66 bp->data[bi / 8] |= m; // Mark block in use.
|
|
67 log_write(bp);
|
|
68 brelse(bp);
|
|
69 bzero(dev, b + bi);
|
|
70 return b + bi;
|
|
71 }
|
|
72 }
|
|
73
|
|
74 brelse(bp);
|
|
75 }
|
|
76
|
|
77 panic("balloc: out of blocks");
|
|
78 }
|
|
79
|
|
80 // Free a disk block.
|
|
81 static void bfree (int dev, uint b)
|
|
82 {
|
|
83 struct buf *bp;
|
|
84 struct superblock sb;
|
|
85 int bi, m;
|
|
86
|
|
87 readsb(dev, &sb);
|
|
88 bp = bread(dev, BBLOCK(b, sb.ninodes));
|
|
89 bi = b % BPB;
|
|
90 m = 1 << (bi % 8);
|
|
91
|
|
92 if ((bp->data[bi / 8] & m) == 0) {
|
|
93 panic("freeing free block");
|
|
94 }
|
|
95
|
|
96 bp->data[bi / 8] &= ~m;
|
|
97 log_write(bp);
|
|
98 brelse(bp);
|
|
99 }
|
|
100
|
|
101 // Inodes.
|
|
102 //
|
|
103 // An inode describes a single unnamed file.
|
|
104 // The inode disk structure holds metadata: the file's type,
|
|
105 // its size, the number of links referring to it, and the
|
|
106 // list of blocks holding the file's content.
|
|
107 //
|
|
108 // The inodes are laid out sequentially on disk immediately after
|
|
109 // the superblock. Each inode has a number, indicating its
|
|
110 // position on the disk.
|
|
111 //
|
|
112 // The kernel keeps a cache of in-use inodes in memory
|
|
113 // to provide a place for synchronizing access
|
|
114 // to inodes used by multiple processes. The cached
|
|
115 // inodes include book-keeping information that is
|
|
116 // not stored on disk: ip->ref and ip->flags.
|
|
117 //
|
|
118 // An inode and its in-memory represtative go through a
|
|
119 // sequence of states before they can be used by the
|
|
120 // rest of the file system code.
|
|
121 //
|
|
122 // * Allocation: an inode is allocated if its type (on disk)
|
|
123 // is non-zero. ialloc() allocates, iput() frees if
|
|
124 // the link count has fallen to zero.
|
|
125 //
|
|
126 // * Referencing in cache: an entry in the inode cache
|
|
127 // is free if ip->ref is zero. Otherwise ip->ref tracks
|
|
128 // the number of in-memory pointers to the entry (open
|
|
129 // files and current directories). iget() to find or
|
|
130 // create a cache entry and increment its ref, iput()
|
|
131 // to decrement ref.
|
|
132 //
|
|
133 // * Valid: the information (type, size, &c) in an inode
|
|
134 // cache entry is only correct when the I_VALID bit
|
|
135 // is set in ip->flags. ilock() reads the inode from
|
|
136 // the disk and sets I_VALID, while iput() clears
|
|
137 // I_VALID if ip->ref has fallen to zero.
|
|
138 //
|
|
139 // * Locked: file system code may only examine and modify
|
|
140 // the information in an inode and its content if it
|
|
141 // has first locked the inode. The I_BUSY flag indicates
|
|
142 // that the inode is locked. ilock() sets I_BUSY,
|
|
143 // while iunlock clears it.
|
|
144 //
|
|
145 // Thus a typical sequence is:
|
|
146 // ip = iget(dev, inum)
|
|
147 // ilock(ip)
|
|
148 // ... examine and modify ip->xxx ...
|
|
149 // iunlock(ip)
|
|
150 // iput(ip)
|
|
151 //
|
|
152 // ilock() is separate from iget() so that system calls can
|
|
153 // get a long-term reference to an inode (as for an open file)
|
|
154 // and only lock it for short periods (e.g., in read()).
|
|
155 // The separation also helps avoid deadlock and races during
|
|
156 // pathname lookup. iget() increments ip->ref so that the inode
|
|
157 // stays cached and pointers to it remain valid.
|
|
158 //
|
|
159 // Many internal file system functions expect the caller to
|
|
160 // have locked the inodes involved; this lets callers create
|
|
161 // multi-step atomic operations.
|
|
162
|
|
163 struct {
|
|
164 struct spinlock lock;
|
|
165 struct inode inode[NINODE];
|
|
166 } icache;
|
|
167
|
|
168 void iinit (void)
|
|
169 {
|
|
170 initlock(&icache.lock, "icache");
|
|
171 }
|
|
172
|
|
173 static struct inode* iget (uint dev, uint inum);
|
|
174
|
|
175 //PAGEBREAK!
|
|
176 // Allocate a new inode with the given type on device dev.
|
|
177 // A free inode has a type of zero.
|
|
178 struct inode* ialloc (uint dev, short type)
|
|
179 {
|
|
180 int inum;
|
|
181 struct buf *bp;
|
|
182 struct dinode *dip;
|
|
183 struct superblock sb;
|
|
184
|
|
185 readsb(dev, &sb);
|
|
186
|
|
187 for (inum = 1; inum < sb.ninodes; inum++) {
|
|
188 bp = bread(dev, IBLOCK(inum));
|
|
189 dip = (struct dinode*) bp->data + inum % IPB;
|
|
190
|
|
191 if (dip->type == 0) { // a free inode
|
|
192 memset(dip, 0, sizeof(*dip));
|
|
193 dip->type = type;
|
|
194 log_write(bp); // mark it allocated on the disk
|
|
195 brelse(bp);
|
|
196 return iget(dev, inum);
|
|
197 }
|
|
198
|
|
199 brelse(bp);
|
|
200 }
|
|
201
|
|
202 panic("ialloc: no inodes");
|
|
203 }
|
|
204
|
|
205 // Copy a modified in-memory inode to disk.
|
|
206 void iupdate (struct inode *ip)
|
|
207 {
|
|
208 struct buf *bp;
|
|
209 struct dinode *dip;
|
|
210
|
|
211 bp = bread(ip->dev, IBLOCK(ip->inum));
|
|
212
|
|
213 dip = (struct dinode*) bp->data + ip->inum % IPB;
|
|
214 dip->type = ip->type;
|
|
215 dip->major = ip->major;
|
|
216 dip->minor = ip->minor;
|
|
217 dip->nlink = ip->nlink;
|
|
218 dip->size = ip->size;
|
|
219
|
|
220 memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
|
|
221 log_write(bp);
|
|
222 brelse(bp);
|
|
223 }
|
|
224
|
|
225 // Find the inode with number inum on device dev
|
|
226 // and return the in-memory copy. Does not lock
|
|
227 // the inode and does not read it from disk.
|
|
228 static struct inode* iget (uint dev, uint inum)
|
|
229 {
|
|
230 struct inode *ip, *empty;
|
|
231
|
|
232 acquire(&icache.lock);
|
|
233
|
|
234 // Is the inode already cached?
|
|
235 empty = 0;
|
|
236
|
|
237 for (ip = &icache.inode[0]; ip < &icache.inode[NINODE]; ip++) {
|
|
238 if (ip->ref > 0 && ip->dev == dev && ip->inum == inum) {
|
|
239 ip->ref++;
|
|
240 release(&icache.lock);
|
|
241 return ip;
|
|
242 }
|
|
243
|
|
244 if (empty == 0 && ip->ref == 0) { // Remember empty slot.
|
|
245 empty = ip;
|
|
246 }
|
|
247 }
|
|
248
|
|
249 // Recycle an inode cache entry.
|
|
250 if (empty == 0) {
|
|
251 panic("iget: no inodes");
|
|
252 }
|
|
253
|
|
254 ip = empty;
|
|
255 ip->dev = dev;
|
|
256 ip->inum = inum;
|
|
257 ip->ref = 1;
|
|
258 ip->flags = 0;
|
|
259 release(&icache.lock);
|
|
260
|
|
261 return ip;
|
|
262 }
|
|
263
|
|
264 // Increment reference count for ip.
|
|
265 // Returns ip to enable ip = idup(ip1) idiom.
|
|
266 struct inode* idup (struct inode *ip)
|
|
267 {
|
|
268 acquire(&icache.lock);
|
|
269 ip->ref++;
|
|
270 release(&icache.lock);
|
|
271 return ip;
|
|
272 }
|
|
273
|
|
274 // Lock the given inode.
|
|
275 // Reads the inode from disk if necessary.
|
|
276 void ilock (struct inode *ip)
|
|
277 {
|
|
278 struct buf *bp;
|
|
279 struct dinode *dip;
|
|
280
|
|
281 if (ip == 0 || ip->ref < 1) {
|
|
282 panic("ilock");
|
|
283 }
|
|
284
|
|
285 acquire(&icache.lock);
|
|
286 while (ip->flags & I_BUSY) {
|
|
287 sleep(ip, &icache.lock);
|
|
288 }
|
|
289
|
|
290 ip->flags |= I_BUSY;
|
|
291 release(&icache.lock);
|
|
292
|
|
293 if (!(ip->flags & I_VALID)) {
|
|
294 bp = bread(ip->dev, IBLOCK(ip->inum));
|
|
295
|
|
296 dip = (struct dinode*) bp->data + ip->inum % IPB;
|
|
297 ip->type = dip->type;
|
|
298 ip->major = dip->major;
|
|
299 ip->minor = dip->minor;
|
|
300 ip->nlink = dip->nlink;
|
|
301 ip->size = dip->size;
|
|
302
|
|
303 memmove(ip->addrs, dip->addrs, sizeof(ip->addrs));
|
|
304 brelse(bp);
|
|
305 ip->flags |= I_VALID;
|
|
306
|
|
307 if (ip->type == 0) {
|
|
308 panic("ilock: no type");
|
|
309 }
|
|
310 }
|
|
311 }
|
|
312
|
|
313 // Unlock the given inode.
|
|
314 void iunlock (struct inode *ip)
|
|
315 {
|
|
316 if (ip == 0 || !(ip->flags & I_BUSY) || ip->ref < 1) {
|
|
317 panic("iunlock");
|
|
318 }
|
|
319
|
|
320 acquire(&icache.lock);
|
|
321 ip->flags &= ~I_BUSY;
|
|
322 wakeup(ip);
|
|
323 release(&icache.lock);
|
|
324 }
|
|
325
|
|
326 // Drop a reference to an in-memory inode.
|
|
327 // If that was the last reference, the inode cache entry can
|
|
328 // be recycled.
|
|
329 // If that was the last reference and the inode has no links
|
|
330 // to it, free the inode (and its content) on disk.
|
|
331 void iput (struct inode *ip)
|
|
332 {
|
|
333 acquire(&icache.lock);
|
|
334
|
|
335 if (ip->ref == 1 && (ip->flags & I_VALID) && ip->nlink == 0) {
|
|
336 // inode has no links: truncate and free inode.
|
|
337 if (ip->flags & I_BUSY) {
|
|
338 panic("iput busy");
|
|
339 }
|
|
340
|
|
341 ip->flags |= I_BUSY;
|
|
342 release(&icache.lock);
|
|
343 itrunc(ip);
|
|
344 ip->type = 0;
|
|
345 iupdate(ip);
|
|
346
|
|
347 acquire(&icache.lock);
|
|
348 ip->flags = 0;
|
|
349 wakeup(ip);
|
|
350 }
|
|
351
|
|
352 ip->ref--;
|
|
353 release(&icache.lock);
|
|
354 }
|
|
355
|
|
356 // Common idiom: unlock, then put.
|
|
357 void iunlockput (struct inode *ip)
|
|
358 {
|
|
359 iunlock(ip);
|
|
360 iput(ip);
|
|
361 }
|
|
362
|
|
363 //PAGEBREAK!
|
|
364 // Inode content
|
|
365 //
|
|
366 // The content (data) associated with each inode is stored
|
|
367 // in blocks on the disk. The first NDIRECT block numbers
|
|
368 // are listed in ip->addrs[]. The next NINDIRECT blocks are
|
|
369 // listed in block ip->addrs[NDIRECT].
|
|
370
|
|
371 // Return the disk block address of the nth block in inode ip.
|
|
372 // If there is no such block, bmap allocates one.
|
|
373 static uint bmap (struct inode *ip, uint bn)
|
|
374 {
|
|
375 uint addr, *a;
|
|
376 struct buf *bp;
|
|
377
|
|
378 if (bn < NDIRECT) {
|
|
379 if ((addr = ip->addrs[bn]) == 0) {
|
|
380 ip->addrs[bn] = addr = balloc(ip->dev);
|
|
381 }
|
|
382
|
|
383 return addr;
|
|
384 }
|
|
385
|
|
386 bn -= NDIRECT;
|
|
387
|
|
388 if (bn < NINDIRECT) {
|
|
389 // Load indirect block, allocating if necessary.
|
|
390 if ((addr = ip->addrs[NDIRECT]) == 0) {
|
|
391 ip->addrs[NDIRECT] = addr = balloc(ip->dev);
|
|
392 }
|
|
393
|
|
394 bp = bread(ip->dev, addr);
|
|
395 a = (uint*) bp->data;
|
|
396
|
|
397 if ((addr = a[bn]) == 0) {
|
|
398 a[bn] = addr = balloc(ip->dev);
|
|
399 log_write(bp);
|
|
400 }
|
|
401
|
|
402 brelse(bp);
|
|
403 return addr;
|
|
404 }
|
|
405
|
|
406 panic("bmap: out of range");
|
|
407 }
|
|
408
|
|
409 // Truncate inode (discard contents).
|
|
410 // Only called when the inode has no links
|
|
411 // to it (no directory entries referring to it)
|
|
412 // and has no in-memory reference to it (is
|
|
413 // not an open file or current directory).
|
|
414 static void itrunc (struct inode *ip)
|
|
415 {
|
|
416 int i, j;
|
|
417 struct buf *bp;
|
|
418 uint *a;
|
|
419
|
|
420 for (i = 0; i < NDIRECT; i++) {
|
|
421 if (ip->addrs[i]) {
|
|
422 bfree(ip->dev, ip->addrs[i]);
|
|
423 ip->addrs[i] = 0;
|
|
424 }
|
|
425 }
|
|
426
|
|
427 if (ip->addrs[NDIRECT]) {
|
|
428 bp = bread(ip->dev, ip->addrs[NDIRECT]);
|
|
429 a = (uint*) bp->data;
|
|
430
|
|
431 for (j = 0; j < NINDIRECT; j++) {
|
|
432 if (a[j]) {
|
|
433 bfree(ip->dev, a[j]);
|
|
434 }
|
|
435 }
|
|
436
|
|
437 brelse(bp);
|
|
438 bfree(ip->dev, ip->addrs[NDIRECT]);
|
|
439 ip->addrs[NDIRECT] = 0;
|
|
440 }
|
|
441
|
|
442 ip->size = 0;
|
|
443 iupdate(ip);
|
|
444 }
|
|
445
|
|
446 // Copy stat information from inode.
|
|
447 void stati (struct inode *ip, struct stat *st)
|
|
448 {
|
|
449 st->dev = ip->dev;
|
|
450 st->ino = ip->inum;
|
|
451 st->type = ip->type;
|
|
452 st->nlink = ip->nlink;
|
|
453 st->size = ip->size;
|
|
454 }
|
|
455
|
31
|
456 __code cbc_readi (struct inode *ip, char *dst, uint off, uint n, struct file *f, __code (*next)(int ret))
|
24
|
457 {
|
|
458 uint tot, m;
|
|
459 struct buf *bp;
|
|
460
|
|
461 if (ip->type == T_DEV) {
|
27
|
462 if (ip->major < 0 || ip->major >= NDEV || !cbc_devsw[ip->major].read) {
|
24
|
463 goto next(-1);
|
|
464 }
|
|
465
|
31
|
466 goto cbc_devsw[ip->major].read(ip, dst, n, f, next);
|
24
|
467 }
|
|
468
|
|
469 if (off > ip->size || off + n < off) {
|
|
470 goto next(-1);
|
|
471 }
|
|
472
|
|
473 if (off + n > ip->size) {
|
|
474 n = ip->size - off;
|
|
475 }
|
|
476
|
|
477 for (tot = 0; tot < n; tot += m, off += m, dst += m) {
|
|
478 bp = bread(ip->dev, bmap(ip, off / BSIZE));
|
|
479 m = min(n - tot, BSIZE - off%BSIZE);
|
|
480 memmove(dst, bp->data + off % BSIZE, m);
|
|
481 brelse(bp);
|
|
482 }
|
|
483
|
32
|
484 if (n > 0)
|
|
485 f->off += n;
|
|
486 iunlock(f->ip);
|
|
487
|
24
|
488 goto next(n);
|
|
489 }
|
|
490
|
0
|
491 //PAGEBREAK!
|
|
492 // Read data from inode.
|
|
493 int readi (struct inode *ip, char *dst, uint off, uint n)
|
|
494 {
|
|
495 uint tot, m;
|
|
496 struct buf *bp;
|
|
497
|
|
498 if (ip->type == T_DEV) {
|
|
499 if (ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].read) {
|
|
500 return -1;
|
|
501 }
|
|
502
|
|
503 return devsw[ip->major].read(ip, dst, n);
|
|
504 }
|
|
505
|
|
506 if (off > ip->size || off + n < off) {
|
|
507 return -1;
|
|
508 }
|
|
509
|
|
510 if (off + n > ip->size) {
|
|
511 n = ip->size - off;
|
|
512 }
|
|
513
|
|
514 for (tot = 0; tot < n; tot += m, off += m, dst += m) {
|
|
515 bp = bread(ip->dev, bmap(ip, off / BSIZE));
|
|
516 m = min(n - tot, BSIZE - off%BSIZE);
|
|
517 memmove(dst, bp->data + off % BSIZE, m);
|
|
518 brelse(bp);
|
|
519 }
|
|
520
|
|
521 return n;
|
|
522 }
|
|
523
|
|
524 // PAGEBREAK!
|
|
525 // Write data to inode.
|
|
526 int writei (struct inode *ip, char *src, uint off, uint n)
|
|
527 {
|
|
528 uint tot, m;
|
|
529 struct buf *bp;
|
|
530
|
|
531 if (ip->type == T_DEV) {
|
|
532 if (ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].write) {
|
|
533 return -1;
|
|
534 }
|
|
535
|
|
536 return devsw[ip->major].write(ip, src, n);
|
|
537 }
|
|
538
|
|
539 if (off > ip->size || off + n < off) {
|
|
540 return -1;
|
|
541 }
|
|
542
|
|
543 if (off + n > MAXFILE * BSIZE) {
|
|
544 return -1;
|
|
545 }
|
|
546
|
|
547 for (tot = 0; tot < n; tot += m, off += m, src += m) {
|
|
548 bp = bread(ip->dev, bmap(ip, off / BSIZE));
|
|
549 m = min(n - tot, BSIZE - off%BSIZE);
|
|
550 memmove(bp->data + off % BSIZE, src, m);
|
|
551 log_write(bp);
|
|
552 brelse(bp);
|
|
553 }
|
|
554
|
|
555 if (n > 0 && off > ip->size) {
|
|
556 ip->size = off;
|
|
557 iupdate(ip);
|
|
558 }
|
|
559
|
|
560 return n;
|
|
561 }
|
|
562
|
|
563 //PAGEBREAK!
|
|
564 // Directories
|
|
565
|
|
566 int namecmp (const char *s, const char *t)
|
|
567 {
|
|
568 return strncmp(s, t, DIRSIZ);
|
|
569 }
|
|
570
|
|
571 // Look for a directory entry in a directory.
|
|
572 // If found, set *poff to byte offset of entry.
|
|
573 struct inode* dirlookup (struct inode *dp, char *name, uint *poff)
|
|
574 {
|
|
575 uint off, inum;
|
|
576 struct dirent de;
|
|
577
|
|
578 if (dp->type != T_DIR) {
|
|
579 panic("dirlookup not DIR");
|
|
580 }
|
|
581
|
|
582 for (off = 0; off < dp->size; off += sizeof(de)) {
|
|
583 if (readi(dp, (char*) &de, off, sizeof(de)) != sizeof(de)) {
|
|
584 panic("dirlink read");
|
|
585 }
|
|
586
|
|
587 if (de.inum == 0) {
|
|
588 continue;
|
|
589 }
|
|
590
|
|
591 if (namecmp(name, de.name) == 0) {
|
|
592 // entry matches path element
|
|
593 if (poff) {
|
|
594 *poff = off;
|
|
595 }
|
|
596
|
|
597 inum = de.inum;
|
|
598 return iget(dp->dev, inum);
|
|
599 }
|
|
600 }
|
|
601
|
|
602 return 0;
|
|
603 }
|
|
604
|
|
605 // Write a new directory entry (name, inum) into the directory dp.
|
|
606 int dirlink (struct inode *dp, char *name, uint inum)
|
|
607 {
|
|
608 int off;
|
|
609 struct dirent de;
|
|
610 struct inode *ip;
|
|
611
|
|
612 // Check that name is not present.
|
|
613 if ((ip = dirlookup(dp, name, 0)) != 0) {
|
|
614 iput(ip);
|
|
615 return -1;
|
|
616 }
|
|
617
|
|
618 // Look for an empty dirent.
|
|
619 for (off = 0; off < dp->size; off += sizeof(de)) {
|
|
620 if (readi(dp, (char*) &de, off, sizeof(de)) != sizeof(de)) {
|
|
621 panic("dirlink read");
|
|
622 }
|
|
623
|
|
624 if (de.inum == 0) {
|
|
625 break;
|
|
626 }
|
|
627 }
|
|
628
|
|
629 strncpy(de.name, name, DIRSIZ);
|
|
630 de.inum = inum;
|
|
631
|
|
632 if (writei(dp, (char*) &de, off, sizeof(de)) != sizeof(de)) {
|
|
633 panic("dirlink");
|
|
634 }
|
|
635
|
|
636 return 0;
|
|
637 }
|
|
638
|
|
639 //PAGEBREAK!
|
|
640 // Paths
|
|
641
|
|
642 // Copy the next path element from path into name.
|
|
643 // Return a pointer to the element following the copied one.
|
|
644 // The returned path has no leading slashes,
|
|
645 // so the caller can check *path=='\0' to see if the name is the last one.
|
|
646 // If no name to remove, return 0.
|
|
647 //
|
|
648 // Examples:
|
|
649 // skipelem("a/bb/c", name) = "bb/c", setting name = "a"
|
|
650 // skipelem("///a//bb", name) = "bb", setting name = "a"
|
|
651 // skipelem("a", name) = "", setting name = "a"
|
|
652 // skipelem("", name) = skipelem("////", name) = 0
|
|
653 //
|
|
654 static char* skipelem (char *path, char *name)
|
|
655 {
|
|
656 char *s;
|
|
657 int len;
|
|
658
|
|
659 while (*path == '/') {
|
|
660 path++;
|
|
661 }
|
|
662
|
|
663 if (*path == 0) {
|
|
664 return 0;
|
|
665 }
|
|
666
|
|
667 s = path;
|
|
668
|
|
669 while (*path != '/' && *path != 0) {
|
|
670 path++;
|
|
671 }
|
|
672
|
|
673 len = path - s;
|
|
674
|
|
675 if (len >= DIRSIZ) {
|
|
676 memmove(name, s, DIRSIZ);
|
|
677 } else {
|
|
678 memmove(name, s, len);
|
|
679 name[len] = 0;
|
|
680 }
|
|
681
|
|
682 while (*path == '/') {
|
|
683 path++;
|
|
684 }
|
|
685
|
|
686 return path;
|
|
687 }
|
|
688
|
|
689 // Look up and return the inode for a path name.
|
|
690 // If parent != 0, return the inode for the parent and copy the final
|
|
691 // path element into name, which must have room for DIRSIZ bytes.
|
|
692 static struct inode* namex (char *path, int nameiparent, char *name)
|
|
693 {
|
|
694 struct inode *ip, *next;
|
|
695
|
|
696 if (*path == '/') {
|
|
697 ip = iget(ROOTDEV, ROOTINO);
|
|
698 } else {
|
|
699 ip = idup(proc->cwd);
|
|
700 }
|
|
701
|
|
702 while ((path = skipelem(path, name)) != 0) {
|
|
703 ilock(ip);
|
|
704
|
|
705 if (ip->type != T_DIR) {
|
|
706 iunlockput(ip);
|
|
707 return 0;
|
|
708 }
|
|
709
|
|
710 if (nameiparent && *path == '\0') {
|
|
711 // Stop one level early.
|
|
712 iunlock(ip);
|
|
713 return ip;
|
|
714 }
|
|
715
|
|
716 if ((next = dirlookup(ip, name, 0)) == 0) {
|
|
717 iunlockput(ip);
|
|
718 return 0;
|
|
719 }
|
|
720
|
|
721 iunlockput(ip);
|
|
722 ip = next;
|
|
723 }
|
|
724
|
|
725 if (nameiparent) {
|
|
726 iput(ip);
|
|
727 return 0;
|
|
728 }
|
|
729
|
|
730 return ip;
|
|
731 }
|
|
732
|
|
733 struct inode* namei (char *path)
|
|
734 {
|
|
735 char name[DIRSIZ];
|
|
736 return namex(path, 0, name);
|
|
737 }
|
|
738
|
|
739 struct inode* nameiparent (char *path, char *name)
|
|
740 {
|
|
741 return namex(path, 1, name);
|
|
742 }
|