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