|
|
1.1 root 1: /*
2: * Copyright (c) 1999-2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * The contents of this file constitute Original Code as defined in and
7: * are subject to the Apple Public Source License Version 1.1 (the
8: * "License"). You may not use this file except in compliance with the
9: * License. Please obtain a copy of the License at
10: * http://www.apple.com/publicsource and read it before using this file.
11: *
12: * This Original Code and all software distributed under the License are
13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17: * License for the specific language governing rights and limitations
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22:
23: #if HFS_HARDLINKS
24:
25: #include <sys/systm.h>
26: #include <sys/kernel.h>
27: #include <sys/malloc.h>
28: #include <sys/namei.h>
29: #include <sys/stat.h>
30: #include <sys/vnode.h>
31: #include <vfs/vfs_support.h>
32:
33: #include "hfs.h"
34: #include "hfscommon/headers/FileMgrInternal.h"
35:
36:
37: #define OFFSETOF(structure,field) ((size_t)&((structure *) 0)->field)
38:
39: #define MAKE_DATANODE_NAME(NAME,FID) \
40: (void) sprintf((NAME), "%s%d", HFS_LINK_PREFIX, (FID))
41:
42:
43: #define ALIAS_VERS 2
44: #define ALIAS_TYPE 'alis'
45: #define ALIAS_ENDMARK (-1)
46: #define ALIAS_RSRC_ID 0
47:
48: /* alias kind is a file or directory */
49: enum {
50: kFileAlias, /* 0 file alias */
51: kDirAlias /* 1 directory/folder alias */
52: };
53: typedef int16_t AliasKind;
54:
55:
56: #if PRAGMA_STRUCT_ALIGN
57: #pragma options align=mac68k
58: #elif PRAGMA_STRUCT_PACKPUSH
59: #pragma pack(push, 2)
60: #elif PRAGMA_STRUCT_PACK
61: #pragma pack(2)
62: #endif
63:
64: /* Alias record as it is manipulated by Alias manager */
65: struct aliasrec {
66: /* first two fields are for application use */
67: OSType userType; /* appl stored type */
68: u_int16_t aliasSize; /* alias record size in bytes */
69:
70: /*
71: * What follows is a variable length amount of data that is
72: * private. These private fields should not be accessed by
73: * apps directly.
74: */
75: int16_t aliasVersion; /* alias version, used internally */
76: AliasKind thisAliasKind; /* file or directory */
77: Str27 volumeName; /* volume name */
78: u_int32_t volumeCrDate; /* volume creation date used as volID */
79: u_int16_t volumeSig; /* volume signature (flat or hierarchical) */
80: int16_t volumeType; /* 0 = HD */
81: u_int32_t parDirID; /* parent directory ID */
82: Str63 fileName; /* file or directory(if dir Alias) name */
83: u_int32_t fileNum; /* unique file number(also known as file ID) or directory ID */
84: u_int32_t fileCrDate; /* file or directory creation date */
85: OSType fileType; /* file type */
86: OSType fdCreator; /* file's creator */
87: int16_t nlvlFrom; /* # of levels from fromFile/toFile until a common */
88: int16_t nlvlTo; /* ancestor directory is found */
89: u_int32_t volumeAttr; /* bitfield for VolMntInfo exists or not, vol ejectable or not etc. */
90: int16_t volumeFSID; /* file system ID for the volume */
91: int8_t unused[10]; /* 10 bytes for future expansion */
92: int16_t vdwhat; /* what kind of information */
93: int16_t vdlen; /* length of variable data */
94: };
95:
96:
97:
98: struct rsrcforkhdr {
99: u_int32_t rsrcDataOffset; /* Offset to the resource data from beginning of the file */
100: u_int32_t rsrcMapOffset; /* Offset to the resource map from the beginning of the file */
101: u_int32_t rsrcDataLength; /* Total length of resource data */
102: u_int32_t rsrcMapLength; /* Total length of resource map */
103: };
104:
105: /*
106: For each resource type in a resource file, the resource map contains a type entry
107: which specifies the resource type, how many resources there are of this type, and
108: the offset from the beginning of the type list to the reference list for this type
109: */
110: struct TypeEntry {
111: ResType tType; /* The resource type*/
112: u_int16_t tCount; /* Number of resources of this type, minus 1*/
113: u_int16_t tOffset; /* Offset to resource reference list for this resource type.*/
114: };
115: typedef struct TypeEntry TypeEntry;
116: typedef TypeEntry *TypeEntryPtr;
117: /*
118: For each resource in a resource file, the resource map contains a reference entry
119: which specifies the resource ID, the offset to the resource�s name (if any), the
120: offset to the actual resource data from the beginning of the resource data, and
121: the handle to the resource data in memory, if it�s loaded.
122: */
123:
124:
125: union ResAttrData {
126: u_int8_t rAttr; /* Attributes for this resource*/
127: u_int32_t rDataLocation; /* Offset from beginning of resource data to length of data for this resource.*/
128: };
129: typedef union ResAttrData ResAttrData;
130:
131: struct ReferenceEntry {
132: int16_t rID; /* Resource ID*/
133: int16_t rNameOffset; /* Offset to resource name. (-1 if no name)*/
134: ResAttrData rAttrData; /* Attributes or offset to length of data for this resource*/
135: Handle rHandle; /* Resource handle. (0 if resource is not loaded)*/
136: };
137: typedef struct ReferenceEntry ReferenceEntry;
138:
139: /*
140: The resource map describes all of the resources in a file, with a list of type entries,
141: reference entries, and a list of resource names. It also contains the file reference
142: number of the resource file, and some attributes.
143: I�ve split the resource map into two structs, the rsrcmaphdr, which contains
144: information that is in an empty resource map (a resource map that contains no resources)
145: and the rsrcmap proper, which contains fields which describe type entries, reference
146: entries, and the name list.
147: */
148:
149:
150: struct rsrcmaphdr {
151: struct rsrcforkhdr mHeader; /* Copy of the resource header*/
152: Handle mNext; /* Handle to next resource map*/
153: u_int16_t mRefNum; /* File reference number of this resource file*/
154: u_int8_t mAttr; /* Map attributes (read only, compact, changed)*/
155: u_int8_t mInMemoryAttr; /* Override attributes & decompression bit*/
156: u_int16_t mTypes; /* Offset from start of resource map to type list (typically 0x1C)*/
157: u_int16_t mNames; /* Offset from start of resource map to name list*/
158: };
159:
160: struct rsrcmap {
161: struct rsrcmaphdr mapHeader; /* Header information for the resource map*/
162: u_int16_t typeCount; /* Number of resource types in this map, minus 1*/
163: TypeEntry typeList[1]; /* Type entries for all the types in this resource file*/
164: ReferenceEntry refList[1]; /* Reference entries for all resources in this file*/
165: };
166:
167:
168: struct ResourceFork {
169: struct rsrcforkhdr rsrcHead;
170: u_int8_t reserved[240];
171: u_int32_t rsrcLen;
172: struct aliasrec rsrcData;
173: struct rsrcmap rsrcMap;
174: };
175:
176:
177: #if PRAGMA_STRUCT_ALIGN
178: #pragma options align=reset
179: #elif PRAGMA_STRUCT_PACKPUSH
180: #pragma pack(pop)
181: #elif PRAGMA_STRUCT_PACK
182: #pragma pack()
183: #endif
184:
185:
186: /*
187: *
188: */
189: int
190: readlinknode(ExtendedVCB *vcb, hfsCatalogInfo *catInfo, UInt32 *nodeID)
191: {
192: int result;
193: struct ResourceFork *rfp = NULL;
194: struct buf *bp = NULL;
195: int blksize;
196: daddr_t diskblk;
197: off_t diskoff;
198:
199: *nodeID = 0;
200: blksize = VCBTOHFS(vcb)->hfs_phys_block_size;
201: if (catInfo->nodeData.cnd_rsrcfork.logicalSize < sizeof(struct ResourceFork))
202: return (ENOENT);
203:
204: diskoff = vcb->hfsPlusIOPosOffset;
205: diskoff += (off_t)catInfo->nodeData.cnd_rsrcfork.extents[0].startBlock * (off_t)vcb->blockSize;
206: diskblk = diskoff / blksize;
207:
208: result = bread( VCBTOHFS(vcb)->hfs_devvp,
209: IOBLKNOFORBLK(diskblk, blksize),
210: IOBYTECCNTFORBLK(diskblk, blksize, blksize),
211: NOCRED,
212: &bp);
213: if (result) {
214: if (bp != NULL)
215: brelse(bp);
216: return (result);
217: }
218:
219: rfp = (struct ResourceFork *) ((char *)bp->b_data + IOBYTEOFFSETFORBLK(diskblk, blksize));
220:
221: /* Make sure we have an alias record */
222: if ((rfp->rsrcData.aliasSize >= sizeof(struct aliasrec)) &&
223: (rfp->rsrcData.aliasVersion == ALIAS_VERS) &&
224: (rfp->rsrcData.thisAliasKind == kFileAlias)
225: ) {
226: *nodeID = rfp->rsrcData.fileNum;
227: } else {
228: brelse(bp);
229: return (ENOENT);
230: }
231:
232: brelse(bp);
233:
234: return (0);
235: }
236:
237: /*
238: * Create a new link node
239: *
240: * A link node is a reference to a data node. The only useable fields in the
241: * link are the parentID, name and text encoding. All other catalog fields
242: * are ignored. The target data node reference is stored as an alias.
243: */
244: static int
245: newlinknode(struct hfsnode *dnhp, UInt32 linkPID, char *linkName, struct ucred *cred)
246: {
247: struct hfsCatalogInfo catInfo;
248: struct buf *bp = NULL;
249: struct ResourceFork *rfp = NULL;
250: struct FInfo *fip;
251: ExtendedVCB *vcb;
252: int forksize;
253: u_int32_t blk, diskblk;
254: u_int32_t numblk;
255: u_int32_t phyblksize;
256: int result;
257:
258: blk = numblk = 0;
259: vcb = HTOVCB(dnhp);
260: phyblksize = VCBTOHFS(vcb)->hfs_phys_block_size;
261: forksize = sizeof(struct ResourceFork);
262: if (forksize > phyblksize)
263: return (EINVAL);
264:
265: /* Create the link node directly in the catalog */
266: result = hfsCreate(vcb, linkPID, linkName, IFREG);
267: if (result) return (result);
268:
269: /*
270: * XXX SER Here is a good example where hfsCreate should pass in a catinfo and return
271: * things like the hint and file ID there should be no reason to call lookup here
272: */
273: catInfo.hint = 0;
274: result = hfsLookup(vcb, linkPID, linkName, -1, &catInfo);
275: if (result) goto errExit;
276:
277: fip = (struct FInfo *)&catInfo.nodeData.cnd_finderInfo;
278: fip->fdType = kHardLinkFileType;
279: fip->fdCreator = kHardLinkCreator;
280: fip->fdFlags |= kIsAlias + kHasBeenInited;
281:
282: /* Allocate space for link node's resource fork */
283: result = MacToVFSError(BlockAllocate(vcb, 0, forksize, forksize, TRUE, &blk, &numblk));
284: if (result) goto errExit;
285:
286: catInfo.nodeData.cnd_rsrcfork.extents[0].startBlock = blk;
287: catInfo.nodeData.cnd_rsrcfork.extents[0].blockCount = numblk;
288: catInfo.nodeData.cnd_rsrcfork.logicalSize = forksize;
289: catInfo.nodeData.cnd_rsrcfork.totalBlocks = numblk;
290:
291: result = UpdateCatalogNode(vcb, linkPID, linkName, catInfo.hint, &catInfo.nodeData);
292: if (result) goto errExit;
293:
294: /* map logical block to physical disk block */
295: diskblk = blk * (vcb->blockSize / phyblksize) +
296: vcb->hfsPlusIOPosOffset / phyblksize;
297:
298: bp = getblk (VCBTOHFS(vcb)->hfs_devvp,
299: IOBLKNOFORBLK(diskblk, phyblksize),
300: IOBYTECCNTFORBLK(diskblk, phyblksize, phyblksize),
301: 0, 0);
302: rfp = (struct ResourceFork *) ((char *)bp->b_data + IOBYTEOFFSETFORBLK(diskblk, phyblksize));
303: bzero(rfp, phyblksize);
304:
305: /* fill in resource fork header... */
306: rfp->rsrcHead.rsrcDataOffset = OFFSETOF(struct ResourceFork,rsrcLen);
307: rfp->rsrcHead.rsrcMapOffset = OFFSETOF(struct ResourceFork,rsrcMap);
308: rfp->rsrcHead.rsrcDataLength = sizeof(struct aliasrec) + 4;
309: rfp->rsrcHead.rsrcMapLength = sizeof(struct rsrcmap);
310:
311: /* fill in resource map... */
312: rfp->rsrcMap.mapHeader.mHeader = rfp->rsrcHead;
313: rfp->rsrcMap.mapHeader.mTypes = sizeof(struct rsrcmaphdr);
314: rfp->rsrcMap.mapHeader.mNames = sizeof(struct rsrcmap);
315: rfp->rsrcMap.typeList[0].tType = ALIAS_TYPE;
316: rfp->rsrcMap.typeList[0].tOffset = sizeof(TypeEntry) + 2;
317: rfp->rsrcMap.refList[0].rID = ALIAS_RSRC_ID;
318: rfp->rsrcMap.refList[0].rNameOffset = -1;
319:
320: /* fill in alias resource... */
321: rfp->rsrcData.aliasSize = rfp->rsrcLen = sizeof(struct aliasrec);
322: rfp->rsrcData.aliasVersion = ALIAS_VERS;
323: rfp->rsrcData.thisAliasKind = kFileAlias;
324: // rfp->rsrcData.fileType = '????';
325: // rfp->rsrcData.fdCreator = '????';
326: rfp->rsrcData.fileNum = H_FILEID(dnhp);
327: rfp->rsrcData.fileCrDate = to_hfs_time(dnhp->h_meta->h_crtime);
328: rfp->rsrcData.volumeCrDate = UTCToLocal(vcb->vcbCrDate);
329: rfp->rsrcData.volumeSig = kHFSSigWord; /* always 'BD' */
330: rfp->rsrcData.nlvlFrom = -1;
331: rfp->rsrcData.nlvlTo = -1;
332: rfp->rsrcData.vdwhat = ALIAS_ENDMARK; /* no extra data */
333:
334: /* XXX can this overflow volumeName field? */
335: result = utf8_to_hfs(vcb, strlen(vcb->vcbVN), vcb->vcbVN, rfp->rsrcData.volumeName);
336: if (result) goto errExit;
337:
338: rfp->rsrcData.parDirID = HTOHFS(dnhp)->hfs_private_metadata_dir;
339: MAKE_DATANODE_NAME(&rfp->rsrcData.fileName[1], H_FILEID(dnhp));
340: rfp->rsrcData.fileName[0] = strlen(&rfp->rsrcData.fileName[1]);
341:
342:
343: /* Finally, write resource fork to disk */
344: result = bwrite(bp);
345: if (result) goto errExit;
346:
347: return (0);
348:
349: errExit:
350: /* get rid of link node */
351: (void) hfsDelete(vcb, linkPID, linkName, TRUE, 0);
352: if (numblk > 0)
353: (void) BlockDeallocate(vcb, blk, numblk);
354:
355: return (result);
356: }
357:
358:
359: /*
360: * 2 locks are needed (dvp and hp)
361: * also need catalog lock and extents lock (for exchange)
362: *
363: * caller's responsibility:
364: * componentname cleanup
365: * unlocking dvp and hp
366: */
367: static int
368: hfs_makelink(hp, dvp, cnp)
369: struct hfsnode *hp;
370: struct vnode *dvp;
371: register struct componentname *cnp;
372: {
373: struct proc *p = cnp->cn_proc;
374: struct hfsnode *dhp = VTOH(dvp);
375: u_int32_t ldirID; /* directory ID of linked nodes directory */
376: ExtendedVCB *vcb = VTOVCB(dvp);
377: u_int32_t hint;
378: char nodeName[32];
379: int retval;
380:
381: ldirID = VTOHFS(dvp)->hfs_private_metadata_dir;
382:
383: /* We don't allow link nodes in our Private Meta Data folder! */
384: if ( H_FILEID(dhp) == ldirID)
385: return EPERM;
386:
387: if (vcb->freeBlocks == 0)
388: return ENOSPC;
389:
390: /* lock catalog b-tree */
391: retval = hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
392: if (retval != E_NONE) goto out2;
393:
394: /*
395: * Create a catalog entry for the new link (parentID + name).
396: */
397: retval = newlinknode(hp, H_FILEID(dhp), cnp->cn_nameptr, cnp->cn_cred);
398: if (retval) goto out;
399:
400: /*
401: * If this is a new hardlink then we need to create the data
402: * node (inode) and replace the original file with a link node.
403: */
404: if (hp->h_meta->h_nlink == 1) {
405: MAKE_DATANODE_NAME(nodeName, H_FILEID(hp));
406:
407: /* move source file to data node directory */
408: hint = 0;
409: retval = hfsMoveRename(vcb, H_DIRID(hp), H_NAME(hp), ldirID, nodeName, &hint);
410: if (retval) goto err1Link;
411:
412: /* replace source file with link node */
413: retval = newlinknode(hp, H_DIRID(hp), H_NAME(hp), cnp->cn_cred);
414: if (retval) goto errMoved;
415:
416: hp->h_meta->h_nlink++;
417: hp->h_nodeflags |= IN_CHANGE;
418: hp->h_meta->h_metaflags |= IN_DATANODE;
419: }
420:
421: out:;
422: /* unlock catalog b-tree */
423: (void) hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_RELEASE, p);
424:
425: out2:;
426: return retval;
427:
428:
429: errMoved:;
430: /* put it source file back */
431: hint = 0;
432: (void) hfsMoveRename(vcb, ldirID, nodeName, H_DIRID(hp), H_NAME(hp), &hint);
433: /* fall through */
434: err1Link:;
435: /* get rid of target link node */
436: (void) hfsDelete(vcb, H_FILEID(dhp), cnp->cn_nameptr, TRUE, 0);
437: goto out;
438: }
439:
440:
441: /*
442: * link vnode call
443: #% link vp U U U
444: #% link tdvp L U U
445: #
446: vop_link {
447: IN WILLRELE struct vnode *vp;
448: IN struct vnode *targetPar_vp;
449: IN struct componentname *cnp;
450:
451: */
452: int
453: hfs_link(ap)
454: struct vop_link_args /* {
455: struct vnode *a_vp;
456: struct vnode *a_tdvp;
457: struct componentname *a_cnp;
458: } */ *ap;
459: {
460: struct vnode *vp = ap->a_vp;
461: struct vnode *tdvp = ap->a_tdvp;
462: struct componentname *cnp = ap->a_cnp;
463: struct proc *p = cnp->cn_proc;
464: struct hfsnode *hp;
465: struct timeval tv;
466: int error;
467:
468: #if HFS_DIAGNOSTIC
469: if ((cnp->cn_flags & HASBUF) == 0)
470: panic("hfs_link: no name");
471: #endif
472: if (tdvp->v_mount != vp->v_mount) {
473: VOP_ABORTOP(tdvp, cnp);
474: error = EXDEV;
475: goto out2;
476: }
477: if (VTOVCB(tdvp)->vcbSigWord != kHFSPlusSigWord)
478: return err_link(ap); /* hfs disks don't support hard links */
479:
480: if (VTOHFS(vp)->hfs_private_metadata_dir == 0)
481: return err_link(ap); /* no private metadata dir, no links possible */
482:
483: if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
484: VOP_ABORTOP(tdvp, cnp);
485: goto out2;
486: }
487: hp = VTOH(vp);
488: if (hp->h_meta->h_nlink >= HFS_LINK_MAX) {
489: VOP_ABORTOP(tdvp, cnp);
490: error = EMLINK;
491: goto out1;
492: }
493: if (hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) {
494: VOP_ABORTOP(tdvp, cnp);
495: error = EPERM;
496: goto out1;
497: }
498: hp->h_meta->h_nlink++;
499: hp->h_nodeflags |= IN_CHANGE;
500: tv = time;
501: error = VOP_UPDATE(vp, &tv, &tv, 1);
502: if (!error)
503: error = hfs_makelink(hp, tdvp, cnp);
504: if (error) {
505: hp->h_meta->h_nlink--;
506: hp->h_nodeflags |= IN_CHANGE;
507: }
508: FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
509: out1:
510: if (tdvp != vp)
511: VOP_UNLOCK(vp, 0, p);
512: out2:
513: vput(tdvp);
514: return (error);
515: }
516:
517: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.