]>
git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_link.c
ec061d5883af8a439c4a5ca952c4936e1dfbc61c
2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/mount.h>
35 #include <sys/vnode.h>
36 #include <vfs/vfs_support.h>
37 #include <libkern/libkern.h>
40 #include "hfs_catalog.h"
41 #include "hfs_format.h"
42 #include "hfs_endian.h"
45 static int cur_link_id
= 0;
49 * Create a new indirect link
51 * An indirect link is a reference to a data node. The only useable
52 * fields in the link are the link number, parentID, name and text
53 * encoding. All other catalog fields are ignored.
56 createindirectlink(struct hfsmount
*hfsmp
, u_int32_t linknum
,
57 u_int32_t linkparid
, char *linkName
, cnid_t
*linkcnid
)
59 struct FndrFileInfo
*fip
;
64 /* Setup the descriptor */
65 bzero(&desc
, sizeof(desc
));
66 desc
.cd_nameptr
= linkName
;
67 desc
.cd_namelen
= strlen(linkName
);
68 desc
.cd_parentcnid
= linkparid
;
70 /* Setup the default attributes */
71 bzero(&attr
, sizeof(attr
));
73 /* links are matched to data nodes by link ID and to volumes by create date */
74 attr
.ca_rdev
= linknum
; /* note: cat backend overloads ca_rdev to be the linknum when nlink = 0 */
75 attr
.ca_itime
= HFSTOVCB(hfsmp
)->vcbCrDate
;
76 attr
.ca_mode
= S_IFREG
;
78 fip
= (struct FndrFileInfo
*)&attr
.ca_finderinfo
;
79 fip
->fdType
= SWAP_BE32 (kHardLinkFileType
); /* 'hlnk' */
80 fip
->fdCreator
= SWAP_BE32 (kHFSPlusCreator
); /* 'hfs+' */
81 fip
->fdFlags
= SWAP_BE16 (kHasBeenInited
);
83 /* Create the indirect link directly in the catalog */
84 result
= cat_create(hfsmp
, &desc
, &attr
, NULL
);
86 if (result
== 0 && linkcnid
!= NULL
)
87 *linkcnid
= attr
.ca_fileid
;
94 * 2 locks are needed (dvp and vp)
95 * also need catalog lock
97 * caller's responsibility:
98 * componentname cleanup
99 * unlocking dvp and vp
102 hfs_makelink(struct hfsmount
*hfsmp
, struct cnode
*cp
, struct cnode
*dcp
,
103 struct componentname
*cnp
)
105 vfs_context_t ctx
= cnp
->cn_context
;
106 struct proc
*p
= vfs_context_proc(ctx
);
107 u_int32_t indnodeno
= 0;
109 struct cat_desc to_desc
;
116 if (cur_link_id
== 0) {
117 cur_link_id
= ((random() & 0x3fffffff) + 100);
118 // printf("hfs: initializing cur link id to: 0x%.8x\n", cur_link_id);
121 /* We don't allow link nodes in our Private Meta Data folder! */
122 if (dcp
->c_fileid
== hfsmp
->hfs_privdir_desc
.cd_cnid
)
125 if (hfs_freeblks(hfsmp
, 0) == 0)
128 bzero(&cookie
, sizeof(cat_cookie_t
));
129 /* Reserve some space in the Catalog file. */
130 if ((retval
= cat_preflight(hfsmp
, (2 * CAT_CREATE
)+ CAT_RENAME
, &cookie
, p
))) {
134 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
136 // save off a copy of the current cnid so we can put
137 // it back if we get errors down below
138 orig_cnid
= cp
->c_desc
.cd_cnid
;
141 * If this is a new hardlink then we need to create the data
142 * node (inode) and replace the original file with a link node.
144 if (cp
->c_nlink
== 2 && (cp
->c_flag
& C_HARDLINK
) == 0) {
146 bzero(&to_desc
, sizeof(to_desc
));
147 to_desc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
148 to_desc
.cd_cnid
= cp
->c_fileid
;
151 /* get a unique indirect node number */
153 indnodeno
= cp
->c_fileid
;
155 indnodeno
= cur_link_id
++;
158 MAKE_INODE_NAME(inodename
, indnodeno
);
160 /* move source file to data node directory */
161 to_desc
.cd_nameptr
= inodename
;
162 to_desc
.cd_namelen
= strlen(inodename
);
164 retval
= cat_rename(hfsmp
, &cp
->c_desc
, &hfsmp
->hfs_privdir_desc
,
167 if (retval
!= 0 && retval
!= EEXIST
) {
168 printf("hfs_makelink: cat_rename to %s failed (%d). fileid %d\n",
169 inodename
, retval
, cp
->c_fileid
);
172 } while (retval
== EEXIST
);
176 /* Replace source file with link node */
177 retval
= createindirectlink(hfsmp
, indnodeno
, cp
->c_parentcnid
,
178 cp
->c_desc
.cd_nameptr
, &cp
->c_desc
.cd_cnid
);
180 /* put it source file back */
183 // Put this back to what it was before.
184 cp
->c_desc
.cd_cnid
= orig_cnid
;
186 err
= cat_rename(hfsmp
, &to_desc
, &dcp
->c_desc
, &cp
->c_desc
, NULL
);
188 panic("hfs_makelink: error %d from cat_rename backout 1", err
);
191 cp
->c_rdev
= indnodeno
;
193 indnodeno
= cp
->c_rdev
;
197 * Create a catalog entry for the new link (parentID + name).
199 retval
= createindirectlink(hfsmp
, indnodeno
, dcp
->c_fileid
, cnp
->cn_nameptr
, NULL
);
200 if (retval
&& newlink
) {
203 /* Get rid of new link */
204 (void) cat_delete(hfsmp
, &cp
->c_desc
, &cp
->c_attr
);
206 // Put this back to what it was before.
207 cp
->c_desc
.cd_cnid
= orig_cnid
;
209 /* Put the source file back */
210 err
= cat_rename(hfsmp
, &to_desc
, &dcp
->c_desc
, &cp
->c_desc
, NULL
);
212 panic("hfs_makelink: error %d from cat_rename backout 2", err
);
218 * Finally, if this is a new hardlink then:
219 * - update HFS Private Data dir
220 * - mark the cnode as a hard link
226 panic("hfs_makelink: retval %d but newlink = 1!\n", retval
);
229 hfsmp
->hfs_privdir_attr
.ca_entries
++;
230 retval
= cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
231 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
233 panic("hfs_makelink: cat_update of privdir failed! (%d)\n",
236 hfs_volupdate(hfsmp
, VOL_MKFILE
, 0);
237 cp
->c_flag
|= C_HARDLINK
;
238 if ((vp
= cp
->c_vp
) != NULLVP
) {
239 if (vnode_get(vp
) == 0) {
240 vnode_set_hard_link(vp
);
244 if ((vp
= cp
->c_rsrc_vp
) != NULLVP
) {
245 if (vnode_get(vp
) == 0) {
246 vnode_set_hard_link(vp
);
250 cp
->c_touch_chgtime
= TRUE
;
251 cp
->c_flag
|= C_FORCEUPDATE
;
253 dcp
->c_flag
|= C_FORCEUPDATE
;
256 hfs_systemfile_unlock(hfsmp
, lockflags
);
258 cat_postflight(hfsmp
, &cookie
, p
);
269 IN WILLRELE struct vnode *vp;
270 IN struct vnode *targetPar_vp;
271 IN struct componentname *cnp;
272 IN vfs_context_t context;
277 hfs_vnop_link(struct vnop_link_args
*ap
)
279 struct hfsmount
*hfsmp
;
280 struct vnode
*vp
= ap
->a_vp
;
281 struct vnode
*tdvp
= ap
->a_tdvp
;
282 struct componentname
*cnp
= ap
->a_cnp
;
286 int error
, ret
, lockflags
;
287 struct cat_desc cndesc
;
289 if (VTOVCB(tdvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
290 return err_link(ap
); /* hfs disks don't support hard links */
292 if (VTOHFS(vp
)->hfs_privdir_desc
.cd_cnid
== 0) {
293 return err_link(ap
); /* no private metadata dir, no links possible */
295 if (vnode_mount(tdvp
) != vnode_mount(vp
)) {
298 if ((error
= hfs_lockpair(VTOC(tdvp
), VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
305 if (cp
->c_nlink
>= HFS_LINK_MAX
) {
309 if (cp
->c_flags
& (IMMUTABLE
| APPEND
)) {
313 if (cp
->c_flag
& (C_NOEXISTS
| C_DELETED
)) {
318 v_type
= vnode_vtype(vp
);
319 if (v_type
== VBLK
|| v_type
== VCHR
) {
320 error
= EINVAL
; /* cannot link to a special file */
324 if (hfs_start_transaction(hfsmp
) != 0) {
325 error
= EINVAL
; /* cannot link to a special file */
330 cp
->c_touch_chgtime
= TRUE
;
332 error
= hfs_makelink(hfsmp
, cp
, tdcp
, cnp
);
335 hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
337 /* Invalidate negative cache entries in the destination directory */
338 if (hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)
339 cache_purge_negatives(tdvp
);
341 /* Update the target directory and volume stats */
344 tdcp
->c_touch_chgtime
= TRUE
;
345 tdcp
->c_touch_modtime
= TRUE
;
346 tdcp
->c_flag
|= C_FORCEUPDATE
;
348 error
= hfs_update(tdvp
, 0);
350 panic("hfs_vnop_link: error updating tdvp 0x%x\n", tdvp
);
353 hfs_volupdate(hfsmp
, VOL_MKFILE
,
354 (tdcp
->c_cnid
== kHFSRootFolderID
));
357 cp
->c_flag
|= C_FORCEUPDATE
; // otherwise hfs_update() might skip the update
359 if ((ret
= hfs_update(vp
, TRUE
)) != 0) {
360 panic("hfs_vnop_link: error %d updating vp @ 0x%x\n", ret
, vp
);
363 hfs_end_transaction(hfsmp
);
365 HFS_KNOTE(vp
, NOTE_LINK
);
366 HFS_KNOTE(tdvp
, NOTE_WRITE
);
368 hfs_unlockpair(tdcp
, cp
);