xref: /386bsd/usr/src/kernel/dosfs/dosfs_lookup.c (revision a2142627)
1 /*
2  *  Written by Paul Popelka (paulp@uts.amdahl.com)
3  *
4  *  You can do anything you want with this software,
5  *    just don't say you wrote it,
6  *    and don't remove this notice.
7  *
8  *  This software is provided "as is".
9  *
10  *  The author supplies this software to be publicly
11  *  redistributed on the understanding that the author
12  *  is not responsible for the correct functioning of
13  *  this software in any circumstances and is not liable
14  *  for any damages caused by this software.
15  *
16  *  October 1992
17  *
18  * $Id: dosfs_lookup.c,v 1.1 94/10/19 23:46:49 bill Exp $
19  */
20 
21 #include "sys/param.h"
22 #include "sys/file.h"
23 #include "sys/time.h"
24 #include "ucred.h"
25 #include "sys/errno.h"
26 #include "uio.h"
27 #include "namei.h"
28 #include "buf.h"
29 #include "vnode.h"
30 #include "sys/mount.h"
31 
32 #include "bpb.h"
33 #include "direntry.h"
34 #include "denode.h"
35 #include "dosfs_mount.h"
36 #include "fat.h"
37 
38 /*
39  *  When we search a directory the blocks containing directory
40  *  entries are read and examined.  The directory entries
41  *  contain information that would normally be in the inode
42  *  of a unix filesystem.  This means that some of a directory's
43  *  contents may also be in memory resident denodes (sort of
44  *  an inode).  This can cause problems if we are searching
45  *  while some other process is modifying a directory.  To
46  *  prevent one process from accessing incompletely modified
47  *  directory information we depend upon being the soul owner
48  *  of a directory block.  bread/brelse provide this service.
49  *  This being the case, when a process modifies a directory
50  *  it must first acquire the disk block that contains the
51  *  directory entry to be modified.  Then update the disk
52  *  block and the denode, and then write the disk block out
53  *  to disk.  This way disk blocks containing directory
54  *  entries and in memory denode's will be in synch.
55  */
56 int
pcfs_lookup(vdp,ndp,p)57 pcfs_lookup(vdp, ndp, p)
58 	struct vnode *vdp;	/* vnode of directory to search		*/
59 	struct nameidata *ndp;
60 	struct proc *p;
61 {
62 	daddr_t bn;
63 	int flag;
64 	int error;
65 	int lockparent;
66 	int wantparent;
67 	int slotstatus;
68 #define	NONE	0
69 #define	FOUND	1
70 	int slotoffset;
71 	int slotcluster;
72 	int frcn;
73 	u_long cluster;
74 	int rootreloff;
75 	int diroff;
76 	int isadir;		/* ~0 if found direntry is a directory	*/
77 	u_long scn;		/* starting cluster number		*/
78 	struct denode *dp;
79 	struct denode *pdp;
80 	struct denode *tdp;
81 	struct pcfsmount *pmp;
82 	struct buf *bp = 0;
83 	struct direntry *dep;
84 	u_char dosfilename[12];
85 
86 #if defined(PCFSDEBUG)
87 printf("pcfs_lookup(): looking for %s\n", ndp->ni_ptr);
88 #endif /* defined(PCFSDEBUG) */
89 	ndp->ni_dvp = vdp;
90 	ndp->ni_vp  = NULL;
91 	dp = VTODE(vdp);
92 	pmp = dp->de_pmp;
93 	lockparent = ndp->ni_nameiop & LOCKPARENT;
94 	flag = ndp->ni_nameiop & OPMASK;
95 	wantparent = ndp->ni_nameiop & (LOCKPARENT | WANTPARENT);
96 #if defined(PCFSDEBUG)
97 printf("pcfs_lookup(): vdp %08x, dp %08x, Attr %02x\n",
98 	vdp, dp, dp->de_Attributes);
99 #endif /* defined(PCFSDEBUG) */
100 
101 /*
102  *  Be sure vdp is a directory.  Since dos filesystems
103  *  don't have the concept of execute permission anybody
104  *  can search a directory.
105  */
106 	if ((dp->de_Attributes & ATTR_DIRECTORY) == 0)
107 		return ENOTDIR;
108 
109 /*
110  *  See if the component of the pathname we are looking for
111  *  is in the directory cache.  If so then do a few things
112  *  and return.
113  */
114 	if (error = cache_lookup(ndp)) {
115 		int vpid;
116 
117 		if (error == ENOENT)
118 			return error;
119 #ifdef PARANOID
120 		if (vdp == ndp->ni_rdir  &&  ndp->ni_isdotdot)
121 			panic("pcfs_lookup: .. thru root");
122 #endif /* PARANOID */
123 		pdp = dp;
124 		vdp = ndp->ni_vp;
125 		dp  = VTODE(vdp);
126 		vpid = vdp->v_id;
127 		if (pdp == dp) {
128 			VREF(vdp);
129 			error = 0;
130 		} else if (ndp->ni_isdotdot) {
131 			DEUNLOCK(pdp);
132 			error = vget(vdp);
133 			if (!error && lockparent && *ndp->ni_next == '\0')
134 				DELOCK(pdp);
135 		} else {
136 			error = vget(vdp);
137 			if (!lockparent || error || *ndp->ni_next != '\0')
138 				DEUNLOCK(pdp);
139 		}
140 
141 		if (!error) {
142 			if (vpid == vdp->v_id) {
143 #if defined(PCFSDEBUG)
144 printf("pcfs_lookup(): cache hit, vnode %08x, file %s\n", vdp, dp->de_Name);
145 #endif /* defined(PCFSDEBUG) */
146 				return 0;
147 			}
148 			deput(dp);
149 			if (lockparent && pdp != dp && *ndp->ni_next == '\0')
150 				DEUNLOCK(pdp);
151 		}
152 		DELOCK(pdp);
153 		dp = pdp;
154 		vdp = DETOV(dp);
155 		ndp->ni_vp = NULL;
156 	}
157 
158 /*
159  *  If they are going after the . or .. entry in the
160  *  root directory, they won't find it.  DOS filesystems
161  *  don't have them in the root directory.  So, we fake it.
162  *  deget() is in on this scam too.
163  */
164 	if ((vdp->v_flag & VROOT)  &&  ndp->ni_ptr[0] == '.'  &&
165 	    (ndp->ni_namelen == 1  ||
166 	     (ndp->ni_namelen == 2  &&  ndp->ni_ptr[1] == '.'))) {
167 		isadir = ATTR_DIRECTORY;
168 		scn = PCFSROOT;
169 #if defined(PCFSDEBUG)
170 printf("pcfs_lookup(): looking for . or .. in root directory\n");
171 #endif /* defined(PCFSDEBUG) */
172 		cluster == PCFSROOT;
173 		diroff = PCFSROOT_OFS;
174 		goto foundroot;
175 	}
176 
177 /*
178  *  Don't search for free slots unless we are creating
179  *  a filename and we are at the end of the pathname.
180  */
181 	slotstatus = FOUND;
182 	if ((flag == CREATE || flag == RENAME) && *ndp->ni_next == '\0') {
183 		slotstatus = NONE;
184 		slotoffset = -1;
185 	}
186 
187 	unix2dosfn((u_char *)ndp->ni_ptr, dosfilename, ndp->ni_namelen);
188 	dosfilename[11] = 0;
189 #if defined(PCFSDEBUG)
190 printf("pcfs_lookup(): dos version of filename %s, length %d\n",
191 	dosfilename, ndp->ni_namelen);
192 #endif /* defined(PCFSDEBUG) */
193 /*
194  *  Search the directory pointed at by vdp for the
195  *  name pointed at by ndp->ni_ptr.
196  */
197 	tdp = NULL;
198 /*
199  *  The outer loop ranges over the clusters that make
200  *  up the directory.  Note that the root directory is
201  *  different from all other directories.  It has a
202  *  fixed number of blocks that are not part of the
203  *  pool of allocatable clusters.  So, we treat it a
204  *  little differently.
205  *  The root directory starts at "cluster" 0.
206  */
207 	rootreloff = 0;
208 	for (frcn = 0; ; frcn++) {
209 		if (error = pcbmap(dp, frcn, &bn, &cluster)) {
210 			if (error == E2BIG)
211 				break;
212 			return error;
213 		}
214 		if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp))
215 			return error;
216 		for (diroff = 0; diroff < pmp->pm_depclust; diroff++) {
217 			dep = (struct direntry *)bp->b_un.b_addr + diroff;
218 
219 /*
220  *  If the slot is empty and we are still looking for
221  *  an empty then remember this one.  If the slot is
222  *  not empty then check to see if it matches what we
223  *  are looking for.  If the slot has never been filled
224  *  with anything, then the remainder of the directory
225  *  has never been used, so there is no point in searching
226  *  it.
227  */
228 			if (dep->deName[0] == SLOT_EMPTY   ||
229 			    dep->deName[0] == SLOT_DELETED) {
230 				if (slotstatus != FOUND) {
231 					slotstatus  = FOUND;
232 					if (cluster == PCFSROOT)
233 						slotoffset = rootreloff;
234 					else
235 						slotoffset = diroff;
236 					slotcluster = cluster;
237 				}
238 				if (dep->deName[0] == SLOT_EMPTY) {
239 					brelse(bp);
240 					goto notfound;
241 				}
242 			} else {
243 				/* Ignore volume labels (anywhere, not just
244 				 * the root directory). */
245 				if ((dep->deAttributes & ATTR_VOLUME) == 0  &&
246 				    memcmp(dosfilename, dep->deName, 11) == 0) {
247 #if defined(PCFSDEBUG)
248 printf("pcfs_lookup(): match diroff %d, rootreloff %d\n", diroff, rootreloff);
249 #endif /* defined(PCFSDEBUG) */
250 /*
251  *  Remember where this directory entry came from
252  *  for whoever did this lookup.
253  *  If this is the root directory we are interested
254  *  in the offset relative to the beginning of the
255  *  directory (not the beginning of the cluster).
256  */
257 					if (cluster == PCFSROOT)
258 						diroff = rootreloff;
259 					ndp->ni_pcfs.pcfs_offset = diroff;
260 					ndp->ni_pcfs.pcfs_cluster = cluster;
261 					goto found;
262 				}
263 			}
264 			rootreloff++;
265 		}			/* for (diroff = 0; .... */
266 /*
267  *  Release the buffer holding the directory cluster
268  *  just searched.
269  */
270 		brelse(bp);
271 	}			/* for (frcn = 0; ; frcn++) */
272 notfound:;
273 /*
274  *  We hold no disk buffers at this point.
275  */
276 
277 /*
278  *  If we get here we didn't find the entry we were looking
279  *  for.  But that's ok if we are creating or renaming and
280  *  are at the end of the pathname and the directory hasn't
281  *  been removed.
282  */
283 #if defined(PCFSDEBUG)
284 printf("pcfs_lookup(): flag %d, refcnt %d, slotstatus %d\n",
285 	flag, dp->de_refcnt, slotstatus);
286 printf("               slotoffset %d, slotcluster %d\n",
287 	slotoffset, slotcluster);
288 #endif /* defined(PCFSDEBUG) */
289 	if ((flag == CREATE || flag == RENAME)  &&
290 	    *ndp->ni_next == '\0' && dp->de_refcnt != 0) {
291 		if (slotstatus == NONE) {
292 			ndp->ni_pcfs.pcfs_offset  = 0;
293 			ndp->ni_pcfs.pcfs_cluster = 0;
294 			ndp->ni_pcfs.pcfs_count   = 0;
295 		} else {
296 #if defined(PCFSDEBUG)
297 printf("pcfs_lookup(): saving empty slot location\n");
298 #endif /* defined(PCFSDEBUG) */
299 			ndp->ni_pcfs.pcfs_offset  = slotoffset;
300 			ndp->ni_pcfs.pcfs_cluster = slotcluster;
301 			ndp->ni_pcfs.pcfs_count   = 1;
302 		}
303 /*		dp->de_flag |= DEUPD; /* never update dos directories */
304 		ndp->ni_nameiop |= SAVENAME;
305 		if (!lockparent)	/* leave searched dir locked?	*/
306 			DEUNLOCK(dp);
307 	}
308 /*
309  *  Insert name in cache as non-existant if not
310  *  trying to create it.
311  */
312 	if (ndp->ni_makeentry && flag != CREATE)
313 		cache_enter(ndp);
314 	return ENOENT;
315 
316 found:;
317 /*
318  *  NOTE:  We still have the buffer with matched
319  *  directory entry at this point.
320  */
321 	isadir = dep->deAttributes & ATTR_DIRECTORY;
322 	scn = dep->deStartCluster;
323 
324 foundroot:;
325 /*
326  *  If we entered at foundroot, then we are looking
327  *  for the . or .. entry of the filesystems root
328  *  directory.  isadir and scn were setup before
329  *  jumping here.  And, bp is null.  There is no buf header.
330  */
331 
332 /*
333  *  If deleting and at the end of the path, then
334  *  if we matched on "." then don't deget() we would
335  *  probably panic().  Otherwise deget() the directory
336  *  entry.
337  */
338 	if (flag == DELETE && ndp->ni_next == '\0') {
339 		if (dp->de_StartCluster == scn  &&
340 		    isadir) { /* "." */
341 			VREF(vdp);
342 			ndp->ni_vp = vdp;
343 			if (bp) brelse(bp);
344 			return 0;
345 		}
346 		error = deget(pmp, cluster, diroff, dep, &tdp);
347 		if (error) {
348 			if (bp) brelse(bp);
349 			return error;
350 		}
351 		ndp->ni_vp = DETOV(tdp);
352 		if (!lockparent)
353 			DEUNLOCK(dp);
354 		if (bp) brelse(bp);
355 		return 0;
356 	}
357 
358 /*
359  *  If renaming.
360  */
361 	if (flag == RENAME && wantparent && *ndp->ni_next == '\0') {
362 		if (dp->de_StartCluster == scn  &&
363 		    isadir) {
364 			if (bp) brelse(bp);
365 			return EISDIR;
366 		}
367 		error = deget(pmp, cluster, diroff, dep, &tdp);
368 		if (error) {
369 			if (bp) brelse(bp);
370 			return error;
371 		}
372 		ndp->ni_vp = DETOV(tdp);
373 		ndp->ni_nameiop |= SAVENAME;
374 		if (!lockparent)
375 			DEUNLOCK(dp);
376 		if (bp) brelse(bp);
377 		return 0;
378 	}
379 
380 /*
381  *  ?
382  */
383 	pdp = dp;
384 	if (ndp->ni_isdotdot) {
385 		DEUNLOCK(pdp);
386 		error = deget(pmp, cluster, diroff, dep, &tdp);
387 		if (error) {
388 			DELOCK(pdp);
389 			if (bp) brelse(bp);
390 			return error;
391 		}
392 		if (lockparent && *ndp->ni_next == '\0')
393 			DELOCK(pdp);
394 		ndp->ni_vp = DETOV(tdp);
395 	} else if (dp->de_StartCluster == scn  &&
396 		   isadir) { /* "." */
397 		VREF(vdp);
398 		ndp->ni_vp = vdp;
399 	} else {
400 		error = deget(pmp, cluster, diroff, dep, &tdp);
401 		if (error) {
402 			if (bp) brelse(bp);
403 			return error;
404 		}
405 		if (!lockparent || *ndp->ni_next != '\0')
406 			DEUNLOCK(pdp);
407 		ndp->ni_vp = DETOV(tdp);
408 	}
409 	if (bp) brelse(bp);
410 
411 /*
412  *  Insert name in cache if wanted.
413  */
414 	if (ndp->ni_makeentry)
415 		cache_enter(ndp);
416 	return 0;
417 }
418 
419 /*
420  *  dep - directory to copy into the directory
421  *  ndp - nameidata structure containing info on
422  *    where to put the directory entry in the directory.
423  *  depp - return the address of the denode for the
424  *    created directory entry if depp != 0
425  */
426 int
createde(dep,ndp,depp)427 createde(dep, ndp, depp)
428 	struct denode *dep;
429 	struct nameidata *ndp;
430 	struct denode **depp;
431 {
432 	int bn;
433 	int error;
434 	u_long dirclust, diroffset;
435 	struct direntry *ndep;
436 	struct denode *ddep = VTODE(ndp->ni_dvp);	/* directory to add to */
437 	struct pcfsmount *pmp = dep->de_pmp;
438 	struct buf *bp;
439 #if defined(PCFSDEBUG)
440 printf("createde(dep %08x, ndp %08x, depp %08x)\n", dep, ndp, depp);
441 #endif /* defined(PCFSDEBUG) */
442 
443 /*
444  *  If no space left in the directory then allocate
445  *  another cluster and chain it onto the end of the
446  *  file.  There is one exception to this.  That is,
447  *  if the root directory has no more space it can NOT
448  *  be expanded.  extendfile() checks for and fails attempts to
449  *  extend the root directory.  We just return an error
450  *  in that case.
451  */
452 	if (ndp->ni_pcfs.pcfs_count == 0) {
453 		if (error = extendfile(ddep, &bp, &dirclust))
454 			return error;
455 		ndep = (struct direntry *)bp->b_un.b_addr;
456 /*
457  *  Let caller know where we put the directory entry.
458  */
459 		ndp->ni_pcfs.pcfs_cluster = dirclust;
460 		ndp->ni_pcfs.pcfs_offset  = diroffset = 0;
461 	}
462 
463 	else {
464 /*
465  *  There is space in the existing directory.  So,
466  *  we just read in the cluster with space.  Copy
467  *  the new directory entry in.  Then write it to
468  *  disk.
469  *  NOTE:  DOS directories do not get smaller as
470  *  clusters are emptied.
471  */
472 		dirclust = ndp->ni_pcfs.pcfs_cluster;
473 		diroffset = ndp->ni_pcfs.pcfs_offset;
474 
475 		error = readep(pmp, dirclust, diroffset, &bp, &ndep);
476 		if (error)
477 			return error;
478 	}
479 	*ndep = dep->de_de;
480 /*
481  *  If they want us to return with the denode gotten.
482  */
483 	if (depp) {
484 		error = deget(pmp, dirclust, diroffset, ndep, depp);
485 		if (error)
486 			return error;
487 	}
488 	if (error = bwrite(bp))
489 /*deput()?*/
490 		return error;
491 	return 0;
492 }
493 
494 /*
495  *  Read in a directory entry and mark it as being deleted.
496  */
497 int
markdeleted(pmp,dirclust,diroffset)498 markdeleted(pmp, dirclust, diroffset)
499 	struct pcfsmount *pmp;
500 	u_long dirclust;
501 	u_long diroffset;
502 {
503 	int error;
504 	struct direntry *ep;
505 	struct buf *bp;
506 
507 	error = readep(pmp, dirclust, diroffset, &bp, &ep);
508 	if (error)
509 		return error;
510 	ep->deName[0] = SLOT_DELETED;
511 	return bwrite(bp);
512 }
513 
514 /*
515  *  Remove a directory entry.
516  *  At this point the file represented by the directory
517  *  entry to be removed is still full length until no
518  *  one has it open.  When the file no longer being
519  *  used pcfs_inactive() is called and will truncate
520  *  the file to 0 length.  When the vnode containing
521  *  the denode is needed for some other purpose by
522  *  VFS it will call pcfs_reclaim() which will remove
523  *  the denode from the denode cache.
524  */
525 int
removede(ndp)526 removede(ndp)
527 	struct nameidata *ndp;
528 {
529 	struct denode *dep = VTODE(ndp->ni_vp);	/* the file being removed */
530 	struct pcfsmount *pmp = dep->de_pmp;
531 	int error;
532 
533 #if defined(PCFSDEBUG)
534 /*printf("removede(): filename %s\n", dep->de_Name);
535 printf("rmde(): dep %08x, ndpcluster %d, ndpoffset %d\n",
536 	dep, ndp->ni_pcfs.pcfs_cluster, ndp->ni_pcfs.pcfs_offset);*/
537 #endif /* defined(PCFSDEBUG) */
538 
539 /*
540  *  Read the directory block containing the directory
541  *  entry we are to make free.  The nameidata structure
542  *  holds the cluster number and directory entry index
543  *  number of the entry to free.
544  */
545 	error = markdeleted(pmp, ndp->ni_pcfs.pcfs_cluster,
546 		ndp->ni_pcfs.pcfs_offset);
547 
548 	dep->de_refcnt--;
549 	return error;
550 }
551 
552 /*
553  *  Be sure a directory is empty except for "." and "..".
554  *  Return 1 if empty, return 0 if not empty or error.
555  */
556 int
dosdirempty(dep)557 dosdirempty(dep)
558 	struct denode *dep;
559 {
560 	int dei;
561 	int error;
562 	u_long cn;
563 	daddr_t bn;
564 	struct buf *bp;
565 	struct pcfsmount *pmp = dep->de_pmp;
566 	struct direntry *dentp;
567 
568 /*
569  *  Since the filesize field in directory entries for a directory
570  *  is zero, we just have to feel our way through the directory
571  *  until we hit end of file.
572  */
573 	for (cn = 0;; cn++) {
574 		error = pcbmap(dep, cn, &bn, 0);
575 		if (error == E2BIG)
576 			return 1;	/* it's empty */
577 		error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED,
578 			&bp);
579 		if (error)
580 			return error;
581 		dentp = (struct direntry *)bp->b_un.b_addr;
582 		for (dei = 0; dei < pmp->pm_depclust; dei++) {
583 			if (dentp->deName[0] != SLOT_DELETED) {
584 /*
585  *  In dos directories an entry whose name starts with SLOT_EMPTY (0)
586  *  starts the beginning of the unused part of the directory, so we
587  *  can just return that it is empty.
588  */
589 				if (dentp->deName[0] == SLOT_EMPTY) {
590 					brelse(bp);
591 					return 1;
592 				}
593 /*
594  *  Any names other than "." and ".." in a directory mean
595  *  it is not empty.
596  */
597 				if (memcmp(dentp->deName, ".          ", 11)  &&
598 				    memcmp(dentp->deName, "..         ", 11)) {
599 					brelse(bp);
600 #if defined(PCFSDEBUG)
601 printf("dosdirempty(): entry %d found %02x, %02x\n", dei, dentp->deName[0],
602 	dentp->deName[1]);
603 #endif /* defined(PCFSDEBUG) */
604 					return 0;	/* not empty */
605 				}
606 			}
607 			dentp++;
608 		}
609 		brelse(bp);
610 	}
611 	/*NOTREACHED*/
612 }
613 
614 /*
615  *  Check to see if the directory described by target is
616  *  in some subdirectory of source.  This prevents something
617  *  like the following from succeeding and leaving a bunch
618  *  or files and directories orphaned.
619  *	mv /a/b/c /a/b/c/d/e/f
620  *  Where c and f are directories.
621  *  source - the inode for /a/b/c
622  *  target - the inode for /a/b/c/d/e/f
623  *  Returns 0 if target is NOT a subdirectory of source.
624  *  Otherwise returns a non-zero error number.
625  *  The target inode is always unlocked on return.
626  */
627 int
doscheckpath(source,target)628 doscheckpath(source, target)
629 	struct denode *source;
630 	struct denode *target;
631 {
632 	daddr_t scn;
633 	struct denode dummy;
634 	struct pcfsmount *pmp;
635 	struct direntry *ep;
636 	struct denode *dep;
637 	struct buf *bp = NULL;
638 	int error = 0;
639 
640 	dep = target;
641 	if ((target->de_Attributes & ATTR_DIRECTORY) == 0  ||
642 	    (source->de_Attributes & ATTR_DIRECTORY) == 0) {
643 		error = ENOTDIR;
644 		goto out;
645 	}
646 	if (dep->de_StartCluster == source->de_StartCluster) {
647 		error = EEXIST;
648 		goto out;
649 	}
650 	if (dep->de_StartCluster == PCFSROOT)
651 		goto out;
652 	for (;;) {
653 		if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
654 			error = ENOTDIR;
655 			goto out;
656 		}
657 		pmp = dep->de_pmp;
658 		scn = dep->de_StartCluster;
659 		error = bread(pmp->pm_devvp, cntobn(pmp, scn),
660 			pmp->pm_bpcluster, NOCRED, &bp);
661 		if (error) {
662 			break;
663 		}
664 		ep = (struct direntry *)bp->b_un.b_addr + 1;
665 		if ((ep->deAttributes & ATTR_DIRECTORY) == 0  ||
666 		    memcmp(ep->deName, "..         ", 11) != 0) {
667 			error = ENOTDIR;
668 			break;
669 		}
670 		if (ep->deStartCluster == source->de_StartCluster) {
671 			error = EINVAL;
672 			break;
673 		}
674 		if (ep->deStartCluster == PCFSROOT)
675 			break;
676 		deput(dep);
677 		/* NOTE: deget() clears dep on error */
678 		error = deget(pmp, ep->deStartCluster, 0, ep, &dep);
679 		brelse(bp);
680 		bp = NULL;
681 		if (error)
682 			break;
683 	}
684 out:;
685 	if (bp)
686 		brelse(bp);
687 	if (error == ENOTDIR)
688 		printf("doscheckpath(): .. not a directory?\n");
689 	if (dep != NULL)
690 		deput(dep);
691 	return error;
692 }
693 
694 /*
695  *  Read in the disk block containing the directory entry
696  *  (dirclu, dirofs) and return the address of the buf header,
697  *  and the address of the directory entry within the block.
698  */
699 int
readep(pmp,dirclu,dirofs,bpp,epp)700 readep(pmp, dirclu, dirofs, bpp, epp)
701 	struct pcfsmount *pmp;
702 	u_long dirclu, dirofs;
703 	struct buf **bpp;
704 	struct direntry **epp;
705 {
706 	int error;
707 	daddr_t bn;
708 
709 	bn = detobn(pmp, dirclu, dirofs);
710 	if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, bpp)) {
711 		*bpp = NULL;
712 		return error;
713 	}
714 	if (epp)
715 		*epp = bptoep(pmp, *bpp, dirofs);
716 	return 0;
717 }
718 
719 
720 /*
721  *  Read in the disk block containing the directory entry
722  *  dep came from and return the address of the buf header,
723  *  and the address of the directory entry within the block.
724  */
725 int
readde(dep,bpp,epp)726 readde(dep, bpp, epp)
727 	struct denode *dep;
728 	struct buf **bpp;
729 	struct direntry **epp;
730 {
731 	return readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,
732 		      bpp, epp);
733 }
734