]>
git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_link.c
2 * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
27 #include <sys/systm.h>
28 #include <sys/kernel.h>
29 #include <sys/malloc.h>
30 #include <sys/mount.h>
31 #include <sys/namei.h>
33 #include <sys/vnode.h>
34 #include <vfs/vfs_support.h>
35 #include <libkern/libkern.h>
38 #include "hfs_catalog.h"
39 #include "hfs_format.h"
40 #include "hfs_endian.h"
44 * Create a new indirect link
46 * An indirect link is a reference to a data node. The only useable
47 * fields in the link are the link number, parentID, name and text
48 * encoding. All other catalog fields are ignored.
51 createindirectlink(struct hfsmount
*hfsmp
, u_int32_t linknum
,
52 u_int32_t linkparid
, char *linkName
, cnid_t
*linkcnid
)
54 struct FndrFileInfo
*fip
;
59 /* Setup the descriptor */
60 bzero(&desc
, sizeof(desc
));
61 desc
.cd_nameptr
= linkName
;
62 desc
.cd_namelen
= strlen(linkName
);
63 desc
.cd_parentcnid
= linkparid
;
65 /* Setup the default attributes */
66 bzero(&attr
, sizeof(attr
));
68 /* links are matched to data nodes by link ID and to volumes by create date */
69 attr
.ca_rdev
= linknum
; /* note: cat backend overloads ca_rdev to be the linknum when nlink = 0 */
70 attr
.ca_itime
= HFSTOVCB(hfsmp
)->vcbCrDate
;
71 attr
.ca_mode
= S_IFREG
;
73 fip
= (struct FndrFileInfo
*)&attr
.ca_finderinfo
;
74 fip
->fdType
= SWAP_BE32 (kHardLinkFileType
); /* 'hlnk' */
75 fip
->fdCreator
= SWAP_BE32 (kHFSPlusCreator
); /* 'hfs+' */
76 fip
->fdFlags
= SWAP_BE16 (kHasBeenInited
);
78 hfs_global_shared_lock_acquire(hfsmp
);
80 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
81 hfs_global_shared_lock_release(hfsmp
);
86 /* Create the indirect link directly in the catalog */
87 result
= cat_create(hfsmp
, &desc
, &attr
, NULL
);
89 if (result
== 0 && linkcnid
!= NULL
)
90 *linkcnid
= attr
.ca_fileid
;
93 journal_end_transaction(hfsmp
->jnl
);
95 hfs_global_shared_lock_release(hfsmp
);
102 * 2 locks are needed (dvp and vp)
103 * also need catalog lock
105 * caller's responsibility:
106 * componentname cleanup
107 * unlocking dvp and vp
110 hfs_makelink(struct hfsmount
*hfsmp
, struct cnode
*cp
, struct cnode
*dcp
,
111 struct componentname
*cnp
)
113 struct proc
*p
= cnp
->cn_proc
;
114 u_int32_t indnodeno
= 0;
116 struct cat_desc to_desc
;
119 cat_cookie_t cookie
= {0};
122 /* We don't allow link nodes in our Private Meta Data folder! */
123 if (dcp
->c_fileid
== hfsmp
->hfs_privdir_desc
.cd_cnid
)
126 if (hfs_freeblks(hfsmp
, 0) == 0)
129 /* Reserve some space in the Catalog file. */
130 if ((retval
= cat_preflight(hfsmp
, (2 * CAT_CREATE
)+ CAT_RENAME
, &cookie
, p
))) {
134 /* Lock catalog b-tree */
135 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
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 */
152 indnodeno
= ((random() & 0x3fffffff) + 100);
153 MAKE_INODE_NAME(inodename
, indnodeno
);
155 /* move source file to data node directory */
156 to_desc
.cd_nameptr
= inodename
;
157 to_desc
.cd_namelen
= strlen(inodename
);
159 retval
= cat_rename(hfsmp
, &cp
->c_desc
, &hfsmp
->hfs_privdir_desc
,
162 } while (retval
== EEXIST
);
166 /* Replace source file with link node */
167 retval
= createindirectlink(hfsmp
, indnodeno
, cp
->c_parentcnid
,
168 cp
->c_desc
.cd_nameptr
, &cp
->c_desc
.cd_cnid
);
170 /* put it source file back */
175 err
= cat_rename(hfsmp
, &to_desc
, &dcp
->c_desc
, &cp
->c_desc
, NULL
);
177 panic("hfs_makelink: error %d from cat_rename backout 1", err
);
180 (void) cat_rename(hfsmp
, &to_desc
, &dcp
->c_desc
, &cp
->c_desc
, NULL
);
184 cp
->c_rdev
= indnodeno
;
186 indnodeno
= cp
->c_rdev
;
190 * Create a catalog entry for the new link (parentID + name).
192 retval
= createindirectlink(hfsmp
, indnodeno
, dcp
->c_fileid
, cnp
->cn_nameptr
, NULL
);
193 if (retval
&& newlink
) {
194 /* Get rid of new link */
195 (void) cat_delete(hfsmp
, &cp
->c_desc
, &cp
->c_attr
);
197 /* Put the source file back */
202 err
= cat_rename(hfsmp
, &to_desc
, &dcp
->c_desc
, &cp
->c_desc
, NULL
);
204 panic("hfs_makelink: error %d from cat_rename backout 2", err
);
207 (void) cat_rename(hfsmp
, &to_desc
, &dcp
->c_desc
, &cp
->c_desc
, NULL
);
213 * Finally, if this is a new hardlink then:
214 * - update HFS Private Data dir
215 * - mark the cnode as a hard link
218 hfsmp
->hfs_privdir_attr
.ca_entries
++;
219 (void)cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
220 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
221 hfs_volupdate(hfsmp
, VOL_MKFILE
, 0);
222 cp
->c_flag
|= (C_CHANGE
| C_HARDLINK
);
226 /* Unlock catalog b-tree */
227 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
229 cat_postflight(hfsmp
, &cookie
, p
);
240 IN WILLRELE struct vnode *vp;
241 IN struct vnode *targetPar_vp;
242 IN struct componentname *cnp;
248 struct vop_link_args
/* {
250 struct vnode *a_tdvp;
251 struct componentname *a_cnp;
254 struct hfsmount
*hfsmp
;
255 struct vnode
*vp
= ap
->a_vp
;
256 struct vnode
*tdvp
= ap
->a_tdvp
;
257 struct componentname
*cnp
= ap
->a_cnp
;
258 struct proc
*p
= cnp
->cn_proc
;
267 if ((cnp
->cn_flags
& HASBUF
) == 0)
268 panic("hfs_link: no name");
270 if (tdvp
->v_mount
!= vp
->v_mount
) {
271 VOP_ABORTOP(tdvp
, cnp
);
275 if (VTOVCB(tdvp
)->vcbSigWord
!= kHFSPlusSigWord
)
276 return err_link(ap
); /* hfs disks don't support hard links */
278 if (hfsmp
->hfs_privdir_desc
.cd_cnid
== 0)
279 return err_link(ap
); /* no private metadata dir, no links possible */
281 if (tdvp
!= vp
&& (error
= vn_lock(vp
, LK_EXCLUSIVE
, p
))) {
282 VOP_ABORTOP(tdvp
, cnp
);
288 if (cp
->c_nlink
>= HFS_LINK_MAX
) {
289 VOP_ABORTOP(tdvp
, cnp
);
293 if (cp
->c_flags
& (IMMUTABLE
| APPEND
)) {
294 VOP_ABORTOP(tdvp
, cnp
);
298 if (vp
->v_type
== VBLK
|| vp
->v_type
== VCHR
) {
299 VOP_ABORTOP(tdvp
, cnp
);
300 error
= EINVAL
; /* cannot link to a special file */
304 hfs_global_shared_lock_acquire(hfsmp
);
306 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
307 hfs_global_shared_lock_release(hfsmp
);
308 VOP_ABORTOP(tdvp
, cnp
);
309 error
= EINVAL
; /* cannot link to a special file */
315 cp
->c_flag
|= C_CHANGE
;
318 error
= VOP_UPDATE(vp
, &tv
, &tv
, 1);
320 error
= hfs_makelink(hfsmp
, cp
, tdcp
, cnp
);
324 cp
->c_flag
|= C_CHANGE
;
326 /* Update the target directory and volume stats */
329 tdcp
->c_flag
|= C_CHANGE
| C_UPDATE
;
331 (void) VOP_UPDATE(tdvp
, &tv
, &tv
, 0);
333 hfs_volupdate(hfsmp
, VOL_MKFILE
,
334 (tdcp
->c_cnid
== kHFSRootFolderID
));
337 // XXXdbg - need to do this here as well because cp could have changed
338 error
= VOP_UPDATE(vp
, &tv
, &tv
, 1);
342 journal_end_transaction(hfsmp
->jnl
);
344 hfs_global_shared_lock_release(hfsmp
);
346 /* free the pathname buffer */
348 char *tmp
= cnp
->cn_pnbuf
;
349 cnp
->cn_pnbuf
= NULL
;
350 cnp
->cn_flags
&= ~HASBUF
;
351 FREE_ZONE(tmp
, cnp
->cn_pnlen
, M_NAMEI
);
354 HFS_KNOTE(vp
, NOTE_LINK
);
355 HFS_KNOTE(tdvp
, NOTE_WRITE
);
359 VOP_UNLOCK(vp
, 0, p
);