|
|
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.