|
|
1.1 root 1: /*
2: * Written by Paul Popelka ([email protected])
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: * pcfs_denode.c,v 1.4.2.1 1993/08/05 02:37:20 cgd Exp
19: */
20:
21: #include "param.h"
22: #include "systm.h"
23: #include "mount.h"
24: #include "proc.h"
25: #include "buf.h"
26: #include "vnode.h"
27: #include "kernel.h" /* defines "time" */
28:
29: #include "bpb.h"
30: #include "pcfsmount.h"
31: #include "direntry.h"
32: #include "denode.h"
33: #include "fat.h"
34:
35: #define DEHSZ 512
36: #if ((DEHSZ & (DEHSZ-1)) == 0)
37: #define DEHASH(dev, deno) (((dev)+(deno)+((deno)>>16))&(DEHSZ-1))
38: #else
39: #define DEHASH(dev, deno) (((dev)+(deno)+((deno)>>16))%DEHSZ)
40: #endif /* ((DEHSZ & (DEHSZ-1)) == 0) */
41:
42: union dehead {
43: union dehead *deh_head[2];
44: struct denode *deh_chain[2];
45: } dehead[DEHSZ];
46:
47: pcfs_init()
48: {
49: int i;
50: union dehead *deh;
51:
52: if (VN_MAXPRIVATE < sizeof(struct denode))
53: panic("pcfs_init: vnode too small");
54:
55: for (i = DEHSZ, deh = dehead; --i >= 0; deh++) {
56: deh->deh_head[0] = deh;
57: deh->deh_head[1] = deh;
58: }
59: }
60:
61: /*
62: * If deget() succeeds it returns with the gotten denode
63: * locked().
64: * pmp - address of pcfsmount structure of the filesystem
65: * containing the denode of interest. The pm_dev field
66: * and the address of the pcfsmount structure are used.
67: * dirclust - which cluster bp contains, if dirclust is 0
68: * (root directory) diroffset is relative to the beginning
69: * of the root directory, otherwise it is cluster relative.
70: * diroffset - offset past begin of cluster of denode we
71: * want
72: * direntptr - address of the direntry structure of interest.
73: * direntptr is NULL, the block is read if necessary.
74: * depp - returns the address of the gotten denode.
75: */
76: int
77: deget (pmp, dirclust, diroffset, direntptr, depp)
78: struct pcfsmount *pmp; /* so we know the maj/min number */
79: u_long dirclust; /* cluster this dir entry came from */
80: u_long diroffset; /* index of entry within the cluster */
81: struct direntry *direntptr;
82: struct denode **depp; /* returns the addr of the gotten denode*/
83: {
84: int error;
85: dev_t dev = pmp->pm_dev;
86: union dehead *deh;
87: struct mount *mntp = pmp->pm_mountp;
88: extern struct vnodeops pcfs_vnodeops;
89: struct denode *ldep;
90: struct vnode *nvp;
91: struct buf *bp;
92: #if defined(PCFSDEBUG)
93: printf("deget(pmp %08x, dirclust %d, diroffset %x, direntptr %x, depp %08x)\n",
94: pmp, dirclust, diroffset, direntptr, depp);
95: #endif /* defined(PCFSDEBUG) */
96:
97: /* If dir entry is given and refers to a directory, convert to
98: * canonical form
99: */
100: if (direntptr && (direntptr->deAttributes & ATTR_DIRECTORY)) {
101: dirclust = direntptr->deStartCluster;
102: if (dirclust == PCFSROOT)
103: diroffset = PCFSROOT_OFS;
104: else
105: diroffset = 0;
106: }
107:
108: /*
109: * See if the denode is in the denode cache. Use the location of
110: * the directory entry to compute the hash value.
111: * For subdir use address of "." entry.
112: * for root dir use cluster PCFSROOT, offset PCFSROOT_OFS
113: *
114: * NOTE: The check for de_refcnt > 0 below insures the denode
115: * being examined does not represent an unlinked but
116: * still open file. These files are not to be accessible
117: * even when the directory entry that represented the
118: * file happens to be reused while the deleted file is still
119: * open.
120: */
121: deh = &dehead[DEHASH(dev, dirclust + diroffset)];
122: loop:
123: for (ldep = deh->deh_chain[0]; ldep != (struct denode *)deh;
124: ldep = ldep->de_forw) {
125: if (dev != ldep->de_dev || ldep->de_refcnt == 0)
126: continue;
127: if (dirclust != ldep->de_dirclust
128: || diroffset != ldep->de_diroffset)
129: continue;
130: if (ldep->de_flag & DELOCKED) {
131: /* should we brelse() the passed buf hdr to
132: * avoid some potential deadlock? */
133: ldep->de_flag |= DEWANT;
134: sleep((caddr_t)ldep, PINOD);
135: goto loop;
136: }
137: if (vget(DETOV(ldep)))
138: goto loop;
139: *depp = ldep;
140: return 0;
141: }
142:
143:
144: /*
145: * Directory entry was not in cache, have to create
146: * a vnode and copy it from the passed disk buffer.
147: */
148: /* getnewvnode() does a VREF() on the vnode */
149: if (error = getnewvnode(VT_MSDOSFS, mntp, &pcfs_vnodeops, &nvp)) {
150: *depp = 0;
151: return error;
152: }
153: ldep = VTODE(nvp);
154: ldep->de_vnode = nvp;
155: ldep->de_flag = 0;
156: ldep->de_devvp = 0;
157: ldep->de_lockf = 0;
158: ldep->de_dev = dev;
159: fc_purge(ldep, 0); /* init the fat cache for this denode */
160:
161: /*
162: * Insert the denode into the hash queue and lock the
163: * denode so it can't be accessed until we've read it
164: * in and have done what we need to it.
165: */
166: insque(ldep, deh);
167: DELOCK(ldep);
168:
169: /*
170: * Copy the directory entry into the denode area of the
171: * vnode.
172: */
173: if (dirclust == PCFSROOT && diroffset == PCFSROOT_OFS) {
174: /* Directory entry for the root directory.
175: * There isn't one, so we manufacture one.
176: * We should probably rummage through the root directory and
177: * find a label entry (if it exists), and then use the time
178: * and date from that entry as the time and date for the
179: * root denode.
180: */
181: ldep->de_Attributes = ATTR_DIRECTORY;
182: ldep->de_StartCluster = PCFSROOT;
183: ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec;
184: /* fill in time and date so that dos2unixtime() doesn't
185: * spit up when called from pcfs_getattr() with root denode */
186: ldep->de_Time = 0x0000; /* 00:00:00 */
187: ldep->de_Date = (0 << 9) | (1 << 5) | (1 << 0);
188: /* Jan 1, 1980 */
189: /* leave the other fields as garbage */
190: }
191: else {
192: bp = NULL;
193: if (!direntptr) {
194: error = readep(pmp, dirclust, diroffset, &bp,
195: &direntptr);
196: if (error)
197: return error;
198: }
199: ldep->de_de = *direntptr;
200: if (bp)
201: brelse (bp);
202: }
203:
204: /*
205: * Fill in a few fields of the vnode and finish filling
206: * in the denode. Then return the address of the found
207: * denode.
208: */
209: ldep->de_pmp = pmp;
210: ldep->de_devvp = pmp->pm_devvp;
211: ldep->de_refcnt = 1;
212: ldep->de_dirclust = dirclust;
213: ldep->de_diroffset = diroffset;
214: if (ldep->de_Attributes & ATTR_DIRECTORY) {
215: /*
216: * Since DOS directory entries that describe directories
217: * have 0 in the filesize field, we take this opportunity
218: * to find out the length of the directory and plug it
219: * into the denode structure.
220: */
221: u_long size;
222:
223: nvp->v_type = VDIR;
224: if (ldep->de_StartCluster == PCFSROOT)
225: nvp->v_flag |= VROOT;
226: else {
227: error = pcbmap(ldep, 0xffff, 0, &size);
228: if (error == E2BIG) {
229: ldep->de_FileSize = size << pmp->pm_cnshift;
230: error = 0;
231: }
232: else
233: printf("deget(): pcbmap returned %d\n", error);
234: }
235: }
236: else
237: nvp->v_type = VREG;
238: VREF(ldep->de_devvp);
239: *depp = ldep;
240: return 0;
241: }
242:
243: void
244: deput(dep)
245: struct denode *dep;
246: {
247: if ((dep->de_flag & DELOCKED) == 0)
248: panic("deput: denode not locked");
249: DEUNLOCK(dep);
250: vrele(DETOV(dep));
251: }
252:
253: int
254: deupdat(dep, tp, waitfor)
255: struct denode *dep;
256: struct timeval *tp;
257: int waitfor;
258: {
259: int error;
260: daddr_t bn;
261: int diro;
262: struct buf *bp;
263: struct direntry *dirp;
264: struct pcfsmount *pmp = dep->de_pmp;
265: struct vnode *vp = DETOV(dep);
266: #if defined(PCFSDEBUG)
267: printf("deupdat(): dep %08x\n", dep);
268: #endif /* defined(PCFSDEBUG) */
269:
270: /*
271: * If the update bit is off, or this denode is from
272: * a readonly filesystem, or this denode is for a
273: * directory, or the denode represents an open but
274: * unlinked file then don't do anything. DOS directory
275: * entries that describe a directory do not ever
276: * get updated. This is the way dos treats them.
277: */
278: if ((dep->de_flag & DEUPD) == 0 ||
279: vp->v_mount->mnt_flag & MNT_RDONLY ||
280: dep->de_Attributes & ATTR_DIRECTORY ||
281: dep->de_refcnt <= 0)
282: return 0;
283:
284: /*
285: * Read in the cluster containing the directory entry
286: * we want to update.
287: */
288: if (error = readde(dep, &bp, &dirp))
289: return error;
290:
291: /*
292: * Put the passed in time into the directory entry.
293: */
294: unix2dostime(&time, (union dosdate *)&dep->de_Date,
295: (union dostime *)&dep->de_Time);
296: dep->de_flag &= ~DEUPD;
297:
298: /*
299: * Copy the directory entry out of the denode into
300: * the cluster it came from.
301: */
302: *dirp = dep->de_de; /* structure copy */
303:
304: /*
305: * Write the cluster back to disk. If they asked
306: * for us to wait for the write to complete, then
307: * use bwrite() otherwise use bdwrite().
308: */
309: error = 0; /* note that error is 0 from above, but ... */
310: if (waitfor)
311: error = bwrite(bp);
312: else
313: bdwrite(bp);
314: return error;
315: }
316:
317: /*
318: * Truncate the file described by dep to the length
319: * specified by length.
320: */
321: int
322: detrunc(dep, length, flags)
323: struct denode *dep;
324: u_long length;
325: int flags;
326: {
327: int error;
328: int allerror;
329: u_long eofentry;
330: u_long chaintofree;
331: daddr_t bn;
332: int boff;
333: int isadir = dep->de_Attributes & ATTR_DIRECTORY;
334: struct buf *bp;
335: struct pcfsmount *pmp = dep->de_pmp;
336: #if defined(PCFSDEBUG)
337: printf("detrunc(): file %s, length %d, flags %d\n", dep->de_Name, length, flags);
338: #endif /* defined(PCFSDEBUG) */
339:
340: /*
341: * Disallow attempts to truncate the root directory
342: * since it is of fixed size. That's just the way
343: * dos filesystems are. We use the VROOT bit in the
344: * vnode because checking for the directory bit and
345: * a startcluster of 0 in the denode is not adequate
346: * to recognize the root directory at this point in
347: * a file or directory's life.
348: */
349: if (DETOV(dep)->v_flag & VROOT) {
350: printf("detrunc(): can't truncate root directory, clust %d, offset %d\n",
351: dep->de_dirclust, dep->de_diroffset);
352: return EINVAL;
353: }
354:
355: vnode_pager_setsize(DETOV(dep), length);
356:
357: if (dep->de_FileSize <= length) {
358: dep->de_flag |= DEUPD;
359: error = deupdat(dep, &time, 1);
360: #if defined(PCFSDEBUG)
361: printf("detrunc(): file is shorter return point, errno %d\n", error);
362: #endif /* defined(PCFSDEBUG) */
363: return error;
364: }
365:
366: /*
367: * If the desired length is 0 then remember the starting
368: * cluster of the file and set the StartCluster field in
369: * the directory entry to 0. If the desired length is
370: * not zero, then get the number of the last cluster in
371: * the shortened file. Then get the number of the first
372: * cluster in the part of the file that is to be freed.
373: * Then set the next cluster pointer in the last cluster
374: * of the file to CLUST_EOFE.
375: */
376: if (length == 0) {
377: chaintofree = dep->de_StartCluster;
378: dep->de_StartCluster = 0;
379: eofentry = ~0;
380: } else {
381: error = pcbmap(dep, (length-1) >> pmp->pm_cnshift,
382: 0, &eofentry);
383: if (error) {
384: #if defined(PCFSDEBUG)
385: printf("detrunc(): pcbmap fails %d\n", error);
386: #endif /* defined(PCFSDEBUG) */
387: return error;
388: }
389: }
390:
391: fc_purge(dep, (length + pmp->pm_crbomask) >> pmp->pm_cnshift);
392:
393: /*
394: * If the new length is not a multiple of the cluster size
395: * then we must zero the tail end of the new last cluster in case
396: * it becomes part of the file again because of a seek.
397: */
398: if ((boff = length & pmp->pm_crbomask) != 0) {
399: /* should read from file vnode or
400: * filesystem vnode depending on if file or dir */
401: if (isadir) {
402: bn = cntobn(pmp, eofentry);
403: error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
404: NOCRED, &bp);
405: } else {
406: bn = (length-1) >> pmp->pm_cnshift;
407: error = bread(DETOV(dep), bn, pmp->pm_bpcluster,
408: NOCRED, &bp);
409: }
410: if (error) {
411: brelse(bp);
412: return error;
413: }
414: vnode_pager_uncache(DETOV(dep)); /* what's this for? */
415: /* is this the right
416: * place for it? */
417: bzero(bp->b_un.b_addr + boff, pmp->pm_bpcluster - boff);
418: if (flags & IO_SYNC)
419: bwrite(bp);
420: else
421: bdwrite(bp);
422: }
423:
424: /*
425: * Write out the updated directory entry. Even
426: * if the update fails we free the trailing clusters.
427: */
428: dep->de_FileSize = length;
429: dep->de_flag |= DEUPD;
430: vinvalbuf(DETOV(dep), length > 0);
431: allerror = deupdat(dep, &time, MNT_WAIT);
432: #if defined(PCFSDEBUG)
433: printf("detrunc(): allerror %d, eofentry %d\n",
434: allerror, eofentry);
435: #endif /* defined(PCFSDEBUG) */
436:
437: /*
438: * If we need to break the cluster chain for the file
439: * then do it now.
440: */
441: if (eofentry != ~0) {
442: error = fatentry(FAT_GET_AND_SET, pmp, eofentry,
443: &chaintofree, CLUST_EOFE);
444: if (error) {
445: #if defined(PCFSDEBUG)
446: printf("detrunc(): fatentry errors %d\n", error);
447: #endif /* defined(PCFSDEBUG) */
448: return error;
449: }
450: fc_setcache(dep, FC_LASTFC, (length - 1) >> pmp->pm_cnshift,
451: eofentry);
452: }
453:
454: /*
455: * Now free the clusters removed from the file because
456: * of the truncation.
457: */
458: if (chaintofree != 0 && !PCFSEOF(chaintofree))
459: freeclusterchain(pmp, chaintofree);
460:
461: return allerror;
462: }
463:
464: /*
465: * Move a denode to its correct hash queue after
466: * the file it represents has been moved to a new
467: * directory.
468: */
469: reinsert(dep)
470: struct denode *dep;
471: {
472: struct pcfsmount *pmp = dep->de_pmp;
473: union dehead *deh;
474:
475: /*
476: * Fix up the denode cache. If the denode is
477: * for a directory, there is nothing to do since the
478: * hash is based on the starting cluster of the directory
479: * file and that hasn't changed. If for a file the hash
480: * is based on the location
481: * of the directory entry, so we must remove it from the
482: * cache and re-enter it with the hash based on the new
483: * location of the directory entry.
484: */
485: if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
486: remque(dep);
487: deh = &dehead[DEHASH(pmp->pm_dev,
488: dep->de_dirclust + dep->de_diroffset)];
489: insque(dep, deh);
490: }
491: }
492:
493: int pcfs_prtactive; /* print reclaims of active vnodes */
494:
495: int
496: pcfs_reclaim(vp)
497: struct vnode *vp;
498: {
499: struct denode *dep = VTODE(vp);
500: int i;
501: #if defined(PCFSDEBUG)
502: printf("pcfs_reclaim(): dep %08x, file %s, refcnt %d\n",
503: dep, dep->de_Name, dep->de_refcnt);
504: #endif /* defined(PCFSDEBUG) */
505:
506: if (pcfs_prtactive && vp->v_usecount != 0)
507: vprint("pcfs_reclaim(): pushing active", vp);
508:
509: /*
510: * Remove the denode from the denode hash chain we
511: * are in.
512: */
513: remque(dep);
514: dep->de_forw = dep;
515: dep->de_back = dep;
516:
517: cache_purge(vp);
518: /*
519: * Indicate that one less file on the filesystem is open.
520: */
521: if (dep->de_devvp) {
522: vrele(dep->de_devvp);
523: dep->de_devvp = 0;
524: }
525:
526: dep->de_flag = 0;
527: return 0;
528: }
529:
530: int
531: pcfs_inactive(vp, p)
532: struct vnode *vp;
533: struct proc *p;
534: {
535: struct denode *dep = VTODE(vp);
536: int error = 0;
537: #if defined(PCFSDEBUG)
538: printf("pcfs_inactive(): dep %08x, de_Name[0] %x\n", dep, dep->de_Name[0]);
539: #endif /* defined(PCFSDEBUG) */
540:
541: if (pcfs_prtactive && vp->v_usecount != 0)
542: vprint("pcfs_inactive(): pushing active", vp);
543:
544: /*
545: * Get rid of denodes related to stale file handles.
546: * Hmmm, what does this really do?
547: */
548: if (dep->de_Name[0] == SLOT_DELETED) {
549: if ((vp->v_flag & VXLOCK) == 0)
550: vgone(vp);
551: return 0;
552: }
553:
554: /*
555: * If the file has been deleted and it is on a read/write
556: * filesystem, then truncate the file, and mark the directory
557: * slot as empty. (This may not be necessary for the dos
558: * filesystem.
559: */
560: #if defined(PCFSDEBUG)
561: printf("pcfs_inactive(): dep %08x, refcnt %d, mntflag %x, MNT_RDONLY %x\n",
562: dep, dep->de_refcnt, vp->v_mount->mnt_flag, MNT_RDONLY);
563: #endif /* defined(PCFSDEBUG) */
564: DELOCK(dep);
565: if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
566: error = detrunc(dep, (u_long)0, 0);
567: dep->de_flag |= DEUPD;
568: dep->de_Name[0] = SLOT_DELETED;
569: }
570: DEUPDAT(dep, &time, 0);
571: DEUNLOCK(dep);
572: dep->de_flag = 0;
573:
574: /*
575: * If we are done with the denode, then reclaim
576: * it so that it can be reused now.
577: */
578: #if defined(PCFSDEBUG)
579: printf("pcfs_inactive(): v_usecount %d, de_Name[0] %x\n", vp->v_usecount,
580: dep->de_Name[0]);
581: #endif /* defined(PCFSDEBUG) */
582: if (vp->v_usecount == 0 && dep->de_Name[0] == SLOT_DELETED)
583: vgone(vp);
584: return error;
585: }
586:
587: int
588: delock(dep)
589: struct denode *dep;
590: {
591: while (dep->de_flag & DELOCKED) {
592: dep->de_flag |= DEWANT;
593: if (dep->de_spare0 == curproc->p_pid)
594: panic("delock: locking against myself");
595: dep->de_spare1 = curproc->p_pid;
596: (void) sleep((caddr_t)dep, PINOD);
597: }
598: dep->de_spare1 = 0;
599: dep->de_spare0 = curproc->p_pid;
600: dep->de_flag |= DELOCKED;
601:
602: return 0;
603: }
604:
605: int
606: deunlock(dep)
607: struct denode *dep;
608: {
609: if ((dep->de_flag & DELOCKED) == 0)
610: vprint("deunlock: found unlocked denode", DETOV(dep));
611: dep->de_spare0 = 0;
612: dep->de_flag &= ~DELOCKED;
613: if (dep->de_flag & DEWANT) {
614: dep->de_flag &= ~DEWANT;
615: wakeup((caddr_t)dep);
616: }
617:
618: return 0;
619: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.