]>
Commit | Line | Data |
---|---|---|
9bccf70c | 1 | /* |
2d21ac55 | 2 | * Copyright (c) 2000-2007 Apple Inc. All rights reserved. |
5d5c5d0d | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
9bccf70c | 5 | * |
2d21ac55 A |
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. | |
8f6c56a5 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
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 | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
9bccf70c A |
27 | */ |
28 | ||
29 | #include <sys/systm.h> | |
30 | #include <sys/kernel.h> | |
91447636 | 31 | #include <sys/file_internal.h> |
9bccf70c A |
32 | #include <sys/dirent.h> |
33 | #include <sys/stat.h> | |
34 | #include <sys/buf.h> | |
35 | #include <sys/mount.h> | |
2d21ac55 | 36 | #include <sys/vnode_if.h> |
91447636 | 37 | #include <sys/vnode_internal.h> |
9bccf70c | 38 | #include <sys/malloc.h> |
9bccf70c | 39 | #include <sys/ubc.h> |
2d21ac55 | 40 | #include <sys/ubc_internal.h> |
91447636 | 41 | #include <sys/paths.h> |
9bccf70c | 42 | #include <sys/quota.h> |
55e303ae A |
43 | #include <sys/time.h> |
44 | #include <sys/disk.h> | |
91447636 A |
45 | #include <sys/kauth.h> |
46 | #include <sys/uio_internal.h> | |
9bccf70c A |
47 | |
48 | #include <miscfs/specfs/specdev.h> | |
49 | #include <miscfs/fifofs/fifo.h> | |
50 | #include <vfs/vfs_support.h> | |
51 | #include <machine/spl.h> | |
52 | ||
53 | #include <sys/kdebug.h> | |
3a60a9f5 | 54 | #include <sys/sysctl.h> |
9bccf70c A |
55 | |
56 | #include "hfs.h" | |
57 | #include "hfs_catalog.h" | |
58 | #include "hfs_cnode.h" | |
9bccf70c A |
59 | #include "hfs_dbg.h" |
60 | #include "hfs_mount.h" | |
61 | #include "hfs_quota.h" | |
62 | #include "hfs_endian.h" | |
63 | ||
64 | #include "hfscommon/headers/BTreesInternal.h" | |
65 | #include "hfscommon/headers/FileMgrInternal.h" | |
66 | ||
9bccf70c | 67 | |
55e303ae | 68 | #define KNDETACH_VNLOCKED 0x00000001 |
9bccf70c | 69 | |
55e303ae | 70 | #define CARBON_TEMP_DIR_NAME "Cleanup At Startup" |
9bccf70c | 71 | |
90556fb8 | 72 | |
9bccf70c A |
73 | /* Global vfs data structures for hfs */ |
74 | ||
3a60a9f5 A |
75 | /* Always F_FULLFSYNC? 1=yes,0=no (default due to "various" reasons is 'no') */ |
76 | int always_do_fullfsync = 0; | |
77 | SYSCTL_INT (_kern, OID_AUTO, always_do_fullfsync, CTLFLAG_RW, &always_do_fullfsync, 0, "always F_FULLFSYNC when fsync is called"); | |
9bccf70c | 78 | |
91447636 A |
79 | static int hfs_makenode(struct vnode *dvp, struct vnode **vpp, |
80 | struct componentname *cnp, struct vnode_attr *vap, | |
81 | vfs_context_t ctx); | |
9bccf70c | 82 | |
2d21ac55 A |
83 | static int hfs_metasync(struct hfsmount *hfsmp, daddr64_t node, __unused struct proc *p); |
84 | static int hfs_metasync_all(struct hfsmount *hfsmp); | |
9bccf70c | 85 | |
55e303ae A |
86 | static int hfs_removedir(struct vnode *, struct vnode *, struct componentname *, |
87 | int); | |
88 | ||
89 | static int hfs_removefile(struct vnode *, struct vnode *, struct componentname *, | |
2d21ac55 A |
90 | int, int, int); |
91 | ||
92 | #if FIFO | |
93 | static int hfsfifo_read(struct vnop_read_args *); | |
94 | static int hfsfifo_write(struct vnop_write_args *); | |
95 | static int hfsfifo_close(struct vnop_close_args *); | |
96 | static int hfsfifo_kqfilt_add(struct vnop_kqfilt_add_args *); | |
97 | static int hfsfifo_kqfilt_remove(struct vnop_kqfilt_remove_args *); | |
98 | ||
99 | extern int (**fifo_vnodeop_p)(void *); | |
100 | #endif /* FIFO */ | |
91447636 A |
101 | |
102 | static int hfs_vnop_close(struct vnop_close_args*); | |
103 | static int hfs_vnop_create(struct vnop_create_args*); | |
104 | static int hfs_vnop_exchange(struct vnop_exchange_args*); | |
105 | static int hfs_vnop_fsync(struct vnop_fsync_args*); | |
106 | static int hfs_vnop_mkdir(struct vnop_mkdir_args*); | |
107 | static int hfs_vnop_mknod(struct vnop_mknod_args*); | |
108 | static int hfs_vnop_getattr(struct vnop_getattr_args*); | |
109 | static int hfs_vnop_open(struct vnop_open_args*); | |
110 | static int hfs_vnop_readdir(struct vnop_readdir_args*); | |
111 | static int hfs_vnop_remove(struct vnop_remove_args*); | |
112 | static int hfs_vnop_rename(struct vnop_rename_args*); | |
113 | static int hfs_vnop_rmdir(struct vnop_rmdir_args*); | |
114 | static int hfs_vnop_symlink(struct vnop_symlink_args*); | |
115 | static int hfs_vnop_setattr(struct vnop_setattr_args*); | |
2d21ac55 A |
116 | static int hfs_vnop_readlink(struct vnop_readlink_args *); |
117 | static int hfs_vnop_pathconf(struct vnop_pathconf_args *); | |
118 | static int hfs_vnop_kqfiltremove(struct vnop_kqfilt_remove_args *); | |
119 | static int hfs_vnop_whiteout(struct vnop_whiteout_args *); | |
120 | static int hfsspec_read(struct vnop_read_args *); | |
121 | static int hfsspec_write(struct vnop_write_args *); | |
122 | static int hfsspec_close(struct vnop_close_args *); | |
55e303ae A |
123 | |
124 | /* Options for hfs_removedir and hfs_removefile */ | |
91447636 | 125 | #define HFSRM_SKIP_RESERVE 0x01 |
55e303ae A |
126 | |
127 | ||
9bccf70c | 128 | |
9bccf70c A |
129 | |
130 | /***************************************************************************** | |
131 | * | |
132 | * Common Operations on vnodes | |
133 | * | |
134 | *****************************************************************************/ | |
135 | ||
136 | /* | |
91447636 A |
137 | * Create a regular file. |
138 | */ | |
9bccf70c | 139 | static int |
91447636 | 140 | hfs_vnop_create(struct vnop_create_args *ap) |
9bccf70c | 141 | { |
2d21ac55 A |
142 | int error; |
143 | ||
144 | again: | |
145 | error = hfs_makenode(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap, ap->a_context); | |
146 | ||
147 | /* | |
148 | * We speculatively skipped the original lookup of the leaf | |
149 | * for CREATE. Since it exists, go get it as long as they | |
150 | * didn't want an exclusive create. | |
151 | */ | |
152 | if ((error == EEXIST) && !(ap->a_vap->va_vaflags & VA_EXCLUSIVE)) { | |
153 | struct vnop_lookup_args args; | |
154 | ||
155 | args.a_desc = &vnop_lookup_desc; | |
156 | args.a_dvp = ap->a_dvp; | |
157 | args.a_vpp = ap->a_vpp; | |
158 | args.a_cnp = ap->a_cnp; | |
159 | args.a_context = ap->a_context; | |
160 | args.a_cnp->cn_nameiop = LOOKUP; | |
161 | error = hfs_vnop_lookup(&args); | |
162 | /* | |
163 | * We can also race with remove for this file. | |
164 | */ | |
165 | if (error == ENOENT) { | |
166 | goto again; | |
167 | } | |
168 | ||
169 | /* Make sure it was file. */ | |
170 | if ((error == 0) && !vnode_isreg(*args.a_vpp)) { | |
171 | vnode_put(*args.a_vpp); | |
172 | error = EEXIST; | |
173 | } | |
174 | args.a_cnp->cn_nameiop = CREATE; | |
175 | } | |
176 | return (error); | |
9bccf70c A |
177 | } |
178 | ||
9bccf70c | 179 | /* |
91447636 A |
180 | * Make device special file. |
181 | */ | |
9bccf70c | 182 | static int |
91447636 | 183 | hfs_vnop_mknod(struct vnop_mknod_args *ap) |
9bccf70c | 184 | { |
91447636 A |
185 | struct vnode_attr *vap = ap->a_vap; |
186 | struct vnode *dvp = ap->a_dvp; | |
9bccf70c A |
187 | struct vnode **vpp = ap->a_vpp; |
188 | struct cnode *cp; | |
189 | int error; | |
190 | ||
91447636 A |
191 | if (VTOVCB(dvp)->vcbSigWord != kHFSPlusSigWord) { |
192 | return (ENOTSUP); | |
9bccf70c A |
193 | } |
194 | ||
195 | /* Create the vnode */ | |
91447636 | 196 | error = hfs_makenode(dvp, vpp, ap->a_cnp, vap, ap->a_context); |
9bccf70c A |
197 | if (error) |
198 | return (error); | |
91447636 | 199 | |
9bccf70c | 200 | cp = VTOC(*vpp); |
91447636 A |
201 | cp->c_touch_acctime = TRUE; |
202 | cp->c_touch_chgtime = TRUE; | |
203 | cp->c_touch_modtime = TRUE; | |
204 | ||
9bccf70c A |
205 | if ((vap->va_rdev != VNOVAL) && |
206 | (vap->va_type == VBLK || vap->va_type == VCHR)) | |
207 | cp->c_rdev = vap->va_rdev; | |
91447636 | 208 | |
9bccf70c A |
209 | return (0); |
210 | } | |
211 | ||
9bccf70c | 212 | /* |
91447636 A |
213 | * Open a file/directory. |
214 | */ | |
9bccf70c | 215 | static int |
91447636 | 216 | hfs_vnop_open(struct vnop_open_args *ap) |
9bccf70c A |
217 | { |
218 | struct vnode *vp = ap->a_vp; | |
91447636 | 219 | struct filefork *fp; |
55e303ae | 220 | struct timeval tv; |
91447636 | 221 | int error; |
9bccf70c A |
222 | |
223 | /* | |
224 | * Files marked append-only must be opened for appending. | |
225 | */ | |
91447636 | 226 | if ((VTOC(vp)->c_flags & APPEND) && !vnode_isdir(vp) && |
9bccf70c A |
227 | (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) |
228 | return (EPERM); | |
229 | ||
91447636 A |
230 | if (vnode_isreg(vp) && !UBCINFOEXISTS(vp)) |
231 | return (EBUSY); /* file is in use by the kernel */ | |
55e303ae | 232 | |
91447636 A |
233 | /* Don't allow journal file to be opened externally. */ |
234 | if (VTOC(vp)->c_fileid == VTOHFS(vp)->hfs_jnlfileid) | |
235 | return (EPERM); | |
55e303ae A |
236 | /* |
237 | * On the first (non-busy) open of a fragmented | |
238 | * file attempt to de-frag it (if its less than 20MB). | |
239 | */ | |
240 | if ((VTOHFS(vp)->hfs_flags & HFS_READ_ONLY) || | |
91447636 | 241 | (VTOHFS(vp)->jnl == NULL) || |
2d21ac55 A |
242 | #if NAMEDSTREAMS |
243 | !vnode_isreg(vp) || vnode_isinuse(vp, 0) || vnode_isnamedstream(vp)) { | |
244 | #else | |
91447636 | 245 | !vnode_isreg(vp) || vnode_isinuse(vp, 0)) { |
2d21ac55 | 246 | #endif |
55e303ae A |
247 | return (0); |
248 | } | |
91447636 A |
249 | |
250 | if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) | |
251 | return (error); | |
55e303ae A |
252 | fp = VTOF(vp); |
253 | if (fp->ff_blocks && | |
254 | fp->ff_extents[7].blockCount != 0 && | |
255 | fp->ff_size <= (20 * 1024 * 1024)) { | |
2d21ac55 A |
256 | struct timeval now; |
257 | struct cnode *cp = VTOC(vp); | |
55e303ae A |
258 | /* |
259 | * Wait until system bootup is done (3 min). | |
2d21ac55 A |
260 | * And don't relocate a file that's been modified |
261 | * within the past minute -- this can lead to | |
262 | * system thrashing. | |
55e303ae A |
263 | */ |
264 | microuptime(&tv); | |
2d21ac55 A |
265 | microtime(&now); |
266 | if (tv.tv_sec > (60 * 3) && | |
267 | ((now.tv_sec - cp->c_mtime) > 60)) { | |
91447636 A |
268 | (void) hfs_relocate(vp, VTOVCB(vp)->nextAllocation + 4096, |
269 | vfs_context_ucred(ap->a_context), | |
270 | vfs_context_proc(ap->a_context)); | |
55e303ae | 271 | } |
55e303ae | 272 | } |
91447636 | 273 | hfs_unlock(VTOC(vp)); |
55e303ae | 274 | |
9bccf70c A |
275 | return (0); |
276 | } | |
277 | ||
9bccf70c | 278 | |
91447636 A |
279 | /* |
280 | * Close a file/directory. | |
281 | */ | |
9bccf70c | 282 | static int |
91447636 A |
283 | hfs_vnop_close(ap) |
284 | struct vnop_close_args /* { | |
9bccf70c A |
285 | struct vnode *a_vp; |
286 | int a_fflag; | |
91447636 | 287 | vfs_context_t a_context; |
9bccf70c A |
288 | } */ *ap; |
289 | { | |
290 | register struct vnode *vp = ap->a_vp; | |
91447636 A |
291 | register struct cnode *cp; |
292 | struct proc *p = vfs_context_proc(ap->a_context); | |
293 | struct hfsmount *hfsmp; | |
294 | int busy; | |
55e303ae | 295 | |
91447636 | 296 | if ( hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) != 0) |
9bccf70c | 297 | return (0); |
91447636 A |
298 | cp = VTOC(vp); |
299 | hfsmp = VTOHFS(vp); | |
9bccf70c | 300 | |
91447636 A |
301 | // if we froze the fs and we're exiting, then "thaw" the fs |
302 | if (hfsmp->hfs_freezing_proc == p && proc_exiting(p)) { | |
303 | hfsmp->hfs_freezing_proc = NULL; | |
304 | hfs_global_exclusive_lock_release(hfsmp); | |
3a60a9f5 | 305 | lck_rw_unlock_exclusive(&hfsmp->hfs_insync); |
91447636 | 306 | } |
9bccf70c | 307 | |
91447636 | 308 | busy = vnode_isinuse(vp, 1); |
9bccf70c | 309 | |
91447636 A |
310 | if (busy) { |
311 | hfs_touchtimes(VTOHFS(vp), cp); | |
312 | } | |
313 | if (vnode_isdir(vp)) { | |
314 | hfs_reldirhints(cp, busy); | |
315 | } else if (vnode_issystem(vp) && !busy) { | |
316 | vnode_recycle(vp); | |
9bccf70c | 317 | } |
91447636 A |
318 | |
319 | hfs_unlock(cp); | |
9bccf70c A |
320 | return (0); |
321 | } | |
322 | ||
323 | /* | |
91447636 A |
324 | * Get basic attributes. |
325 | */ | |
9bccf70c | 326 | static int |
91447636 | 327 | hfs_vnop_getattr(struct vnop_getattr_args *ap) |
9bccf70c | 328 | { |
2d21ac55 A |
329 | #define VNODE_ATTR_TIMES \ |
330 | (VNODE_ATTR_va_access_time|VNODE_ATTR_va_change_time|VNODE_ATTR_va_modify_time) | |
331 | #define VNODE_ATTR_AUTH \ | |
332 | (VNODE_ATTR_va_mode | VNODE_ATTR_va_uid | VNODE_ATTR_va_gid | \ | |
333 | VNODE_ATTR_va_flags | VNODE_ATTR_va_acl) | |
334 | ||
9bccf70c | 335 | struct vnode *vp = ap->a_vp; |
91447636 A |
336 | struct vnode_attr *vap = ap->a_vap; |
337 | struct vnode *rvp = NULL; | |
338 | struct hfsmount *hfsmp; | |
339 | struct cnode *cp; | |
2d21ac55 | 340 | uint64_t data_size; |
91447636 A |
341 | enum vtype v_type; |
342 | int error = 0; | |
9bccf70c | 343 | |
91447636 | 344 | cp = VTOC(vp); |
2d21ac55 A |
345 | |
346 | /* | |
347 | * Shortcut for vnode_authorize path. Each of the attributes | |
348 | * in this set is updated atomically so we don't need to take | |
349 | * the cnode lock to access them. | |
350 | */ | |
351 | if ((vap->va_active & ~VNODE_ATTR_AUTH) == 0) { | |
352 | /* Make sure file still exists. */ | |
353 | if (cp->c_flag & C_NOEXISTS) | |
354 | return (ENOENT); | |
355 | ||
356 | vap->va_uid = cp->c_uid; | |
357 | vap->va_gid = cp->c_gid; | |
358 | vap->va_mode = cp->c_mode; | |
359 | vap->va_flags = cp->c_flags; | |
360 | vap->va_supported |= VNODE_ATTR_AUTH & ~VNODE_ATTR_va_acl; | |
361 | ||
362 | if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) { | |
363 | vap->va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE; | |
364 | VATTR_SET_SUPPORTED(vap, va_acl); | |
365 | } | |
366 | return (0); | |
367 | } | |
91447636 | 368 | hfsmp = VTOHFS(vp); |
91447636 | 369 | v_type = vnode_vtype(vp); |
9bccf70c | 370 | |
2d21ac55 A |
371 | /* |
372 | * If time attributes are requested and we have cnode times | |
373 | * that require updating, then acquire an exclusive lock on | |
374 | * the cnode before updating the times. Otherwise we can | |
375 | * just acquire a shared lock. | |
376 | */ | |
377 | if ((vap->va_active & VNODE_ATTR_TIMES) && | |
378 | (cp->c_touch_acctime || cp->c_touch_chgtime || cp->c_touch_modtime)) { | |
379 | if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) | |
380 | return (error); | |
381 | hfs_touchtimes(hfsmp, cp); | |
382 | } else { | |
383 | if ((error = hfs_lock(cp, HFS_SHARED_LOCK))) | |
384 | return (error); | |
385 | } | |
386 | ||
91447636 | 387 | if (v_type == VDIR) { |
2d21ac55 A |
388 | data_size = (cp->c_entries + 2) * AVERAGE_HFSDIRENTRY_SIZE; |
389 | ||
91447636 | 390 | if (VATTR_IS_ACTIVE(vap, va_nlink)) { |
2d21ac55 | 391 | int nlink; |
91447636 | 392 | |
2d21ac55 A |
393 | /* |
394 | * For directories, the va_nlink is esentially a count | |
395 | * of the ".." references to a directory plus the "." | |
396 | * reference and the directory itself. So for HFS+ this | |
397 | * becomes the sub-directory count plus two. | |
398 | * | |
399 | * In the absence of a sub-directory count we use the | |
400 | * directory's item count. This will be too high in | |
401 | * most cases since it also includes files. | |
402 | */ | |
403 | if ((hfsmp->hfs_flags & HFS_FOLDERCOUNT) && | |
404 | (cp->c_attr.ca_recflags & kHFSHasFolderCountMask)) | |
405 | nlink = cp->c_attr.ca_dircount; /* implied ".." entries */ | |
406 | else | |
407 | nlink = cp->c_entries; | |
408 | ||
409 | /* Account for ourself and our "." entry */ | |
410 | nlink += 2; | |
411 | /* Hide our private directories. */ | |
412 | if (cp->c_cnid == kHFSRootFolderID) { | |
413 | if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0) { | |
414 | --nlink; | |
415 | } | |
416 | if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0) { | |
417 | --nlink; | |
418 | } | |
91447636 | 419 | } |
2d21ac55 A |
420 | VATTR_RETURN(vap, va_nlink, (u_int64_t)nlink); |
421 | } | |
91447636 A |
422 | if (VATTR_IS_ACTIVE(vap, va_nchildren)) { |
423 | int entries; | |
424 | ||
425 | entries = cp->c_entries; | |
2d21ac55 A |
426 | /* Hide our private files and directories. */ |
427 | if (cp->c_cnid == kHFSRootFolderID) { | |
428 | if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0) | |
429 | --entries; | |
430 | if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0) | |
431 | --entries; | |
432 | if (hfsmp->jnl || ((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) | |
433 | entries -= 2; /* hide the journal files */ | |
91447636 A |
434 | } |
435 | VATTR_RETURN(vap, va_nchildren, entries); | |
436 | } | |
2d21ac55 A |
437 | /* |
438 | * The va_dirlinkcount is the count of real directory hard links. | |
439 | * (i.e. its not the sum of the implied "." and ".." references) | |
440 | */ | |
441 | if (VATTR_IS_ACTIVE(vap, va_dirlinkcount)) { | |
442 | VATTR_RETURN(vap, va_dirlinkcount, (uint32_t)cp->c_linkcount); | |
443 | } | |
444 | } else /* !VDIR */ { | |
445 | data_size = VCTOF(vp, cp)->ff_size; | |
446 | ||
447 | VATTR_RETURN(vap, va_nlink, (u_int64_t)cp->c_linkcount); | |
448 | if (VATTR_IS_ACTIVE(vap, va_data_alloc)) { | |
449 | u_int64_t blocks; | |
450 | ||
451 | blocks = VCTOF(vp, cp)->ff_blocks; | |
452 | VATTR_RETURN(vap, va_data_alloc, blocks * (u_int64_t)hfsmp->blockSize); | |
453 | } | |
91447636 | 454 | } |
9bccf70c | 455 | |
91447636 A |
456 | /* conditional because 64-bit arithmetic can be expensive */ |
457 | if (VATTR_IS_ACTIVE(vap, va_total_size)) { | |
458 | if (v_type == VDIR) { | |
2d21ac55 | 459 | VATTR_RETURN(vap, va_total_size, (cp->c_entries + 2) * AVERAGE_HFSDIRENTRY_SIZE); |
91447636 | 460 | } else { |
2d21ac55 | 461 | u_int64_t total_size = 0; |
91447636 A |
462 | struct cnode *rcp; |
463 | ||
464 | if (cp->c_datafork) { | |
465 | total_size = cp->c_datafork->ff_size; | |
466 | } | |
9bccf70c | 467 | |
91447636 | 468 | if (cp->c_blocks - VTOF(vp)->ff_blocks) { |
2d21ac55 | 469 | error = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE); |
91447636 A |
470 | if (error) { |
471 | goto out; | |
472 | } | |
473 | ||
474 | rcp = VTOC(rvp); | |
475 | if (rcp && rcp->c_rsrcfork) { | |
476 | total_size += rcp->c_rsrcfork->ff_size; | |
477 | } | |
478 | } | |
9bccf70c | 479 | |
91447636 | 480 | VATTR_RETURN(vap, va_total_size, total_size); |
91447636 | 481 | } |
9bccf70c | 482 | } |
91447636 A |
483 | if (VATTR_IS_ACTIVE(vap, va_total_alloc)) { |
484 | if (v_type == VDIR) { | |
485 | VATTR_RETURN(vap, va_total_alloc, 0); | |
486 | } else { | |
2d21ac55 | 487 | VATTR_RETURN(vap, va_total_alloc, (u_int64_t)cp->c_blocks * (u_int64_t)hfsmp->blockSize); |
91447636 | 488 | } |
9bccf70c A |
489 | } |
490 | ||
9bccf70c | 491 | /* |
91447636 A |
492 | * If the VFS wants extended security data, and we know that we |
493 | * don't have any (because it never told us it was setting any) | |
494 | * then we can return the supported bit and no data. If we do | |
495 | * have extended security, we can just leave the bit alone and | |
496 | * the VFS will use the fallback path to fetch it. | |
9bccf70c | 497 | */ |
91447636 A |
498 | if (VATTR_IS_ACTIVE(vap, va_acl)) { |
499 | if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) { | |
2d21ac55 | 500 | vap->va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE; |
91447636 A |
501 | VATTR_SET_SUPPORTED(vap, va_acl); |
502 | } | |
55e303ae | 503 | } |
91447636 | 504 | if (VATTR_IS_ACTIVE(vap, va_access_time)) { |
2d21ac55 | 505 | /* Access times are lazily updated, get current time if needed */ |
91447636 A |
506 | if (cp->c_touch_acctime) { |
507 | struct timeval tv; | |
508 | ||
509 | microtime(&tv); | |
510 | vap->va_access_time.tv_sec = tv.tv_sec; | |
511 | } else { | |
512 | vap->va_access_time.tv_sec = cp->c_atime; | |
513 | } | |
514 | vap->va_access_time.tv_nsec = 0; | |
515 | VATTR_SET_SUPPORTED(vap, va_access_time); | |
516 | } | |
2d21ac55 A |
517 | vap->va_create_time.tv_sec = cp->c_itime; |
518 | vap->va_create_time.tv_nsec = 0; | |
91447636 A |
519 | vap->va_modify_time.tv_sec = cp->c_mtime; |
520 | vap->va_modify_time.tv_nsec = 0; | |
91447636 A |
521 | vap->va_change_time.tv_sec = cp->c_ctime; |
522 | vap->va_change_time.tv_nsec = 0; | |
91447636 | 523 | vap->va_backup_time.tv_sec = cp->c_btime; |
2d21ac55 A |
524 | vap->va_backup_time.tv_nsec = 0; |
525 | ||
526 | /* XXX is this really a good 'optimal I/O size'? */ | |
527 | vap->va_iosize = hfsmp->hfs_logBlockSize; | |
528 | vap->va_uid = cp->c_uid; | |
529 | vap->va_gid = cp->c_gid; | |
530 | vap->va_mode = cp->c_mode; | |
531 | vap->va_flags = cp->c_flags; | |
91447636 | 532 | |
9bccf70c A |
533 | /* |
534 | * Exporting file IDs from HFS Plus: | |
535 | * | |
536 | * For "normal" files the c_fileid is the same value as the | |
537 | * c_cnid. But for hard link files, they are different - the | |
538 | * c_cnid belongs to the active directory entry (ie the link) | |
539 | * and the c_fileid is for the actual inode (ie the data file). | |
540 | * | |
91447636 A |
541 | * The stat call (getattr) uses va_fileid and the Carbon APIs, |
542 | * which are hardlink-ignorant, will ask for va_linkid. | |
9bccf70c | 543 | */ |
2d21ac55 A |
544 | vap->va_fileid = (u_int64_t)cp->c_fileid; |
545 | /* Hardlinked directories have multiple cnids and parents (one per link). */ | |
546 | if ((v_type == VDIR) && (cp->c_flag & C_HARDLINK)) { | |
547 | vap->va_linkid = (u_int64_t)hfs_currentcnid(cp); | |
548 | vap->va_parentid = (u_int64_t)hfs_currentparent(cp); | |
549 | } else { | |
550 | vap->va_linkid = (u_int64_t)cp->c_cnid; | |
551 | vap->va_parentid = (u_int64_t)cp->c_parentcnid; | |
552 | } | |
553 | vap->va_fsid = cp->c_dev; | |
554 | vap->va_filerev = 0; | |
555 | vap->va_encoding = cp->c_encoding; | |
556 | vap->va_rdev = (v_type == VBLK || v_type == VCHR) ? cp->c_rdev : 0; | |
557 | vap->va_data_size = data_size; | |
558 | ||
559 | /* Mark them all at once instead of individual VATTR_SET_SUPPORTED calls. */ | |
560 | vap->va_supported |= VNODE_ATTR_va_create_time | VNODE_ATTR_va_modify_time | | |
561 | VNODE_ATTR_va_change_time| VNODE_ATTR_va_backup_time | | |
562 | VNODE_ATTR_va_iosize | VNODE_ATTR_va_uid | | |
563 | VNODE_ATTR_va_gid | VNODE_ATTR_va_mode | | |
564 | VNODE_ATTR_va_flags |VNODE_ATTR_va_fileid | | |
565 | VNODE_ATTR_va_linkid | VNODE_ATTR_va_parentid | | |
566 | VNODE_ATTR_va_fsid | VNODE_ATTR_va_filerev | | |
567 | VNODE_ATTR_va_encoding | VNODE_ATTR_va_rdev | | |
568 | VNODE_ATTR_va_data_size; | |
91447636 A |
569 | |
570 | /* if this is the root, let VFS to find out the mount name, which may be different from the real name */ | |
2d21ac55 | 571 | if (VATTR_IS_ACTIVE(vap, va_name) && (cp->c_cnid != kHFSRootFolderID)) { |
91447636 A |
572 | /* Return the name for ATTR_CMN_NAME */ |
573 | if (cp->c_desc.cd_namelen == 0) { | |
2d21ac55 A |
574 | if ((cp->c_flag & C_HARDLINK) && ((cp->c_flag & C_DELETED) == 0 || (cp->c_linkcount > 1))) { |
575 | cnid_t nextlinkid; | |
576 | cnid_t prevlinkid; | |
577 | struct vnode *file_vp; | |
578 | ||
579 | if ((error = hfs_lookuplink(hfsmp, cp->c_fileid, &prevlinkid, &nextlinkid))) { | |
580 | goto out; | |
581 | } | |
582 | ||
583 | // | |
584 | // don't bother trying to get a linkid that's the same | |
585 | // as the current cnid | |
586 | // | |
587 | if (nextlinkid == VTOC(vp)->c_cnid) { | |
588 | if (prevlinkid == VTOC(vp)->c_cnid) { | |
589 | hfs_unlock(cp); | |
590 | goto out2; | |
591 | } else { | |
592 | nextlinkid = prevlinkid; | |
593 | } | |
594 | } | |
595 | ||
596 | hfs_unlock(cp); | |
597 | ||
598 | if (nextlinkid == 0 || (error = hfs_vget(hfsmp, nextlinkid, &file_vp, 1))) { | |
599 | if (prevlinkid == 0 || (error = hfs_vget(hfsmp, prevlinkid, &file_vp, 1))) { | |
600 | goto out2; | |
601 | } | |
602 | } | |
603 | ||
604 | cp = VTOC(file_vp); | |
605 | if (hfs_lock(cp, HFS_SHARED_LOCK) == 0) { | |
606 | if (cp->c_desc.cd_namelen) { | |
607 | strlcpy(vap->va_name, (const char *)cp->c_desc.cd_nameptr, MAXPATHLEN); | |
608 | } | |
609 | hfs_unlock(cp); | |
610 | vnode_put(file_vp); | |
611 | goto out2; | |
612 | } | |
613 | ||
614 | if (vnode_name(file_vp)) { | |
615 | strlcpy(vap->va_name, vnode_name(file_vp), MAXPATHLEN); | |
616 | } else { | |
617 | error = ENOENT; | |
618 | } | |
619 | vnode_put(file_vp); | |
620 | goto out2; | |
621 | } else { | |
91447636 A |
622 | error = ENOENT; |
623 | goto out; | |
2d21ac55 A |
624 | } |
625 | } else { | |
626 | strlcpy(vap->va_name, (const char *)cp->c_desc.cd_nameptr, MAXPATHLEN); | |
627 | VATTR_SET_SUPPORTED(vap, va_name); | |
91447636 | 628 | } |
9bccf70c | 629 | } |
9bccf70c | 630 | |
91447636 A |
631 | out: |
632 | hfs_unlock(cp); | |
2d21ac55 | 633 | out2: |
91447636 A |
634 | if (rvp) { |
635 | vnode_put(rvp); | |
636 | } | |
637 | return (error); | |
638 | } | |
9bccf70c A |
639 | |
640 | static int | |
91447636 A |
641 | hfs_vnop_setattr(ap) |
642 | struct vnop_setattr_args /* { | |
9bccf70c | 643 | struct vnode *a_vp; |
91447636 A |
644 | struct vnode_attr *a_vap; |
645 | vfs_context_t a_context; | |
9bccf70c A |
646 | } */ *ap; |
647 | { | |
91447636 | 648 | struct vnode_attr *vap = ap->a_vap; |
9bccf70c | 649 | struct vnode *vp = ap->a_vp; |
91447636 A |
650 | struct cnode *cp = NULL; |
651 | struct hfsmount *hfsmp; | |
652 | kauth_cred_t cred = vfs_context_ucred(ap->a_context); | |
653 | struct proc *p = vfs_context_proc(ap->a_context); | |
654 | int error = 0; | |
655 | uid_t nuid; | |
656 | gid_t ngid; | |
9bccf70c | 657 | |
91447636 | 658 | hfsmp = VTOHFS(vp); |
9bccf70c | 659 | |
91447636 A |
660 | /* Don't allow modification of the journal file. */ |
661 | if (hfsmp->hfs_jnlfileid == VTOC(vp)->c_fileid) { | |
662 | return (EPERM); | |
55e303ae A |
663 | } |
664 | ||
91447636 A |
665 | /* |
666 | * File size change request. | |
667 | * We are guaranteed that this is not a directory, and that | |
668 | * the filesystem object is writeable. | |
669 | */ | |
670 | VATTR_SET_SUPPORTED(vap, va_data_size); | |
671 | if (VATTR_IS_ACTIVE(vap, va_data_size) && !vnode_islnk(vp)) { | |
55e303ae | 672 | |
91447636 A |
673 | /* Take truncate lock before taking cnode lock. */ |
674 | hfs_lock_truncate(VTOC(vp), TRUE); | |
2d21ac55 A |
675 | |
676 | /* Perform the ubc_setsize before taking the cnode lock. */ | |
677 | ubc_setsize(vp, vap->va_data_size); | |
678 | ||
91447636 | 679 | if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) { |
2d21ac55 | 680 | hfs_unlock_truncate(VTOC(vp), TRUE); |
91447636 A |
681 | return (error); |
682 | } | |
683 | cp = VTOC(vp); | |
55e303ae | 684 | |
2d21ac55 | 685 | error = hfs_truncate(vp, vap->va_data_size, vap->va_vaflags & 0xffff, 1, ap->a_context); |
91447636 | 686 | |
2d21ac55 | 687 | hfs_unlock_truncate(cp, TRUE); |
91447636 A |
688 | if (error) |
689 | goto out; | |
690 | } | |
691 | if (cp == NULL) { | |
692 | if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) | |
9bccf70c | 693 | return (error); |
91447636 | 694 | cp = VTOC(vp); |
9bccf70c A |
695 | } |
696 | ||
2d21ac55 A |
697 | /* |
698 | * If it is just an access time update request by itself | |
699 | * we know the request is from kernel level code, and we | |
700 | * can delay it without being as worried about consistency. | |
701 | * This change speeds up mmaps, in the rare case that they | |
702 | * get caught behind a sync. | |
703 | */ | |
704 | ||
705 | if (vap->va_active == VNODE_ATTR_va_access_time) { | |
706 | cp->c_touch_acctime=TRUE; | |
707 | goto out; | |
708 | } | |
709 | ||
710 | ||
711 | ||
91447636 A |
712 | /* |
713 | * Owner/group change request. | |
714 | * We are guaranteed that the new owner/group is valid and legal. | |
715 | */ | |
716 | VATTR_SET_SUPPORTED(vap, va_uid); | |
717 | VATTR_SET_SUPPORTED(vap, va_gid); | |
718 | nuid = VATTR_IS_ACTIVE(vap, va_uid) ? vap->va_uid : (uid_t)VNOVAL; | |
719 | ngid = VATTR_IS_ACTIVE(vap, va_gid) ? vap->va_gid : (gid_t)VNOVAL; | |
720 | if (((nuid != (uid_t)VNOVAL) || (ngid != (gid_t)VNOVAL)) && | |
721 | ((error = hfs_chown(vp, nuid, ngid, cred, p)) != 0)) | |
722 | goto out; | |
b4c24cb9 | 723 | |
91447636 A |
724 | /* |
725 | * Mode change request. | |
726 | * We are guaranteed that the mode value is valid and that in | |
727 | * conjunction with the owner and group, this change is legal. | |
728 | */ | |
729 | VATTR_SET_SUPPORTED(vap, va_mode); | |
730 | if (VATTR_IS_ACTIVE(vap, va_mode) && | |
731 | ((error = hfs_chmod(vp, (int)vap->va_mode, cred, p)) != 0)) | |
732 | goto out; | |
b4c24cb9 | 733 | |
91447636 A |
734 | /* |
735 | * File flags change. | |
736 | * We are guaranteed that only flags allowed to change given the | |
737 | * current securelevel are being changed. | |
738 | */ | |
739 | VATTR_SET_SUPPORTED(vap, va_flags); | |
cc9f6e38 | 740 | if (VATTR_IS_ACTIVE(vap, va_flags)) { |
2d21ac55 A |
741 | u_int16_t *fdFlags; |
742 | ||
cc9f6e38 A |
743 | cp->c_flags = vap->va_flags; |
744 | cp->c_touch_chgtime = TRUE; | |
2d21ac55 A |
745 | |
746 | /* | |
747 | * Mirror the UF_HIDDEN flag to the invisible bit of the Finder Info. | |
748 | * | |
749 | * The fdFlags for files and frFlags for folders are both 8 bytes | |
750 | * into the userInfo (the first 16 bytes of the Finder Info). They | |
751 | * are both 16-bit fields. | |
752 | */ | |
753 | fdFlags = (u_int16_t *) &cp->c_finderinfo[8]; | |
754 | if (vap->va_flags & UF_HIDDEN) | |
755 | *fdFlags |= OSSwapHostToBigConstInt16(kFinderInvisibleMask); | |
756 | else | |
757 | *fdFlags &= ~OSSwapHostToBigConstInt16(kFinderInvisibleMask); | |
9bccf70c | 758 | } |
91447636 A |
759 | |
760 | /* | |
761 | * Timestamp updates. | |
762 | */ | |
763 | VATTR_SET_SUPPORTED(vap, va_create_time); | |
764 | VATTR_SET_SUPPORTED(vap, va_access_time); | |
765 | VATTR_SET_SUPPORTED(vap, va_modify_time); | |
766 | VATTR_SET_SUPPORTED(vap, va_backup_time); | |
767 | VATTR_SET_SUPPORTED(vap, va_change_time); | |
768 | if (VATTR_IS_ACTIVE(vap, va_create_time) || | |
769 | VATTR_IS_ACTIVE(vap, va_access_time) || | |
770 | VATTR_IS_ACTIVE(vap, va_modify_time) || | |
771 | VATTR_IS_ACTIVE(vap, va_backup_time)) { | |
91447636 A |
772 | if (VATTR_IS_ACTIVE(vap, va_create_time)) |
773 | cp->c_itime = vap->va_create_time.tv_sec; | |
774 | if (VATTR_IS_ACTIVE(vap, va_access_time)) { | |
775 | cp->c_atime = vap->va_access_time.tv_sec; | |
776 | cp->c_touch_acctime = FALSE; | |
9bccf70c | 777 | } |
91447636 A |
778 | if (VATTR_IS_ACTIVE(vap, va_modify_time)) { |
779 | cp->c_mtime = vap->va_modify_time.tv_sec; | |
780 | cp->c_touch_modtime = FALSE; | |
781 | cp->c_touch_chgtime = TRUE; | |
782 | ||
9bccf70c A |
783 | /* |
784 | * The utimes system call can reset the modification | |
785 | * time but it doesn't know about HFS create times. | |
91447636 | 786 | * So we need to ensure that the creation time is |
9bccf70c A |
787 | * always at least as old as the modification time. |
788 | */ | |
789 | if ((VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) && | |
91447636 A |
790 | (cp->c_cnid != kHFSRootFolderID) && |
791 | (cp->c_mtime < cp->c_itime)) { | |
792 | cp->c_itime = cp->c_mtime; | |
9bccf70c A |
793 | } |
794 | } | |
91447636 A |
795 | if (VATTR_IS_ACTIVE(vap, va_backup_time)) |
796 | cp->c_btime = vap->va_backup_time.tv_sec; | |
797 | cp->c_flag |= C_MODIFIED; | |
9bccf70c | 798 | } |
91447636 A |
799 | |
800 | /* | |
801 | * Set name encoding. | |
802 | */ | |
803 | VATTR_SET_SUPPORTED(vap, va_encoding); | |
804 | if (VATTR_IS_ACTIVE(vap, va_encoding)) { | |
805 | cp->c_encoding = vap->va_encoding; | |
806 | hfs_setencodingbits(hfsmp, cp->c_encoding); | |
9bccf70c | 807 | } |
91447636 | 808 | |
91447636 | 809 | if ((error = hfs_update(vp, TRUE)) != 0) |
2d21ac55 | 810 | goto out; |
55e303ae | 811 | HFS_KNOTE(vp, NOTE_ATTRIB); |
91447636 A |
812 | out: |
813 | if (cp) | |
814 | hfs_unlock(cp); | |
9bccf70c A |
815 | return (error); |
816 | } | |
817 | ||
818 | ||
819 | /* | |
820 | * Change the mode on a file. | |
821 | * cnode must be locked before calling. | |
822 | */ | |
55e303ae | 823 | __private_extern__ |
9bccf70c | 824 | int |
2d21ac55 | 825 | hfs_chmod(struct vnode *vp, int mode, __unused kauth_cred_t cred, __unused struct proc *p) |
9bccf70c A |
826 | { |
827 | register struct cnode *cp = VTOC(vp); | |
9bccf70c A |
828 | |
829 | if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord) | |
830 | return (0); | |
831 | ||
b4c24cb9 A |
832 | // XXXdbg - don't allow modification of the journal or journal_info_block |
833 | if (VTOHFS(vp)->jnl && cp && cp->c_datafork) { | |
834 | struct HFSPlusExtentDescriptor *extd; | |
835 | ||
55e303ae | 836 | extd = &cp->c_datafork->ff_extents[0]; |
b4c24cb9 A |
837 | if (extd->startBlock == VTOVCB(vp)->vcbJinfoBlock || extd->startBlock == VTOHFS(vp)->jnl_start) { |
838 | return EPERM; | |
839 | } | |
840 | } | |
841 | ||
9bccf70c | 842 | #if OVERRIDE_UNKNOWN_PERMISSIONS |
91447636 | 843 | if (((unsigned int)vfs_flags(VTOVFS(vp))) & MNT_UNKNOWNPERMISSIONS) { |
9bccf70c A |
844 | return (0); |
845 | }; | |
846 | #endif | |
9bccf70c A |
847 | cp->c_mode &= ~ALLPERMS; |
848 | cp->c_mode |= (mode & ALLPERMS); | |
91447636 | 849 | cp->c_touch_chgtime = TRUE; |
9bccf70c A |
850 | return (0); |
851 | } | |
852 | ||
853 | ||
55e303ae | 854 | __private_extern__ |
9bccf70c | 855 | int |
91447636 | 856 | hfs_write_access(struct vnode *vp, kauth_cred_t cred, struct proc *p, Boolean considerFlags) |
9bccf70c A |
857 | { |
858 | struct cnode *cp = VTOC(vp); | |
9bccf70c | 859 | int retval = 0; |
91447636 | 860 | int is_member; |
9bccf70c A |
861 | |
862 | /* | |
863 | * Disallow write attempts on read-only file systems; | |
864 | * unless the file is a socket, fifo, or a block or | |
865 | * character device resident on the file system. | |
866 | */ | |
91447636 | 867 | switch (vnode_vtype(vp)) { |
9bccf70c A |
868 | case VDIR: |
869 | case VLNK: | |
870 | case VREG: | |
55e303ae | 871 | if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY) |
9bccf70c | 872 | return (EROFS); |
55e303ae | 873 | break; |
9bccf70c A |
874 | default: |
875 | break; | |
876 | } | |
877 | ||
878 | /* If immutable bit set, nobody gets to write it. */ | |
879 | if (considerFlags && (cp->c_flags & IMMUTABLE)) | |
880 | return (EPERM); | |
881 | ||
882 | /* Otherwise, user id 0 always gets access. */ | |
91447636 | 883 | if (!suser(cred, NULL)) |
9bccf70c A |
884 | return (0); |
885 | ||
886 | /* Otherwise, check the owner. */ | |
887 | if ((retval = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, false)) == 0) | |
888 | return ((cp->c_mode & S_IWUSR) == S_IWUSR ? 0 : EACCES); | |
889 | ||
890 | /* Otherwise, check the groups. */ | |
91447636 A |
891 | if (kauth_cred_ismember_gid(cred, cp->c_gid, &is_member) == 0 && is_member) { |
892 | return ((cp->c_mode & S_IWGRP) == S_IWGRP ? 0 : EACCES); | |
9bccf70c A |
893 | } |
894 | ||
895 | /* Otherwise, check everyone else. */ | |
896 | return ((cp->c_mode & S_IWOTH) == S_IWOTH ? 0 : EACCES); | |
897 | } | |
898 | ||
899 | ||
9bccf70c A |
900 | /* |
901 | * Perform chown operation on cnode cp; | |
902 | * code must be locked prior to call. | |
903 | */ | |
55e303ae | 904 | __private_extern__ |
9bccf70c | 905 | int |
2d21ac55 A |
906 | #if !QUOTA |
907 | hfs_chown(struct vnode *vp, uid_t uid, gid_t gid, __unused kauth_cred_t cred, | |
908 | __unused struct proc *p) | |
909 | #else | |
91447636 | 910 | hfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred, |
2d21ac55 A |
911 | __unused struct proc *p) |
912 | #endif | |
9bccf70c A |
913 | { |
914 | register struct cnode *cp = VTOC(vp); | |
915 | uid_t ouid; | |
916 | gid_t ogid; | |
9bccf70c | 917 | #if QUOTA |
2d21ac55 | 918 | int error = 0; |
9bccf70c A |
919 | register int i; |
920 | int64_t change; | |
921 | #endif /* QUOTA */ | |
922 | ||
923 | if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord) | |
91447636 | 924 | return (ENOTSUP); |
9bccf70c | 925 | |
91447636 | 926 | if (((unsigned int)vfs_flags(VTOVFS(vp))) & MNT_UNKNOWNPERMISSIONS) |
9bccf70c A |
927 | return (0); |
928 | ||
929 | if (uid == (uid_t)VNOVAL) | |
930 | uid = cp->c_uid; | |
931 | if (gid == (gid_t)VNOVAL) | |
932 | gid = cp->c_gid; | |
91447636 A |
933 | |
934 | #if 0 /* we are guaranteed that this is already the case */ | |
9bccf70c A |
935 | /* |
936 | * If we don't own the file, are trying to change the owner | |
937 | * of the file, or are not a member of the target group, | |
938 | * the caller must be superuser or the call fails. | |
939 | */ | |
91447636 A |
940 | if ((kauth_cred_getuid(cred) != cp->c_uid || uid != cp->c_uid || |
941 | (gid != cp->c_gid && | |
942 | (kauth_cred_ismember_gid(cred, gid, &is_member) || !is_member))) && | |
943 | (error = suser(cred, 0))) | |
9bccf70c | 944 | return (error); |
91447636 | 945 | #endif |
9bccf70c A |
946 | |
947 | ogid = cp->c_gid; | |
948 | ouid = cp->c_uid; | |
949 | #if QUOTA | |
950 | if ((error = hfs_getinoquota(cp))) | |
951 | return (error); | |
952 | if (ouid == uid) { | |
91447636 | 953 | dqrele(cp->c_dquot[USRQUOTA]); |
9bccf70c A |
954 | cp->c_dquot[USRQUOTA] = NODQUOT; |
955 | } | |
956 | if (ogid == gid) { | |
91447636 | 957 | dqrele(cp->c_dquot[GRPQUOTA]); |
9bccf70c A |
958 | cp->c_dquot[GRPQUOTA] = NODQUOT; |
959 | } | |
960 | ||
961 | /* | |
962 | * Eventually need to account for (fake) a block per directory | |
91447636 A |
963 | * if (vnode_isdir(vp)) |
964 | * change = VTOHFS(vp)->blockSize; | |
965 | * else | |
9bccf70c A |
966 | */ |
967 | ||
968 | change = (int64_t)(cp->c_blocks) * (int64_t)VTOVCB(vp)->blockSize; | |
969 | (void) hfs_chkdq(cp, -change, cred, CHOWN); | |
970 | (void) hfs_chkiq(cp, -1, cred, CHOWN); | |
971 | for (i = 0; i < MAXQUOTAS; i++) { | |
91447636 | 972 | dqrele(cp->c_dquot[i]); |
9bccf70c A |
973 | cp->c_dquot[i] = NODQUOT; |
974 | } | |
975 | #endif /* QUOTA */ | |
976 | cp->c_gid = gid; | |
977 | cp->c_uid = uid; | |
978 | #if QUOTA | |
979 | if ((error = hfs_getinoquota(cp)) == 0) { | |
980 | if (ouid == uid) { | |
91447636 | 981 | dqrele(cp->c_dquot[USRQUOTA]); |
9bccf70c A |
982 | cp->c_dquot[USRQUOTA] = NODQUOT; |
983 | } | |
984 | if (ogid == gid) { | |
91447636 | 985 | dqrele(cp->c_dquot[GRPQUOTA]); |
9bccf70c A |
986 | cp->c_dquot[GRPQUOTA] = NODQUOT; |
987 | } | |
988 | if ((error = hfs_chkdq(cp, change, cred, CHOWN)) == 0) { | |
989 | if ((error = hfs_chkiq(cp, 1, cred, CHOWN)) == 0) | |
990 | goto good; | |
991 | else | |
992 | (void) hfs_chkdq(cp, -change, cred, CHOWN|FORCE); | |
993 | } | |
994 | for (i = 0; i < MAXQUOTAS; i++) { | |
91447636 | 995 | dqrele(cp->c_dquot[i]); |
9bccf70c A |
996 | cp->c_dquot[i] = NODQUOT; |
997 | } | |
998 | } | |
999 | cp->c_gid = ogid; | |
1000 | cp->c_uid = ouid; | |
1001 | if (hfs_getinoquota(cp) == 0) { | |
1002 | if (ouid == uid) { | |
91447636 | 1003 | dqrele(cp->c_dquot[USRQUOTA]); |
9bccf70c A |
1004 | cp->c_dquot[USRQUOTA] = NODQUOT; |
1005 | } | |
1006 | if (ogid == gid) { | |
91447636 | 1007 | dqrele(cp->c_dquot[GRPQUOTA]); |
9bccf70c A |
1008 | cp->c_dquot[GRPQUOTA] = NODQUOT; |
1009 | } | |
1010 | (void) hfs_chkdq(cp, change, cred, FORCE|CHOWN); | |
1011 | (void) hfs_chkiq(cp, 1, cred, FORCE|CHOWN); | |
1012 | (void) hfs_getinoquota(cp); | |
1013 | } | |
1014 | return (error); | |
1015 | good: | |
1016 | if (hfs_getinoquota(cp)) | |
1017 | panic("hfs_chown: lost quota"); | |
1018 | #endif /* QUOTA */ | |
1019 | ||
2d21ac55 A |
1020 | |
1021 | /* | |
1022 | According to the SUSv3 Standard, chown() shall mark | |
1023 | for update the st_ctime field of the file. | |
1024 | (No exceptions mentioned) | |
1025 | */ | |
91447636 | 1026 | cp->c_touch_chgtime = TRUE; |
9bccf70c A |
1027 | return (0); |
1028 | } | |
1029 | ||
1030 | ||
1031 | /* | |
91447636 A |
1032 | * The hfs_exchange routine swaps the fork data in two files by |
1033 | * exchanging some of the information in the cnode. It is used | |
1034 | * to preserve the file ID when updating an existing file, in | |
1035 | * case the file is being tracked through its file ID. Typically | |
1036 | * its used after creating a new file during a safe-save. | |
9bccf70c | 1037 | */ |
9bccf70c | 1038 | static int |
91447636 A |
1039 | hfs_vnop_exchange(ap) |
1040 | struct vnop_exchange_args /* { | |
9bccf70c A |
1041 | struct vnode *a_fvp; |
1042 | struct vnode *a_tvp; | |
91447636 A |
1043 | int a_options; |
1044 | vfs_context_t a_context; | |
9bccf70c A |
1045 | } */ *ap; |
1046 | { | |
1047 | struct vnode *from_vp = ap->a_fvp; | |
1048 | struct vnode *to_vp = ap->a_tvp; | |
91447636 A |
1049 | struct cnode *from_cp; |
1050 | struct cnode *to_cp; | |
1051 | struct hfsmount *hfsmp; | |
9bccf70c A |
1052 | struct cat_desc tempdesc; |
1053 | struct cat_attr tempattr; | |
2d21ac55 A |
1054 | const unsigned char *from_nameptr; |
1055 | const unsigned char *to_nameptr; | |
1056 | char from_iname[32]; | |
1057 | char to_iname[32]; | |
1058 | u_int32_t tempflag; | |
1059 | cnid_t from_parid; | |
1060 | cnid_t to_parid; | |
91447636 A |
1061 | int lockflags; |
1062 | int error = 0, started_tr = 0, got_cookie = 0; | |
1063 | cat_cookie_t cookie; | |
9bccf70c A |
1064 | |
1065 | /* The files must be on the same volume. */ | |
91447636 | 1066 | if (vnode_mount(from_vp) != vnode_mount(to_vp)) |
9bccf70c A |
1067 | return (EXDEV); |
1068 | ||
91447636 A |
1069 | if (from_vp == to_vp) |
1070 | return (EINVAL); | |
1071 | ||
1072 | if ((error = hfs_lockpair(VTOC(from_vp), VTOC(to_vp), HFS_EXCLUSIVE_LOCK))) | |
1073 | return (error); | |
1074 | ||
1075 | from_cp = VTOC(from_vp); | |
1076 | to_cp = VTOC(to_vp); | |
1077 | hfsmp = VTOHFS(from_vp); | |
1078 | ||
9bccf70c | 1079 | /* Only normal files can be exchanged. */ |
91447636 | 1080 | if (!vnode_isreg(from_vp) || !vnode_isreg(to_vp) || |
91447636 A |
1081 | VNODE_IS_RSRC(from_vp) || VNODE_IS_RSRC(to_vp)) { |
1082 | error = EINVAL; | |
1083 | goto exit; | |
1084 | } | |
9bccf70c | 1085 | |
b4c24cb9 A |
1086 | // XXXdbg - don't allow modification of the journal or journal_info_block |
1087 | if (hfsmp->jnl) { | |
1088 | struct HFSPlusExtentDescriptor *extd; | |
1089 | ||
1090 | if (from_cp->c_datafork) { | |
55e303ae | 1091 | extd = &from_cp->c_datafork->ff_extents[0]; |
b4c24cb9 | 1092 | if (extd->startBlock == VTOVCB(from_vp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) { |
91447636 A |
1093 | error = EPERM; |
1094 | goto exit; | |
b4c24cb9 A |
1095 | } |
1096 | } | |
1097 | ||
1098 | if (to_cp->c_datafork) { | |
55e303ae | 1099 | extd = &to_cp->c_datafork->ff_extents[0]; |
b4c24cb9 | 1100 | if (extd->startBlock == VTOVCB(to_vp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) { |
91447636 A |
1101 | error = EPERM; |
1102 | goto exit; | |
b4c24cb9 A |
1103 | } |
1104 | } | |
1105 | } | |
1106 | ||
91447636 A |
1107 | if ((error = hfs_start_transaction(hfsmp)) != 0) { |
1108 | goto exit; | |
b4c24cb9 | 1109 | } |
91447636 | 1110 | started_tr = 1; |
b4c24cb9 | 1111 | |
55e303ae A |
1112 | /* |
1113 | * Reserve some space in the Catalog file. | |
1114 | */ | |
91447636 A |
1115 | if ((error = cat_preflight(hfsmp, CAT_EXCHANGE, &cookie, vfs_context_proc(ap->a_context)))) { |
1116 | goto exit; | |
55e303ae | 1117 | } |
91447636 | 1118 | got_cookie = 1; |
9bccf70c A |
1119 | |
1120 | /* The backend code always tries to delete the virtual | |
2d21ac55 | 1121 | * extent id for exchanging files so we need to lock |
9bccf70c A |
1122 | * the extents b-tree. |
1123 | */ | |
91447636 | 1124 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK); |
9bccf70c | 1125 | |
2d21ac55 A |
1126 | /* Account for the location of the catalog objects. */ |
1127 | if (from_cp->c_flag & C_HARDLINK) { | |
1128 | MAKE_INODE_NAME(from_iname, sizeof(from_iname), | |
1129 | from_cp->c_attr.ca_linkref); | |
1130 | from_nameptr = (unsigned char *)from_iname; | |
1131 | from_parid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid; | |
1132 | from_cp->c_hint = 0; | |
1133 | } else { | |
1134 | from_nameptr = from_cp->c_desc.cd_nameptr; | |
1135 | from_parid = from_cp->c_parentcnid; | |
1136 | } | |
1137 | if (to_cp->c_flag & C_HARDLINK) { | |
1138 | MAKE_INODE_NAME(to_iname, sizeof(to_iname), | |
1139 | to_cp->c_attr.ca_linkref); | |
1140 | to_nameptr = (unsigned char *)to_iname; | |
1141 | to_parid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid; | |
1142 | to_cp->c_hint = 0; | |
1143 | } else { | |
1144 | to_nameptr = to_cp->c_desc.cd_nameptr; | |
1145 | to_parid = to_cp->c_parentcnid; | |
1146 | } | |
1147 | ||
9bccf70c | 1148 | /* Do the exchange */ |
2d21ac55 A |
1149 | error = ExchangeFileIDs(hfsmp, from_nameptr, to_nameptr, from_parid, |
1150 | to_parid, from_cp->c_hint, to_cp->c_hint); | |
91447636 | 1151 | hfs_systemfile_unlock(hfsmp, lockflags); |
9bccf70c | 1152 | |
91447636 A |
1153 | /* |
1154 | * Note that we don't need to exchange any extended attributes | |
1155 | * since the attributes are keyed by file ID. | |
1156 | */ | |
9bccf70c A |
1157 | |
1158 | if (error != E_NONE) { | |
91447636 A |
1159 | error = MacToVFSError(error); |
1160 | goto exit; | |
9bccf70c A |
1161 | } |
1162 | ||
1163 | /* Purge the vnodes from the name cache */ | |
1164 | if (from_vp) | |
1165 | cache_purge(from_vp); | |
1166 | if (to_vp) | |
1167 | cache_purge(to_vp); | |
1168 | ||
1169 | /* Save a copy of from attributes before swapping. */ | |
1170 | bcopy(&from_cp->c_desc, &tempdesc, sizeof(struct cat_desc)); | |
1171 | bcopy(&from_cp->c_attr, &tempattr, sizeof(struct cat_attr)); | |
2d21ac55 | 1172 | tempflag = from_cp->c_flag & (C_HARDLINK | C_HASXATTRS); |
9bccf70c A |
1173 | |
1174 | /* | |
1175 | * Swap the descriptors and all non-fork related attributes. | |
1176 | * (except the modify date) | |
1177 | */ | |
1178 | bcopy(&to_cp->c_desc, &from_cp->c_desc, sizeof(struct cat_desc)); | |
b4c24cb9 | 1179 | |
9bccf70c A |
1180 | from_cp->c_hint = 0; |
1181 | from_cp->c_fileid = from_cp->c_cnid; | |
1182 | from_cp->c_itime = to_cp->c_itime; | |
1183 | from_cp->c_btime = to_cp->c_btime; | |
1184 | from_cp->c_atime = to_cp->c_atime; | |
1185 | from_cp->c_ctime = to_cp->c_ctime; | |
1186 | from_cp->c_gid = to_cp->c_gid; | |
1187 | from_cp->c_uid = to_cp->c_uid; | |
1188 | from_cp->c_flags = to_cp->c_flags; | |
1189 | from_cp->c_mode = to_cp->c_mode; | |
2d21ac55 A |
1190 | from_cp->c_linkcount = to_cp->c_linkcount; |
1191 | from_cp->c_flag = to_cp->c_flag & (C_HARDLINK | C_HASXATTRS); | |
3a60a9f5 | 1192 | from_cp->c_attr.ca_recflags = to_cp->c_attr.ca_recflags; |
9bccf70c A |
1193 | bcopy(to_cp->c_finderinfo, from_cp->c_finderinfo, 32); |
1194 | ||
1195 | bcopy(&tempdesc, &to_cp->c_desc, sizeof(struct cat_desc)); | |
1196 | to_cp->c_hint = 0; | |
1197 | to_cp->c_fileid = to_cp->c_cnid; | |
1198 | to_cp->c_itime = tempattr.ca_itime; | |
1199 | to_cp->c_btime = tempattr.ca_btime; | |
1200 | to_cp->c_atime = tempattr.ca_atime; | |
1201 | to_cp->c_ctime = tempattr.ca_ctime; | |
1202 | to_cp->c_gid = tempattr.ca_gid; | |
1203 | to_cp->c_uid = tempattr.ca_uid; | |
1204 | to_cp->c_flags = tempattr.ca_flags; | |
1205 | to_cp->c_mode = tempattr.ca_mode; | |
2d21ac55 A |
1206 | to_cp->c_linkcount = tempattr.ca_linkcount; |
1207 | to_cp->c_flag = tempflag; | |
3a60a9f5 | 1208 | to_cp->c_attr.ca_recflags = tempattr.ca_recflags; |
9bccf70c A |
1209 | bcopy(tempattr.ca_finderinfo, to_cp->c_finderinfo, 32); |
1210 | ||
91447636 A |
1211 | /* Rehash the cnodes using their new file IDs */ |
1212 | hfs_chash_rehash(from_cp, to_cp); | |
90556fb8 A |
1213 | |
1214 | /* | |
1215 | * When a file moves out of "Cleanup At Startup" | |
1216 | * we can drop its NODUMP status. | |
1217 | */ | |
1218 | if ((from_cp->c_flags & UF_NODUMP) && | |
1219 | (from_cp->c_parentcnid != to_cp->c_parentcnid)) { | |
1220 | from_cp->c_flags &= ~UF_NODUMP; | |
91447636 | 1221 | from_cp->c_touch_chgtime = TRUE; |
90556fb8 | 1222 | } |
90556fb8 A |
1223 | if ((to_cp->c_flags & UF_NODUMP) && |
1224 | (to_cp->c_parentcnid != from_cp->c_parentcnid)) { | |
1225 | to_cp->c_flags &= ~UF_NODUMP; | |
91447636 | 1226 | to_cp->c_touch_chgtime = TRUE; |
90556fb8 A |
1227 | } |
1228 | ||
55e303ae A |
1229 | HFS_KNOTE(from_vp, NOTE_ATTRIB); |
1230 | HFS_KNOTE(to_vp, NOTE_ATTRIB); | |
1231 | ||
91447636 A |
1232 | exit: |
1233 | if (got_cookie) { | |
1234 | cat_postflight(hfsmp, &cookie, vfs_context_proc(ap->a_context)); | |
b4c24cb9 | 1235 | } |
91447636 A |
1236 | if (started_tr) { |
1237 | hfs_end_transaction(hfsmp); | |
b4c24cb9 A |
1238 | } |
1239 | ||
91447636 | 1240 | hfs_unlockpair(from_cp, to_cp); |
9bccf70c A |
1241 | return (error); |
1242 | } | |
1243 | ||
1244 | ||
1245 | /* | |
91447636 A |
1246 | * cnode must be locked |
1247 | */ | |
1248 | __private_extern__ | |
1249 | int | |
1250 | hfs_fsync(struct vnode *vp, int waitfor, int fullsync, struct proc *p) | |
9bccf70c | 1251 | { |
9bccf70c A |
1252 | struct cnode *cp = VTOC(vp); |
1253 | struct filefork *fp = NULL; | |
1254 | int retval = 0; | |
91447636 | 1255 | struct hfsmount *hfsmp = VTOHFS(vp); |
9bccf70c | 1256 | struct timeval tv; |
9bccf70c | 1257 | int wait; |
91447636 A |
1258 | int lockflag; |
1259 | int took_trunc_lock = 0; | |
9bccf70c | 1260 | |
91447636 | 1261 | wait = (waitfor == MNT_WAIT); |
2d21ac55 A |
1262 | if (always_do_fullfsync) |
1263 | fullsync = 1; | |
1264 | ||
9bccf70c | 1265 | /* HFS directories don't have any data blocks. */ |
91447636 | 1266 | if (vnode_isdir(vp)) |
9bccf70c A |
1267 | goto metasync; |
1268 | ||
1269 | /* | |
1270 | * For system files flush the B-tree header and | |
1271 | * for regular files write out any clusters | |
1272 | */ | |
91447636 | 1273 | if (vnode_issystem(vp)) { |
b4c24cb9 A |
1274 | if (VTOF(vp)->fcbBTCBPtr != NULL) { |
1275 | // XXXdbg | |
55e303ae | 1276 | if (hfsmp->jnl == NULL) { |
b4c24cb9 A |
1277 | BTFlushPath(VTOF(vp)); |
1278 | } | |
1279 | } | |
91447636 A |
1280 | } else if (UBCINFOEXISTS(vp)) { |
1281 | hfs_unlock(cp); | |
1282 | hfs_lock_truncate(cp, TRUE); | |
1283 | took_trunc_lock = 1; | |
9bccf70c | 1284 | |
91447636 | 1285 | /* Don't hold cnode lock when calling into cluster layer. */ |
2d21ac55 | 1286 | (void) cluster_push(vp, wait ? IO_SYNC : 0); |
91447636 A |
1287 | |
1288 | hfs_lock(cp, HFS_FORCE_LOCK); | |
1289 | } | |
9bccf70c A |
1290 | /* |
1291 | * When MNT_WAIT is requested and the zero fill timeout | |
1292 | * has expired then we must explicitly zero out any areas | |
1293 | * that are currently marked invalid (holes). | |
90556fb8 A |
1294 | * |
1295 | * Files with NODUMP can bypass zero filling here. | |
9bccf70c A |
1296 | */ |
1297 | if ((wait || (cp->c_flag & C_ZFWANTSYNC)) && | |
90556fb8 | 1298 | ((cp->c_flags & UF_NODUMP) == 0) && |
2d21ac55 | 1299 | UBCINFOEXISTS(vp) && (vnode_issystem(vp) ==0) && (fp = VTOF(vp)) && |
9bccf70c | 1300 | cp->c_zftimeout != 0) { |
91447636 | 1301 | microuptime(&tv); |
2d21ac55 | 1302 | if (!fullsync && tv.tv_sec < (long)cp->c_zftimeout) { |
9bccf70c A |
1303 | /* Remember that a force sync was requested. */ |
1304 | cp->c_flag |= C_ZFWANTSYNC; | |
91447636 A |
1305 | goto datasync; |
1306 | } | |
1307 | if (!took_trunc_lock) { | |
1308 | hfs_unlock(cp); | |
1309 | hfs_lock_truncate(cp, TRUE); | |
1310 | hfs_lock(cp, HFS_FORCE_LOCK); | |
1311 | took_trunc_lock = 1; | |
1312 | } | |
9bccf70c A |
1313 | |
1314 | while (!CIRCLEQ_EMPTY(&fp->ff_invalidranges)) { | |
1315 | struct rl_entry *invalid_range = CIRCLEQ_FIRST(&fp->ff_invalidranges); | |
1316 | off_t start = invalid_range->rl_start; | |
1317 | off_t end = invalid_range->rl_end; | |
1318 | ||
1319 | /* The range about to be written must be validated | |
91447636 | 1320 | * first, so that VNOP_BLOCKMAP() will return the |
9bccf70c A |
1321 | * appropriate mapping for the cluster code: |
1322 | */ | |
1323 | rl_remove(start, end, &fp->ff_invalidranges); | |
1324 | ||
91447636 A |
1325 | /* Don't hold cnode lock when calling into cluster layer. */ |
1326 | hfs_unlock(cp); | |
9bccf70c | 1327 | (void) cluster_write(vp, (struct uio *) 0, |
91447636 A |
1328 | fp->ff_size, end + 1, start, (off_t)0, |
1329 | IO_HEADZEROFILL | IO_NOZERODIRTY | IO_NOCACHE); | |
1330 | hfs_lock(cp, HFS_FORCE_LOCK); | |
9bccf70c A |
1331 | cp->c_flag |= C_MODIFIED; |
1332 | } | |
91447636 | 1333 | hfs_unlock(cp); |
2d21ac55 | 1334 | (void) cluster_push(vp, wait ? IO_SYNC : 0); |
91447636 A |
1335 | hfs_lock(cp, HFS_FORCE_LOCK); |
1336 | ||
9bccf70c A |
1337 | cp->c_flag &= ~C_ZFWANTSYNC; |
1338 | cp->c_zftimeout = 0; | |
1339 | } | |
91447636 A |
1340 | datasync: |
1341 | if (took_trunc_lock) | |
2d21ac55 | 1342 | hfs_unlock_truncate(cp, TRUE); |
91447636 A |
1343 | |
1344 | /* | |
1345 | * if we have a journal and if journal_active() returns != 0 then the | |
1346 | * we shouldn't do anything to a locked block (because it is part | |
1347 | * of a transaction). otherwise we'll just go through the normal | |
1348 | * code path and flush the buffer. note journal_active() can return | |
1349 | * -1 if the journal is invalid -- however we still need to skip any | |
1350 | * locked blocks as they get cleaned up when we finish the transaction | |
1351 | * or close the journal. | |
1352 | */ | |
1353 | // if (hfsmp->jnl && journal_active(hfsmp->jnl) >= 0) | |
1354 | if (hfsmp->jnl) | |
1355 | lockflag = BUF_SKIP_LOCKED; | |
1356 | else | |
1357 | lockflag = 0; | |
9bccf70c A |
1358 | |
1359 | /* | |
1360 | * Flush all dirty buffers associated with a vnode. | |
1361 | */ | |
91447636 | 1362 | buf_flushdirtyblks(vp, wait, lockflag, "hfs_fsync"); |
9bccf70c A |
1363 | |
1364 | metasync: | |
91447636 A |
1365 | if (vnode_isreg(vp) && vnode_issystem(vp)) { |
1366 | if (VTOF(vp)->fcbBTCBPtr != NULL) { | |
1367 | microuptime(&tv); | |
9bccf70c | 1368 | BTSetLastSync(VTOF(vp), tv.tv_sec); |
91447636 A |
1369 | } |
1370 | cp->c_touch_acctime = FALSE; | |
1371 | cp->c_touch_chgtime = FALSE; | |
1372 | cp->c_touch_modtime = FALSE; | |
3a60a9f5 | 1373 | } else if ( !(vp->v_flag & VSWAP) ) /* User file */ { |
91447636 | 1374 | retval = hfs_update(vp, wait); |
9bccf70c | 1375 | |
2d21ac55 A |
1376 | /* |
1377 | * When MNT_WAIT is requested push out the catalog record for | |
1378 | * this file. If they asked for a full fsync, we can skip this | |
1379 | * because the journal_flush or hfs_metasync_all will push out | |
1380 | * all of the metadata changes. | |
1381 | */ | |
1382 | if ((retval == 0) && wait && !fullsync && cp->c_hint && | |
9bccf70c | 1383 | !ISSET(cp->c_flag, C_DELETED | C_NOEXISTS)) { |
91447636 | 1384 | hfs_metasync(VTOHFS(vp), (daddr64_t)cp->c_hint, p); |
2d21ac55 | 1385 | } |
55e303ae | 1386 | |
2d21ac55 A |
1387 | /* |
1388 | * If this was a full fsync, make sure all metadata | |
1389 | * changes get to stable storage. | |
1390 | */ | |
1391 | if (fullsync) { | |
55e303ae A |
1392 | if (hfsmp->jnl) { |
1393 | journal_flush(hfsmp->jnl); | |
1394 | } else { | |
2d21ac55 | 1395 | retval = hfs_metasync_all(hfsmp); |
91447636 A |
1396 | /* XXX need to pass context! */ |
1397 | VNOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, NULL, FWRITE, NULL); | |
55e303ae A |
1398 | } |
1399 | } | |
9bccf70c A |
1400 | } |
1401 | ||
1402 | return (retval); | |
1403 | } | |
1404 | ||
91447636 | 1405 | |
9bccf70c A |
1406 | /* Sync an hfs catalog b-tree node */ |
1407 | static int | |
2d21ac55 | 1408 | hfs_metasync(struct hfsmount *hfsmp, daddr64_t node, __unused struct proc *p) |
9bccf70c | 1409 | { |
91447636 A |
1410 | vnode_t vp; |
1411 | buf_t bp; | |
1412 | int lockflags; | |
9bccf70c A |
1413 | |
1414 | vp = HFSTOVCB(hfsmp)->catalogRefNum; | |
1415 | ||
b4c24cb9 A |
1416 | // XXXdbg - don't need to do this on a journaled volume |
1417 | if (hfsmp->jnl) { | |
1418 | return 0; | |
1419 | } | |
1420 | ||
91447636 | 1421 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK); |
9bccf70c A |
1422 | /* |
1423 | * Look for a matching node that has been delayed | |
1424 | * but is not part of a set (B_LOCKED). | |
91447636 A |
1425 | * |
1426 | * BLK_ONLYVALID causes buf_getblk to return a | |
1427 | * buf_t for the daddr64_t specified only if it's | |
1428 | * currently resident in the cache... the size | |
1429 | * parameter to buf_getblk is ignored when this flag | |
1430 | * is set | |
9bccf70c | 1431 | */ |
91447636 A |
1432 | bp = buf_getblk(vp, node, 0, 0, 0, BLK_META | BLK_ONLYVALID); |
1433 | ||
1434 | if (bp) { | |
1435 | if ((buf_flags(bp) & (B_LOCKED | B_DELWRI)) == B_DELWRI) | |
1436 | (void) VNOP_BWRITE(bp); | |
1437 | else | |
1438 | buf_brelse(bp); | |
9bccf70c | 1439 | } |
91447636 A |
1440 | |
1441 | hfs_systemfile_unlock(hfsmp, lockflags); | |
9bccf70c A |
1442 | |
1443 | return (0); | |
1444 | } | |
1445 | ||
91447636 | 1446 | |
2d21ac55 A |
1447 | /* |
1448 | * Sync all hfs B-trees. Use this instead of journal_flush for a volume | |
1449 | * without a journal. Note that the volume bitmap does not get written; | |
1450 | * we rely on fsck_hfs to fix that up (which it can do without any loss | |
1451 | * of data). | |
1452 | */ | |
1453 | static int | |
1454 | hfs_metasync_all(struct hfsmount *hfsmp) | |
1455 | { | |
1456 | int lockflags; | |
1457 | ||
1458 | /* Lock all of the B-trees so we get a mutually consistent state */ | |
1459 | lockflags = hfs_systemfile_lock(hfsmp, | |
1460 | SFL_CATALOG|SFL_EXTENTS|SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK); | |
1461 | ||
1462 | /* Sync each of the B-trees */ | |
1463 | if (hfsmp->hfs_catalog_vp) | |
1464 | hfs_btsync(hfsmp->hfs_catalog_vp, 0); | |
1465 | if (hfsmp->hfs_extents_vp) | |
1466 | hfs_btsync(hfsmp->hfs_extents_vp, 0); | |
1467 | if (hfsmp->hfs_attribute_vp) | |
1468 | hfs_btsync(hfsmp->hfs_attribute_vp, 0); | |
1469 | ||
1470 | /* Wait for all of the writes to complete */ | |
1471 | if (hfsmp->hfs_catalog_vp) | |
1472 | vnode_waitforwrites(hfsmp->hfs_catalog_vp, 0, 0, 0, "hfs_metasync_all"); | |
1473 | if (hfsmp->hfs_extents_vp) | |
1474 | vnode_waitforwrites(hfsmp->hfs_extents_vp, 0, 0, 0, "hfs_metasync_all"); | |
1475 | if (hfsmp->hfs_attribute_vp) | |
1476 | vnode_waitforwrites(hfsmp->hfs_attribute_vp, 0, 0, 0, "hfs_metasync_all"); | |
1477 | ||
1478 | hfs_systemfile_unlock(hfsmp, lockflags); | |
1479 | ||
1480 | return 0; | |
1481 | } | |
1482 | ||
1483 | ||
91447636 A |
1484 | /*ARGSUSED 1*/ |
1485 | static int | |
2d21ac55 | 1486 | hfs_btsync_callback(struct buf *bp, __unused void *dummy) |
91447636 A |
1487 | { |
1488 | buf_clearflags(bp, B_LOCKED); | |
1489 | (void) buf_bawrite(bp); | |
1490 | ||
1491 | return(BUF_CLAIMED); | |
1492 | } | |
1493 | ||
1494 | ||
9bccf70c A |
1495 | __private_extern__ |
1496 | int | |
1497 | hfs_btsync(struct vnode *vp, int sync_transaction) | |
1498 | { | |
1499 | struct cnode *cp = VTOC(vp); | |
9bccf70c | 1500 | struct timeval tv; |
91447636 | 1501 | int flags = 0; |
9bccf70c | 1502 | |
91447636 A |
1503 | if (sync_transaction) |
1504 | flags |= BUF_SKIP_NONLOCKED; | |
9bccf70c A |
1505 | /* |
1506 | * Flush all dirty buffers associated with b-tree. | |
1507 | */ | |
91447636 | 1508 | buf_iterate(vp, hfs_btsync_callback, flags, 0); |
b4c24cb9 | 1509 | |
91447636 A |
1510 | microuptime(&tv); |
1511 | if (vnode_issystem(vp) && (VTOF(vp)->fcbBTCBPtr != NULL)) | |
9bccf70c | 1512 | (void) BTSetLastSync(VTOF(vp), tv.tv_sec); |
91447636 A |
1513 | cp->c_touch_acctime = FALSE; |
1514 | cp->c_touch_chgtime = FALSE; | |
1515 | cp->c_touch_modtime = FALSE; | |
9bccf70c A |
1516 | |
1517 | return 0; | |
1518 | } | |
1519 | ||
1520 | /* | |
91447636 | 1521 | * Remove a directory. |
9bccf70c A |
1522 | */ |
1523 | static int | |
91447636 A |
1524 | hfs_vnop_rmdir(ap) |
1525 | struct vnop_rmdir_args /* { | |
9bccf70c A |
1526 | struct vnode *a_dvp; |
1527 | struct vnode *a_vp; | |
1528 | struct componentname *a_cnp; | |
91447636 | 1529 | vfs_context_t a_context; |
9bccf70c A |
1530 | } */ *ap; |
1531 | { | |
91447636 A |
1532 | struct vnode *dvp = ap->a_dvp; |
1533 | struct vnode *vp = ap->a_vp; | |
2d21ac55 A |
1534 | struct cnode *dcp = VTOC(dvp); |
1535 | struct cnode *cp = VTOC(vp); | |
91447636 A |
1536 | int error; |
1537 | ||
2d21ac55 | 1538 | if (!S_ISDIR(cp->c_mode)) { |
91447636 A |
1539 | return (ENOTDIR); |
1540 | } | |
1541 | if (dvp == vp) { | |
1542 | return (EINVAL); | |
1543 | } | |
2d21ac55 | 1544 | if ((error = hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK))) { |
91447636 | 1545 | return (error); |
2d21ac55 | 1546 | } |
91447636 | 1547 | error = hfs_removedir(dvp, vp, ap->a_cnp, 0); |
2d21ac55 | 1548 | hfs_unlockpair(dcp, cp); |
91447636 A |
1549 | |
1550 | return (error); | |
55e303ae A |
1551 | } |
1552 | ||
1553 | /* | |
91447636 A |
1554 | * Remove a directory |
1555 | * | |
1556 | * Both dvp and vp cnodes are locked | |
55e303ae A |
1557 | */ |
1558 | static int | |
91447636 A |
1559 | hfs_removedir(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, |
1560 | int skip_reserve) | |
55e303ae | 1561 | { |
9bccf70c A |
1562 | struct cnode *cp; |
1563 | struct cnode *dcp; | |
1564 | struct hfsmount * hfsmp; | |
91447636 | 1565 | struct cat_desc desc; |
91447636 | 1566 | int lockflags; |
2d21ac55 | 1567 | int error = 0, started_tr = 0; |
9bccf70c A |
1568 | |
1569 | cp = VTOC(vp); | |
1570 | dcp = VTOC(dvp); | |
1571 | hfsmp = VTOHFS(vp); | |
1572 | ||
2d21ac55 | 1573 | if (dcp == cp) { |
9bccf70c | 1574 | return (EINVAL); /* cannot remove "." */ |
2d21ac55 A |
1575 | } |
1576 | if (cp->c_flag & (C_NOEXISTS | C_DELETED)) { | |
1577 | return (0); | |
1578 | } | |
1579 | if (cp->c_entries != 0) { | |
1580 | return (ENOTEMPTY); | |
1581 | } | |
1582 | ||
1583 | /* Check if we're removing the last link to an empty directory. */ | |
1584 | if (cp->c_flag & C_HARDLINK) { | |
1585 | /* We could also return EBUSY here */ | |
1586 | return hfs_unlink(hfsmp, dvp, vp, cnp, skip_reserve); | |
1587 | } | |
1588 | ||
1589 | if ((hfsmp->hfs_attribute_vp != NULL) && | |
1590 | (cp->c_attr.ca_recflags & kHFSHasAttributesMask) != 0) { | |
1591 | ||
1592 | return hfs_removefile(dvp, vp, cnp, 0, 0, 1); | |
1593 | } | |
1594 | ||
1595 | dcp->c_flag |= C_DIR_MODIFICATION; | |
b4c24cb9 | 1596 | |
d7e50217 | 1597 | #if QUOTA |
2d21ac55 A |
1598 | if (hfsmp->hfs_flags & HFS_QUOTAS) |
1599 | (void)hfs_getinoquota(cp); | |
d7e50217 | 1600 | #endif |
91447636 A |
1601 | if ((error = hfs_start_transaction(hfsmp)) != 0) { |
1602 | goto out; | |
b4c24cb9 | 1603 | } |
91447636 | 1604 | started_tr = 1; |
b4c24cb9 | 1605 | |
9bccf70c A |
1606 | /* |
1607 | * Verify the directory is empty (and valid). | |
1608 | * (Rmdir ".." won't be valid since | |
1609 | * ".." will contain a reference to | |
1610 | * the current directory and thus be | |
1611 | * non-empty.) | |
1612 | */ | |
9bccf70c A |
1613 | if ((dcp->c_flags & APPEND) || (cp->c_flags & (IMMUTABLE | APPEND))) { |
1614 | error = EPERM; | |
1615 | goto out; | |
1616 | } | |
1617 | ||
1618 | /* Remove the entry from the namei cache: */ | |
1619 | cache_purge(vp); | |
1620 | ||
91447636 A |
1621 | /* |
1622 | * Protect against a race with rename by using the component | |
1623 | * name passed in and parent id from dvp (instead of using | |
1624 | * the cp->c_desc which may have changed). | |
1625 | */ | |
2d21ac55 | 1626 | desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr; |
91447636 | 1627 | desc.cd_namelen = cnp->cn_namelen; |
2d21ac55 | 1628 | desc.cd_parentcnid = dcp->c_fileid; |
91447636 | 1629 | desc.cd_cnid = cp->c_cnid; |
2d21ac55 A |
1630 | desc.cd_flags = CD_ISDIR; |
1631 | desc.cd_encoding = cp->c_encoding; | |
1632 | desc.cd_hint = 0; | |
1633 | ||
1634 | if (!hfs_valid_cnode(hfsmp, dvp, cnp, cp->c_fileid)) { | |
1635 | error = 0; | |
1636 | goto out; | |
1637 | } | |
9bccf70c | 1638 | |
9bccf70c | 1639 | /* Remove entry from catalog */ |
2d21ac55 A |
1640 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK); |
1641 | ||
1642 | if (!skip_reserve) { | |
1643 | /* | |
1644 | * Reserve some space in the Catalog file. | |
1645 | */ | |
1646 | if ((error = cat_preflight(hfsmp, CAT_DELETE, NULL, 0))) { | |
1647 | hfs_systemfile_unlock(hfsmp, lockflags); | |
1648 | goto out; | |
1649 | } | |
1650 | } | |
1651 | ||
91447636 A |
1652 | error = cat_delete(hfsmp, &desc, &cp->c_attr); |
1653 | if (error == 0) { | |
2d21ac55 A |
1654 | /* The parent lost a child */ |
1655 | if (dcp->c_entries > 0) | |
1656 | dcp->c_entries--; | |
1657 | DEC_FOLDERCOUNT(hfsmp, dcp->c_attr); | |
1658 | dcp->c_dirchangecnt++; | |
1659 | dcp->c_touch_chgtime = TRUE; | |
1660 | dcp->c_touch_modtime = TRUE; | |
1661 | hfs_touchtimes(hfsmp, cp); | |
1662 | (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL); | |
1663 | cp->c_flag &= ~(C_MODIFIED | C_FORCEUPDATE); | |
91447636 | 1664 | } |
2d21ac55 | 1665 | |
91447636 | 1666 | hfs_systemfile_unlock(hfsmp, lockflags); |
9bccf70c | 1667 | |
91447636 A |
1668 | if (error) |
1669 | goto out; | |
9bccf70c A |
1670 | |
1671 | #if QUOTA | |
2d21ac55 A |
1672 | if (hfsmp->hfs_flags & HFS_QUOTAS) |
1673 | (void)hfs_chkiq(cp, -1, NOCRED, 0); | |
9bccf70c A |
1674 | #endif /* QUOTA */ |
1675 | ||
2d21ac55 | 1676 | HFS_KNOTE(dvp, NOTE_WRITE | NOTE_LINK | NOTE_ATTRIB); |
b4c24cb9 | 1677 | |
9bccf70c A |
1678 | hfs_volupdate(hfsmp, VOL_RMDIR, (dcp->c_cnid == kHFSRootFolderID)); |
1679 | ||
2d21ac55 A |
1680 | /* |
1681 | * directory open or in use (e.g. opendir() or current working | |
1682 | * directory for some process); wait for inactive to actually | |
1683 | * remove catalog entry | |
1684 | */ | |
1685 | if (vnode_isinuse(vp, 0)) { | |
1686 | cp->c_flag |= C_DELETED; | |
1687 | } else { | |
1688 | cp->c_mode = 0; /* Makes the vnode go away...see inactive */ | |
1689 | cp->c_flag |= C_NOEXISTS; | |
1690 | } | |
9bccf70c | 1691 | out: |
2d21ac55 A |
1692 | dcp->c_flag &= ~C_DIR_MODIFICATION; |
1693 | wakeup((caddr_t)&dcp->c_flag); | |
1694 | ||
55e303ae | 1695 | HFS_KNOTE(vp, NOTE_DELETE); |
b4c24cb9 | 1696 | |
b4c24cb9 | 1697 | if (started_tr) { |
91447636 | 1698 | hfs_end_transaction(hfsmp); |
b4c24cb9 A |
1699 | } |
1700 | ||
9bccf70c A |
1701 | return (error); |
1702 | } | |
1703 | ||
9bccf70c | 1704 | |
91447636 A |
1705 | /* |
1706 | * Remove a file or link. | |
1707 | */ | |
9bccf70c | 1708 | static int |
91447636 A |
1709 | hfs_vnop_remove(ap) |
1710 | struct vnop_remove_args /* { | |
9bccf70c A |
1711 | struct vnode *a_dvp; |
1712 | struct vnode *a_vp; | |
1713 | struct componentname *a_cnp; | |
91447636 A |
1714 | int a_flags; |
1715 | vfs_context_t a_context; | |
9bccf70c A |
1716 | } */ *ap; |
1717 | { | |
91447636 A |
1718 | struct vnode *dvp = ap->a_dvp; |
1719 | struct vnode *vp = ap->a_vp; | |
2d21ac55 A |
1720 | struct cnode *dcp = VTOC(dvp); |
1721 | struct cnode *cp = VTOC(vp); | |
1722 | struct vnode *rvp = cp->c_rsrc_vp; | |
1723 | int error=0, recycle_rsrc=0, rvid=0; | |
91447636 A |
1724 | |
1725 | if (dvp == vp) { | |
1726 | return (EINVAL); | |
1727 | } | |
1728 | ||
2d21ac55 | 1729 | hfs_lock_truncate(cp, TRUE); |
91447636 | 1730 | |
2d21ac55 | 1731 | if ((error = hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK))) |
91447636 A |
1732 | goto out; |
1733 | ||
2d21ac55 A |
1734 | error = hfs_removefile(dvp, vp, ap->a_cnp, ap->a_flags, 0, 0); |
1735 | ||
1736 | // | |
1737 | // If the remove succeeded and it's an open-unlinked file that has | |
1738 | // a resource fork vnode that's not in use, we will want to recycle | |
1739 | // the rvp *after* we're done unlocking everything. Otherwise the | |
1740 | // resource vnode will keep a v_parent reference on this vnode which | |
1741 | // prevents it from going through inactive/reclaim which means that | |
1742 | // the disk space associated with this file won't get free'd until | |
1743 | // something forces the resource vnode to get recycled (and that can | |
1744 | // take a very long time). | |
1745 | // | |
1746 | if (error == 0 && (cp->c_flag & C_DELETED) && rvp && !vnode_isinuse(rvp, 0)) { | |
1747 | rvid = vnode_vid(rvp); | |
1748 | recycle_rsrc = 1; | |
1749 | } | |
91447636 | 1750 | |
2d21ac55 | 1751 | hfs_unlockpair(dcp, cp); |
91447636 | 1752 | out: |
2d21ac55 A |
1753 | hfs_unlock_truncate(cp, TRUE); |
1754 | ||
1755 | if (recycle_rsrc && vnode_getwithvid(rvp, rvid) == 0) { | |
1756 | vnode_recycle(rvp); | |
1757 | vnode_put(rvp); | |
1758 | } | |
1759 | ||
91447636 | 1760 | return (error); |
55e303ae A |
1761 | } |
1762 | ||
1763 | ||
91447636 A |
1764 | static int |
1765 | hfs_removefile_callback(struct buf *bp, void *hfsmp) { | |
1766 | ||
1767 | if ( !(buf_flags(bp) & B_META)) | |
2d21ac55 | 1768 | panic("hfs: symlink bp @ %p is not marked meta-data!\n", bp); |
91447636 A |
1769 | /* |
1770 | * it's part of the current transaction, kill it. | |
1771 | */ | |
1772 | journal_kill_block(((struct hfsmount *)hfsmp)->jnl, bp); | |
1773 | ||
1774 | return (BUF_CLAIMED); | |
1775 | } | |
55e303ae A |
1776 | |
1777 | /* | |
1778 | * hfs_removefile | |
1779 | * | |
91447636 A |
1780 | * Similar to hfs_vnop_remove except there are additional options. |
1781 | * | |
1782 | * Requires cnode and truncate locks to be held. | |
55e303ae A |
1783 | */ |
1784 | static int | |
91447636 | 1785 | hfs_removefile(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, |
2d21ac55 | 1786 | int flags, int skip_reserve, int allow_dirs) |
55e303ae | 1787 | { |
9bccf70c A |
1788 | struct vnode *rvp = NULL; |
1789 | struct cnode *cp; | |
1790 | struct cnode *dcp; | |
1791 | struct hfsmount *hfsmp; | |
91447636 A |
1792 | struct cat_desc desc; |
1793 | struct timeval tv; | |
1794 | vfs_context_t ctx = cnp->cn_context; | |
9bccf70c A |
1795 | int dataforkbusy = 0; |
1796 | int rsrcforkbusy = 0; | |
1797 | int truncated = 0; | |
91447636 | 1798 | int lockflags; |
9bccf70c | 1799 | int error = 0; |
2d21ac55 A |
1800 | int started_tr = 0; |
1801 | int isbigfile = 0, hasxattrs=0, isdir=0; | |
9bccf70c A |
1802 | |
1803 | cp = VTOC(vp); | |
1804 | dcp = VTOC(dvp); | |
1805 | hfsmp = VTOHFS(vp); | |
91447636 | 1806 | |
2d21ac55 | 1807 | /* Check if we lost a race post lookup. */ |
91447636 | 1808 | if (cp->c_flag & (C_NOEXISTS | C_DELETED)) { |
2d21ac55 | 1809 | return (0); |
91447636 | 1810 | } |
2d21ac55 A |
1811 | |
1812 | if (!hfs_valid_cnode(hfsmp, dvp, cnp, cp->c_fileid)) { | |
1813 | return 0; | |
9bccf70c A |
1814 | } |
1815 | ||
1816 | /* Make sure a remove is permitted */ | |
91447636 | 1817 | if (VNODE_IS_RSRC(vp)) { |
2d21ac55 | 1818 | return (EPERM); |
b4c24cb9 | 1819 | } |
2d21ac55 A |
1820 | /* Don't allow deleting the journal or journal_info_block. */ |
1821 | if (hfsmp->jnl && | |
1822 | (cp->c_fileid == hfsmp->hfs_jnlfileid || cp->c_fileid == hfsmp->hfs_jnlinfoblkid)) { | |
1823 | return (EPERM); | |
1824 | } | |
1825 | /* | |
1826 | * Hard links require special handling. | |
1827 | */ | |
1828 | if (cp->c_flag & C_HARDLINK) { | |
1829 | if ((flags & VNODE_REMOVE_NODELETEBUSY) && vnode_isinuse(vp, 0)) { | |
1830 | return (EBUSY); | |
1831 | } else { | |
1832 | /* A directory hard link with a link count of one is | |
1833 | * treated as a regular directory. Therefore it should | |
1834 | * only be removed using rmdir(). | |
1835 | */ | |
1836 | if ((vnode_isdir(vp) == 1) && (cp->c_linkcount == 1) && | |
1837 | (allow_dirs == 0)) { | |
1838 | return (EPERM); | |
1839 | } | |
1840 | return hfs_unlink(hfsmp, dvp, vp, cnp, skip_reserve); | |
1841 | } | |
1842 | } | |
1843 | /* Directories should call hfs_rmdir! (unless they have a lot of attributes) */ | |
1844 | if (vnode_isdir(vp)) { | |
1845 | if (allow_dirs == 0) | |
1846 | return (EPERM); /* POSIX */ | |
1847 | isdir = 1; | |
1848 | } | |
1849 | /* Sanity check the parent ids. */ | |
1850 | if ((cp->c_parentcnid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) && | |
1851 | (cp->c_parentcnid != dcp->c_fileid)) { | |
1852 | return (EINVAL); | |
1853 | } | |
1854 | ||
1855 | dcp->c_flag |= C_DIR_MODIFICATION; | |
1856 | ||
1857 | // this guy is going away so mark him as such | |
1858 | cp->c_flag |= C_DELETED; | |
1859 | ||
1860 | ||
1861 | /* Remove our entry from the namei cache. */ | |
1862 | cache_purge(vp); | |
9bccf70c A |
1863 | |
1864 | /* | |
2d21ac55 | 1865 | * Acquire a vnode for a non-empty resource fork. |
91447636 | 1866 | * (needed for hfs_truncate) |
9bccf70c | 1867 | */ |
2d21ac55 A |
1868 | if (isdir == 0 && (cp->c_blocks - VTOF(vp)->ff_blocks)) { |
1869 | error = hfs_vgetrsrc(hfsmp, vp, &rvp, FALSE); | |
9bccf70c A |
1870 | if (error) |
1871 | goto out; | |
2d21ac55 A |
1872 | /* Defer the vnode_put on rvp until the hfs_unlock(). */ |
1873 | cp->c_flag |= C_NEED_RVNODE_PUT; | |
9bccf70c | 1874 | } |
2d21ac55 A |
1875 | /* Check if this file is being used. */ |
1876 | if (isdir == 0) { | |
1877 | dataforkbusy = vnode_isinuse(vp, 0); | |
1878 | rsrcforkbusy = rvp ? vnode_isinuse(rvp, 0) : 0; | |
1879 | } | |
1880 | ||
1881 | /* Check if we have to break the deletion into multiple pieces. */ | |
1882 | if (isdir == 0) { | |
1883 | isbigfile = ((cp->c_datafork->ff_size >= HFS_BIGFILE_SIZE) && overflow_extents(VTOF(vp))); | |
b4c24cb9 A |
1884 | } |
1885 | ||
2d21ac55 A |
1886 | /* Check if the file has xattrs. If it does we'll have to delete them in |
1887 | individual transactions in case there are too many */ | |
1888 | if ((hfsmp->hfs_attribute_vp != NULL) && | |
1889 | (cp->c_attr.ca_recflags & kHFSHasAttributesMask) != 0) { | |
1890 | hasxattrs = 1; | |
1891 | } | |
55e303ae | 1892 | |
9bccf70c A |
1893 | /* |
1894 | * Carbon semantics prohibit deleting busy files. | |
91447636 | 1895 | * (enforced when VNODE_REMOVE_NODELETEBUSY is requested) |
9bccf70c | 1896 | */ |
91447636 A |
1897 | if (dataforkbusy || rsrcforkbusy) { |
1898 | if ((flags & VNODE_REMOVE_NODELETEBUSY) || | |
2d21ac55 | 1899 | (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid == 0)) { |
91447636 A |
1900 | error = EBUSY; |
1901 | goto out; | |
1902 | } | |
9bccf70c A |
1903 | } |
1904 | ||
d7e50217 | 1905 | #if QUOTA |
2d21ac55 A |
1906 | if (hfsmp->hfs_flags & HFS_QUOTAS) |
1907 | (void)hfs_getinoquota(cp); | |
d7e50217 A |
1908 | #endif /* QUOTA */ |
1909 | ||
2d21ac55 A |
1910 | /* Check if we need a ubc_setsize. */ |
1911 | if (isdir == 0 && (!dataforkbusy || !rsrcforkbusy)) { | |
91447636 | 1912 | /* |
0c530ab8 A |
1913 | * A ubc_setsize can cause a pagein so defer it |
1914 | * until after the cnode lock is dropped. The | |
1915 | * cnode lock cannot be dropped/reacquired here | |
1916 | * since we might already hold the journal lock. | |
91447636 | 1917 | */ |
91447636 | 1918 | if (!dataforkbusy && cp->c_datafork->ff_blocks && !isbigfile) { |
0c530ab8 | 1919 | cp->c_flag |= C_NEED_DATA_SETSIZE; |
91447636 A |
1920 | } |
1921 | if (!rsrcforkbusy && rvp) { | |
0c530ab8 | 1922 | cp->c_flag |= C_NEED_RSRC_SETSIZE; |
91447636 | 1923 | } |
91447636 A |
1924 | } |
1925 | ||
1926 | if ((error = hfs_start_transaction(hfsmp)) != 0) { | |
1927 | goto out; | |
b4c24cb9 | 1928 | } |
91447636 | 1929 | started_tr = 1; |
b4c24cb9 | 1930 | |
b4c24cb9 | 1931 | // XXXdbg - if we're journaled, kill any dirty symlink buffers |
91447636 A |
1932 | if (hfsmp->jnl && vnode_islnk(vp)) |
1933 | buf_iterate(vp, hfs_removefile_callback, BUF_SKIP_NONLOCKED, (void *)hfsmp); | |
b4c24cb9 | 1934 | |
9bccf70c A |
1935 | /* |
1936 | * Truncate any non-busy forks. Busy forks will | |
2d21ac55 | 1937 | * get truncated when their vnode goes inactive. |
9bccf70c | 1938 | * |
91447636 A |
1939 | * Since we're already inside a transaction, |
1940 | * tell hfs_truncate to skip the ubc_setsize. | |
9bccf70c | 1941 | */ |
2d21ac55 | 1942 | if (isdir == 0) { |
9bccf70c A |
1943 | int mode = cp->c_mode; |
1944 | ||
55e303ae | 1945 | if (!dataforkbusy && !isbigfile && cp->c_datafork->ff_blocks != 0) { |
91447636 A |
1946 | cp->c_mode = 0; /* Suppress hfs_update */ |
1947 | error = hfs_truncate(vp, (off_t)0, IO_NDELAY, 1, ctx); | |
9bccf70c A |
1948 | cp->c_mode = mode; |
1949 | if (error) | |
1950 | goto out; | |
1951 | truncated = 1; | |
1952 | } | |
1953 | if (!rsrcforkbusy && rvp) { | |
2d21ac55 | 1954 | cp->c_mode = 0; /* Suppress hfs_update */ |
91447636 | 1955 | error = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 1, ctx); |
9bccf70c | 1956 | cp->c_mode = mode; |
90556fb8 | 1957 | if (error) |
9bccf70c | 1958 | goto out; |
9bccf70c A |
1959 | truncated = 1; |
1960 | } | |
1961 | } | |
91447636 A |
1962 | |
1963 | /* | |
1964 | * Protect against a race with rename by using the component | |
1965 | * name passed in and parent id from dvp (instead of using | |
1966 | * the cp->c_desc which may have changed). | |
1967 | */ | |
1968 | desc.cd_flags = 0; | |
1969 | desc.cd_encoding = cp->c_desc.cd_encoding; | |
2d21ac55 | 1970 | desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr; |
91447636 | 1971 | desc.cd_namelen = cnp->cn_namelen; |
2d21ac55 | 1972 | desc.cd_parentcnid = dcp->c_fileid; |
91447636 | 1973 | desc.cd_hint = cp->c_desc.cd_hint; |
2d21ac55 | 1974 | desc.cd_cnid = cp->c_cnid; |
91447636 A |
1975 | microtime(&tv); |
1976 | ||
9bccf70c | 1977 | /* |
2d21ac55 A |
1978 | * There are two cases to consider: |
1979 | * 1. File is busy/big ==> move/rename the file | |
1980 | * 2. File is not in use ==> remove the file | |
9bccf70c | 1981 | */ |
2d21ac55 | 1982 | if (dataforkbusy || rsrcforkbusy || isbigfile || hasxattrs) { |
9bccf70c A |
1983 | char delname[32]; |
1984 | struct cat_desc to_desc; | |
1985 | struct cat_desc todir_desc; | |
1986 | ||
1987 | /* | |
1988 | * Orphan this file (move to hidden directory). | |
1989 | */ | |
1990 | bzero(&todir_desc, sizeof(todir_desc)); | |
1991 | todir_desc.cd_parentcnid = 2; | |
1992 | ||
2d21ac55 | 1993 | MAKE_DELETED_NAME(delname, sizeof(delname), cp->c_fileid); |
9bccf70c | 1994 | bzero(&to_desc, sizeof(to_desc)); |
2d21ac55 | 1995 | to_desc.cd_nameptr = (const u_int8_t *)delname; |
9bccf70c | 1996 | to_desc.cd_namelen = strlen(delname); |
2d21ac55 | 1997 | to_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid; |
9bccf70c A |
1998 | to_desc.cd_flags = 0; |
1999 | to_desc.cd_cnid = cp->c_cnid; | |
2000 | ||
91447636 | 2001 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK); |
2d21ac55 A |
2002 | if (!skip_reserve) { |
2003 | if ((error = cat_preflight(hfsmp, CAT_RENAME, NULL, 0))) { | |
2004 | hfs_systemfile_unlock(hfsmp, lockflags); | |
2005 | goto out; | |
2006 | } | |
2007 | } | |
9bccf70c | 2008 | |
91447636 | 2009 | error = cat_rename(hfsmp, &desc, &todir_desc, |
9bccf70c A |
2010 | &to_desc, (struct cat_desc *)NULL); |
2011 | ||
b4c24cb9 | 2012 | if (error == 0) { |
2d21ac55 A |
2013 | hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries++; |
2014 | if (isdir == 1) { | |
2015 | INC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]); | |
2016 | } | |
2017 | (void) cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS], | |
2018 | &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL); | |
91447636 A |
2019 | |
2020 | /* Update the parent directory */ | |
2021 | if (dcp->c_entries > 0) | |
2022 | dcp->c_entries--; | |
2d21ac55 A |
2023 | if (isdir == 1) { |
2024 | DEC_FOLDERCOUNT(hfsmp, dcp->c_attr); | |
2025 | } | |
2026 | dcp->c_dirchangecnt++; | |
91447636 A |
2027 | dcp->c_ctime = tv.tv_sec; |
2028 | dcp->c_mtime = tv.tv_sec; | |
2029 | (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL); | |
2030 | ||
2031 | /* Update the file's state */ | |
2032 | cp->c_flag |= C_DELETED; | |
2033 | cp->c_ctime = tv.tv_sec; | |
2d21ac55 | 2034 | --cp->c_linkcount; |
91447636 | 2035 | (void) cat_update(hfsmp, &to_desc, &cp->c_attr, NULL, NULL); |
b4c24cb9 | 2036 | } |
91447636 A |
2037 | hfs_systemfile_unlock(hfsmp, lockflags); |
2038 | if (error) | |
2039 | goto out; | |
9bccf70c A |
2040 | |
2041 | } else /* Not busy */ { | |
2042 | ||
90556fb8 | 2043 | if (cp->c_blocks > 0) { |
55e303ae A |
2044 | printf("hfs_remove: attempting to delete a non-empty file %s\n", |
2045 | cp->c_desc.cd_nameptr); | |
90556fb8 A |
2046 | error = EBUSY; |
2047 | goto out; | |
2048 | } | |
9bccf70c | 2049 | |
2d21ac55 A |
2050 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK); |
2051 | if (!skip_reserve) { | |
2052 | if ((error = cat_preflight(hfsmp, CAT_DELETE, NULL, 0))) { | |
2053 | hfs_systemfile_unlock(hfsmp, lockflags); | |
2054 | goto out; | |
2055 | } | |
2056 | } | |
b4c24cb9 | 2057 | |
91447636 | 2058 | error = cat_delete(hfsmp, &desc, &cp->c_attr); |
9bccf70c | 2059 | |
90556fb8 | 2060 | if (error && error != ENXIO && error != ENOENT && truncated) { |
55e303ae A |
2061 | if ((cp->c_datafork && cp->c_datafork->ff_size != 0) || |
2062 | (cp->c_rsrcfork && cp->c_rsrcfork->ff_size != 0)) { | |
b4c24cb9 | 2063 | panic("hfs: remove: couldn't delete a truncated file! (%d, data sz %lld; rsrc sz %lld)", |
55e303ae | 2064 | error, cp->c_datafork->ff_size, cp->c_rsrcfork->ff_size); |
b4c24cb9 A |
2065 | } else { |
2066 | printf("hfs: remove: strangely enough, deleting truncated file %s (%d) got err %d\n", | |
2067 | cp->c_desc.cd_nameptr, cp->c_attr.ca_fileid, error); | |
2068 | } | |
2069 | } | |
91447636 | 2070 | if (error == 0) { |
91447636 A |
2071 | /* Update the parent directory */ |
2072 | if (dcp->c_entries > 0) | |
2073 | dcp->c_entries--; | |
2d21ac55 | 2074 | dcp->c_dirchangecnt++; |
91447636 A |
2075 | dcp->c_ctime = tv.tv_sec; |
2076 | dcp->c_mtime = tv.tv_sec; | |
2077 | (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL); | |
2078 | } | |
2079 | hfs_systemfile_unlock(hfsmp, lockflags); | |
2080 | if (error) | |
2081 | goto out; | |
9bccf70c A |
2082 | |
2083 | #if QUOTA | |
2d21ac55 A |
2084 | if (hfsmp->hfs_flags & HFS_QUOTAS) |
2085 | (void)hfs_chkiq(cp, -1, NOCRED, 0); | |
9bccf70c A |
2086 | #endif /* QUOTA */ |
2087 | ||
2088 | cp->c_mode = 0; | |
55e303ae | 2089 | truncated = 0; // because the catalog entry is gone |
91447636 | 2090 | cp->c_flag |= C_NOEXISTS; |
2d21ac55 | 2091 | cp->c_flag &= ~C_DELETED; |
91447636 | 2092 | cp->c_touch_chgtime = TRUE; /* XXX needed ? */ |
2d21ac55 | 2093 | --cp->c_linkcount; |
91447636 | 2094 | |
9bccf70c A |
2095 | hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID)); |
2096 | } | |
2097 | ||
2098 | /* | |
2099 | * All done with this cnode's descriptor... | |
2100 | * | |
2d21ac55 A |
2101 | * Note: all future catalog calls for this cnode must be by |
2102 | * fileid only. This is OK for HFS (which doesn't have file | |
2103 | * thread records) since HFS doesn't support the removal of | |
2104 | * busy files. | |
9bccf70c A |
2105 | */ |
2106 | cat_releasedesc(&cp->c_desc); | |
2107 | ||
55e303ae | 2108 | HFS_KNOTE(dvp, NOTE_WRITE); |
9bccf70c | 2109 | |
55e303ae | 2110 | out: |
2d21ac55 A |
2111 | if (error) { |
2112 | cp->c_flag &= ~C_DELETED; | |
b4c24cb9 | 2113 | } |
b4c24cb9 | 2114 | |
9bccf70c A |
2115 | /* Commit the truncation to the catalog record */ |
2116 | if (truncated) { | |
91447636 A |
2117 | cp->c_flag |= C_FORCEUPDATE; |
2118 | cp->c_touch_chgtime = TRUE; | |
2119 | cp->c_touch_modtime = TRUE; | |
2120 | (void) hfs_update(vp, 0); | |
9bccf70c | 2121 | } |
b4c24cb9 | 2122 | |
b4c24cb9 | 2123 | if (started_tr) { |
91447636 | 2124 | hfs_end_transaction(hfsmp); |
b4c24cb9 A |
2125 | } |
2126 | ||
2d21ac55 A |
2127 | dcp->c_flag &= ~C_DIR_MODIFICATION; |
2128 | wakeup((caddr_t)&dcp->c_flag); | |
2129 | ||
55e303ae A |
2130 | HFS_KNOTE(vp, NOTE_DELETE); |
2131 | if (rvp) { | |
2132 | HFS_KNOTE(rvp, NOTE_DELETE); | |
2d21ac55 | 2133 | } |
55e303ae | 2134 | |
9bccf70c A |
2135 | return (error); |
2136 | } | |
2137 | ||
2138 | ||
2139 | __private_extern__ void | |
2140 | replace_desc(struct cnode *cp, struct cat_desc *cdp) | |
2141 | { | |
2d21ac55 | 2142 | // fixes 4348457 and 4463138 |
0c530ab8 | 2143 | if (&cp->c_desc == cdp) { |
2d21ac55 | 2144 | return; |
0c530ab8 A |
2145 | } |
2146 | ||
9bccf70c A |
2147 | /* First release allocated name buffer */ |
2148 | if (cp->c_desc.cd_flags & CD_HASBUF && cp->c_desc.cd_nameptr != 0) { | |
2d21ac55 | 2149 | const u_int8_t *name = cp->c_desc.cd_nameptr; |
9bccf70c A |
2150 | |
2151 | cp->c_desc.cd_nameptr = 0; | |
2152 | cp->c_desc.cd_namelen = 0; | |
2153 | cp->c_desc.cd_flags &= ~CD_HASBUF; | |
2d21ac55 | 2154 | vfs_removename((const char *)name); |
9bccf70c A |
2155 | } |
2156 | bcopy(cdp, &cp->c_desc, sizeof(cp->c_desc)); | |
2157 | ||
2158 | /* Cnode now owns the name buffer */ | |
2159 | cdp->cd_nameptr = 0; | |
2160 | cdp->cd_namelen = 0; | |
2161 | cdp->cd_flags &= ~CD_HASBUF; | |
2162 | } | |
2163 | ||
2164 | ||
9bccf70c A |
2165 | /* |
2166 | * Rename a cnode. | |
2167 | * | |
91447636 A |
2168 | * The VFS layer guarantees that: |
2169 | * - source and destination will either both be directories, or | |
2170 | * both not be directories. | |
2171 | * - all the vnodes are from the same file system | |
d7e50217 | 2172 | * |
91447636 | 2173 | * When the target is a directory, HFS must ensure that its empty. |
9bccf70c | 2174 | */ |
9bccf70c | 2175 | static int |
91447636 A |
2176 | hfs_vnop_rename(ap) |
2177 | struct vnop_rename_args /* { | |
9bccf70c A |
2178 | struct vnode *a_fdvp; |
2179 | struct vnode *a_fvp; | |
2180 | struct componentname *a_fcnp; | |
2181 | struct vnode *a_tdvp; | |
2182 | struct vnode *a_tvp; | |
2183 | struct componentname *a_tcnp; | |
91447636 | 2184 | vfs_context_t a_context; |
9bccf70c A |
2185 | } */ *ap; |
2186 | { | |
2187 | struct vnode *tvp = ap->a_tvp; | |
2188 | struct vnode *tdvp = ap->a_tdvp; | |
2189 | struct vnode *fvp = ap->a_fvp; | |
2190 | struct vnode *fdvp = ap->a_fdvp; | |
2191 | struct componentname *tcnp = ap->a_tcnp; | |
2192 | struct componentname *fcnp = ap->a_fcnp; | |
91447636 A |
2193 | struct proc *p = vfs_context_proc(ap->a_context); |
2194 | struct cnode *fcp; | |
2195 | struct cnode *fdcp; | |
2196 | struct cnode *tdcp; | |
2197 | struct cnode *tcp; | |
9bccf70c A |
2198 | struct cat_desc from_desc; |
2199 | struct cat_desc to_desc; | |
2200 | struct cat_desc out_desc; | |
91447636 A |
2201 | struct hfsmount *hfsmp; |
2202 | cat_cookie_t cookie; | |
2203 | int tvp_deleted = 0; | |
2204 | int started_tr = 0, got_cookie = 0; | |
2205 | int took_trunc_lock = 0; | |
2206 | int lockflags; | |
2207 | int error; | |
d7e50217 | 2208 | |
91447636 | 2209 | /* When tvp exist, take the truncate lock for the hfs_removefile(). */ |
2d21ac55 | 2210 | if (tvp && (vnode_isreg(tvp) || vnode_islnk(tvp))) { |
91447636 A |
2211 | hfs_lock_truncate(VTOC(tvp), TRUE); |
2212 | took_trunc_lock = 1; | |
2213 | } | |
9bccf70c | 2214 | |
2d21ac55 | 2215 | retry: |
91447636 A |
2216 | error = hfs_lockfour(VTOC(fdvp), VTOC(fvp), VTOC(tdvp), tvp ? VTOC(tvp) : NULL, |
2217 | HFS_EXCLUSIVE_LOCK); | |
2218 | if (error) { | |
2219 | if (took_trunc_lock) | |
2d21ac55 | 2220 | hfs_unlock_truncate(VTOC(tvp), TRUE); |
91447636 | 2221 | return (error); |
55e303ae A |
2222 | } |
2223 | ||
91447636 A |
2224 | fdcp = VTOC(fdvp); |
2225 | fcp = VTOC(fvp); | |
2226 | tdcp = VTOC(tdvp); | |
2227 | tcp = tvp ? VTOC(tvp) : NULL; | |
2228 | hfsmp = VTOHFS(tdvp); | |
55e303ae | 2229 | |
91447636 | 2230 | /* Check for a race against unlink. */ |
2d21ac55 | 2231 | if ((fcp->c_flag & (C_NOEXISTS | C_DELETED)) || !hfs_valid_cnode(hfsmp, fdvp, fcnp, fcp->c_fileid)) { |
91447636 A |
2232 | error = ENOENT; |
2233 | goto out; | |
9bccf70c | 2234 | } |
91447636 | 2235 | |
2d21ac55 A |
2236 | if (tcp && ((tcp->c_flag & (C_NOEXISTS | C_DELETED)) || !hfs_valid_cnode(hfsmp, tdvp, tcnp, tcp->c_fileid))) { |
2237 | // | |
2238 | // hmm, the destination vnode isn't valid any more. | |
2239 | // in this case we can just drop him and pretend he | |
2240 | // never existed in the first place. | |
2241 | // | |
2242 | if (took_trunc_lock) { | |
2243 | hfs_unlock_truncate(VTOC(tvp), TRUE); | |
2244 | took_trunc_lock = 0; | |
2245 | } | |
2246 | ||
2247 | hfs_unlockfour(fdcp, fcp, tdcp, tcp); | |
2248 | ||
2249 | tcp = NULL; | |
2250 | tvp = NULL; | |
2251 | ||
2252 | // retry the locking with tvp null'ed out | |
2253 | goto retry; | |
2254 | } | |
2255 | ||
2256 | fdcp->c_flag |= C_DIR_MODIFICATION; | |
2257 | if (fdvp != tdvp) { | |
2258 | tdcp->c_flag |= C_DIR_MODIFICATION; | |
2259 | } | |
2260 | ||
2261 | /* | |
2262 | * Disallow renaming of a directory hard link if the source and | |
2263 | * destination parent directories are different, or a directory whose | |
2264 | * descendant is a directory hard link and the one of the ancestors | |
2265 | * of the destination directory is a directory hard link. | |
2266 | */ | |
2267 | if (vnode_isdir(fvp) && (fdvp != tdvp)) { | |
2268 | if (fcp->c_flag & C_HARDLINK) { | |
2269 | error = EPERM; | |
2270 | goto out; | |
2271 | } | |
2272 | if (fcp->c_attr.ca_recflags & kHFSHasChildLinkMask) { | |
2273 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); | |
2274 | if (cat_check_link_ancestry(hfsmp, tdcp->c_fileid, 0)) { | |
2275 | error = EPERM; | |
2276 | hfs_systemfile_unlock(hfsmp, lockflags); | |
2277 | goto out; | |
2278 | } | |
2279 | hfs_systemfile_unlock(hfsmp, lockflags); | |
2280 | } | |
2281 | } | |
2282 | ||
9bccf70c | 2283 | /* |
55e303ae A |
2284 | * The following edge case is caught here: |
2285 | * (to cannot be a descendent of from) | |
2286 | * | |
2287 | * o fdvp | |
2288 | * / | |
2289 | * / | |
2290 | * o fvp | |
2291 | * \ | |
2292 | * \ | |
2293 | * o tdvp | |
2294 | * / | |
2295 | * / | |
2296 | * o tvp | |
9bccf70c | 2297 | */ |
2d21ac55 | 2298 | if (tdcp->c_parentcnid == fcp->c_fileid) { |
55e303ae A |
2299 | error = EINVAL; |
2300 | goto out; | |
2301 | } | |
2302 | ||
2303 | /* | |
2304 | * The following two edge cases are caught here: | |
2305 | * (note tvp is not empty) | |
2306 | * | |
2307 | * o tdvp o tdvp | |
2308 | * / / | |
2309 | * / / | |
2310 | * o tvp tvp o fdvp | |
2311 | * \ \ | |
2312 | * \ \ | |
2313 | * o fdvp o fvp | |
2314 | * / | |
2315 | * / | |
2316 | * o fvp | |
2317 | */ | |
91447636 | 2318 | if (tvp && vnode_isdir(tvp) && (tcp->c_entries != 0) && fvp != tvp) { |
55e303ae A |
2319 | error = ENOTEMPTY; |
2320 | goto out; | |
2321 | } | |
2322 | ||
2323 | /* | |
2324 | * The following edge case is caught here: | |
2325 | * (the from child and parent are the same) | |
2326 | * | |
2327 | * o tdvp | |
2328 | * / | |
2329 | * / | |
2330 | * fdvp o fvp | |
2331 | */ | |
2332 | if (fdvp == fvp) { | |
2333 | error = EINVAL; | |
d7e50217 | 2334 | goto out; |
9bccf70c A |
2335 | } |
2336 | ||
2337 | /* | |
d7e50217 | 2338 | * Make sure "from" vnode and its parent are changeable. |
9bccf70c | 2339 | */ |
91447636 | 2340 | if ((fcp->c_flags & (IMMUTABLE | APPEND)) || (fdcp->c_flags & APPEND)) { |
d7e50217 A |
2341 | error = EPERM; |
2342 | goto out; | |
9bccf70c A |
2343 | } |
2344 | ||
9bccf70c | 2345 | /* |
d7e50217 A |
2346 | * If the destination parent directory is "sticky", then the |
2347 | * user must own the parent directory, or the destination of | |
2348 | * the rename, otherwise the destination may not be changed | |
2349 | * (except by root). This implements append-only directories. | |
2350 | * | |
55e303ae | 2351 | * Note that checks for immutable and write access are done |
91447636 | 2352 | * by the call to hfs_removefile. |
9bccf70c | 2353 | */ |
d7e50217 | 2354 | if (tvp && (tdcp->c_mode & S_ISTXT) && |
91447636 A |
2355 | (suser(vfs_context_ucred(tcnp->cn_context), NULL)) && |
2356 | (kauth_cred_getuid(vfs_context_ucred(tcnp->cn_context)) != tdcp->c_uid) && | |
2357 | (hfs_owner_rights(hfsmp, tcp->c_uid, vfs_context_ucred(tcnp->cn_context), p, false)) ) { | |
55e303ae A |
2358 | error = EPERM; |
2359 | goto out; | |
9bccf70c A |
2360 | } |
2361 | ||
55e303ae A |
2362 | #if QUOTA |
2363 | if (tvp) | |
91447636 | 2364 | (void)hfs_getinoquota(tcp); |
55e303ae | 2365 | #endif |
91447636 | 2366 | /* Preflighting done, take fvp out of the name space. */ |
55e303ae A |
2367 | cache_purge(fvp); |
2368 | ||
90556fb8 A |
2369 | /* |
2370 | * When a file moves out of "Cleanup At Startup" | |
2371 | * we can drop its NODUMP status. | |
2372 | */ | |
2373 | if ((fcp->c_flags & UF_NODUMP) && | |
91447636 | 2374 | vnode_isreg(fvp) && |
90556fb8 A |
2375 | (fdvp != tdvp) && |
2376 | (fdcp->c_desc.cd_nameptr != NULL) && | |
2d21ac55 A |
2377 | (strncmp((const char *)fdcp->c_desc.cd_nameptr, |
2378 | CARBON_TEMP_DIR_NAME, | |
2379 | sizeof(CARBON_TEMP_DIR_NAME)) == 0)) { | |
90556fb8 | 2380 | fcp->c_flags &= ~UF_NODUMP; |
91447636 A |
2381 | fcp->c_touch_chgtime = TRUE; |
2382 | (void) hfs_update(fvp, 0); | |
90556fb8 A |
2383 | } |
2384 | ||
9bccf70c | 2385 | bzero(&from_desc, sizeof(from_desc)); |
2d21ac55 | 2386 | from_desc.cd_nameptr = (const u_int8_t *)fcnp->cn_nameptr; |
9bccf70c | 2387 | from_desc.cd_namelen = fcnp->cn_namelen; |
2d21ac55 | 2388 | from_desc.cd_parentcnid = fdcp->c_fileid; |
9bccf70c A |
2389 | from_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED); |
2390 | from_desc.cd_cnid = fcp->c_cnid; | |
d7e50217 | 2391 | |
9bccf70c | 2392 | bzero(&to_desc, sizeof(to_desc)); |
2d21ac55 | 2393 | to_desc.cd_nameptr = (const u_int8_t *)tcnp->cn_nameptr; |
9bccf70c | 2394 | to_desc.cd_namelen = tcnp->cn_namelen; |
2d21ac55 | 2395 | to_desc.cd_parentcnid = tdcp->c_fileid; |
9bccf70c A |
2396 | to_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED); |
2397 | to_desc.cd_cnid = fcp->c_cnid; | |
2398 | ||
91447636 A |
2399 | if ((error = hfs_start_transaction(hfsmp)) != 0) { |
2400 | goto out; | |
2401 | } | |
2402 | started_tr = 1; | |
2403 | ||
2d21ac55 A |
2404 | /* hfs_vnop_link() and hfs_vnop_rename() set kHFSHasChildLinkMask |
2405 | * inside a journal transaction and without holding a cnode lock. | |
2406 | * As setting of this bit depends on being in journal transaction for | |
2407 | * concurrency, check this bit again after we start journal transaction for rename | |
2408 | * to ensure that this directory does not have any descendant that | |
2409 | * is a directory hard link. | |
2410 | */ | |
2411 | if (vnode_isdir(fvp) && (fdvp != tdvp)) { | |
2412 | if (fcp->c_attr.ca_recflags & kHFSHasChildLinkMask) { | |
2413 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); | |
2414 | if (cat_check_link_ancestry(hfsmp, tdcp->c_fileid, 0)) { | |
2415 | error = EPERM; | |
2416 | hfs_systemfile_unlock(hfsmp, lockflags); | |
2417 | goto out; | |
2418 | } | |
2419 | hfs_systemfile_unlock(hfsmp, lockflags); | |
2420 | } | |
2421 | } | |
2422 | ||
91447636 A |
2423 | // if it's a hardlink then re-lookup the name so |
2424 | // that we get the correct cnid in from_desc (see | |
2425 | // the comment in hfs_removefile for more details) | |
2426 | // | |
2427 | if (fcp->c_flag & C_HARDLINK) { | |
2428 | struct cat_desc tmpdesc; | |
2429 | cnid_t real_cnid; | |
2430 | ||
2d21ac55 | 2431 | tmpdesc.cd_nameptr = (const u_int8_t *)fcnp->cn_nameptr; |
91447636 | 2432 | tmpdesc.cd_namelen = fcnp->cn_namelen; |
2d21ac55 | 2433 | tmpdesc.cd_parentcnid = fdcp->c_fileid; |
91447636 | 2434 | tmpdesc.cd_hint = fdcp->c_childhint; |
2d21ac55 A |
2435 | tmpdesc.cd_flags = fcp->c_desc.cd_flags & CD_ISDIR; |
2436 | tmpdesc.cd_encoding = 0; | |
91447636 A |
2437 | |
2438 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); | |
2439 | ||
2440 | if (cat_lookup(hfsmp, &tmpdesc, 0, NULL, NULL, NULL, &real_cnid) != 0) { | |
2441 | hfs_systemfile_unlock(hfsmp, lockflags); | |
2442 | goto out; | |
55e303ae | 2443 | } |
91447636 A |
2444 | |
2445 | // use the real cnid instead of whatever happened to be there | |
2446 | from_desc.cd_cnid = real_cnid; | |
2447 | hfs_systemfile_unlock(hfsmp, lockflags); | |
55e303ae A |
2448 | } |
2449 | ||
2450 | /* | |
2451 | * Reserve some space in the Catalog file. | |
2452 | */ | |
2453 | if ((error = cat_preflight(hfsmp, CAT_RENAME + CAT_DELETE, &cookie, p))) { | |
2454 | goto out; | |
2455 | } | |
91447636 | 2456 | got_cookie = 1; |
55e303ae A |
2457 | |
2458 | /* | |
91447636 | 2459 | * If the destination exists then it may need to be removed. |
55e303ae | 2460 | */ |
55e303ae | 2461 | if (tvp) { |
55e303ae | 2462 | /* |
2d21ac55 A |
2463 | * When fvp matches tvp they could be case variants |
2464 | * or matching hard links. | |
55e303ae | 2465 | */ |
91447636 | 2466 | if (fvp == tvp) { |
2d21ac55 A |
2467 | if (!(fcp->c_flag & C_HARDLINK)) { |
2468 | goto skip_rm; /* simple case variant */ | |
2469 | ||
2470 | } else if ((fdvp != tdvp) || | |
2471 | (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)) { | |
2472 | goto out; /* matching hardlinks, nothing to do */ | |
2473 | ||
2474 | } else if (hfs_namecmp((const u_int8_t *)fcnp->cn_nameptr, fcnp->cn_namelen, | |
2475 | (const u_int8_t *)tcnp->cn_nameptr, tcnp->cn_namelen) == 0) { | |
2476 | goto skip_rm; /* case-variant hardlink in the same dir */ | |
2477 | } else { | |
2478 | goto out; /* matching hardlink, nothing to do */ | |
91447636 | 2479 | } |
91447636 A |
2480 | } |
2481 | ||
2482 | if (vnode_isdir(tvp)) | |
2483 | error = hfs_removedir(tdvp, tvp, tcnp, HFSRM_SKIP_RESERVE); | |
2484 | else { | |
2d21ac55 | 2485 | error = hfs_removefile(tdvp, tvp, tcnp, 0, HFSRM_SKIP_RESERVE, 0); |
91447636 | 2486 | } |
55e303ae | 2487 | |
55e303ae A |
2488 | if (error) |
2489 | goto out; | |
91447636 | 2490 | tvp_deleted = 1; |
55e303ae | 2491 | } |
2d21ac55 | 2492 | skip_rm: |
55e303ae A |
2493 | /* |
2494 | * All done with tvp and fvp | |
2495 | */ | |
2496 | ||
91447636 | 2497 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK); |
d7e50217 | 2498 | error = cat_rename(hfsmp, &from_desc, &tdcp->c_desc, &to_desc, &out_desc); |
91447636 | 2499 | hfs_systemfile_unlock(hfsmp, lockflags); |
55e303ae A |
2500 | |
2501 | if (error) { | |
d7e50217 | 2502 | goto out; |
55e303ae | 2503 | } |
9bccf70c | 2504 | |
91447636 | 2505 | /* Invalidate negative cache entries in the destination directory */ |
2d21ac55 | 2506 | if (tdcp->c_flag & C_NEG_ENTRIES) { |
91447636 | 2507 | cache_purge_negatives(tdvp); |
2d21ac55 A |
2508 | tdcp->c_flag &= ~C_NEG_ENTRIES; |
2509 | } | |
91447636 | 2510 | |
d7e50217 | 2511 | /* Update cnode's catalog descriptor */ |
91447636 | 2512 | replace_desc(fcp, &out_desc); |
2d21ac55 | 2513 | fcp->c_parentcnid = tdcp->c_fileid; |
91447636 | 2514 | fcp->c_hint = 0; |
9bccf70c | 2515 | |
91447636 | 2516 | hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_RMDIR : VOL_RMFILE, |
d7e50217 | 2517 | (fdcp->c_cnid == kHFSRootFolderID)); |
91447636 | 2518 | hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_MKDIR : VOL_MKFILE, |
d7e50217 A |
2519 | (tdcp->c_cnid == kHFSRootFolderID)); |
2520 | ||
d7e50217 | 2521 | /* Update both parent directories. */ |
b4c24cb9 | 2522 | if (fdvp != tdvp) { |
2d21ac55 A |
2523 | if (vnode_isdir(fvp)) { |
2524 | /* If the source directory has directory hard link | |
2525 | * descendants, set the kHFSHasChildLinkBit in the | |
2526 | * destination parent hierarchy | |
2527 | */ | |
2528 | if ((fcp->c_attr.ca_recflags & kHFSHasChildLinkMask) && | |
2529 | !(tdcp->c_attr.ca_recflags & kHFSHasChildLinkMask)) { | |
2530 | ||
2531 | tdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask; | |
2532 | ||
2533 | error = cat_set_childlinkbit(hfsmp, tdcp->c_parentcnid); | |
2534 | if (error) { | |
2535 | printf ("hfs_vnop_rename: error updating parent chain for %u\n", tdcp->c_cnid); | |
2536 | error = 0; | |
2537 | } | |
2538 | } | |
2539 | INC_FOLDERCOUNT(hfsmp, tdcp->c_attr); | |
2540 | DEC_FOLDERCOUNT(hfsmp, fdcp->c_attr); | |
2541 | } | |
d7e50217 | 2542 | tdcp->c_entries++; |
2d21ac55 | 2543 | tdcp->c_dirchangecnt++; |
d7e50217 A |
2544 | if (fdcp->c_entries > 0) |
2545 | fdcp->c_entries--; | |
2d21ac55 | 2546 | fdcp->c_dirchangecnt++; |
91447636 A |
2547 | fdcp->c_touch_chgtime = TRUE; |
2548 | fdcp->c_touch_modtime = TRUE; | |
2549 | ||
2550 | fdcp->c_flag |= C_FORCEUPDATE; // XXXdbg - force it out! | |
2551 | (void) hfs_update(fdvp, 0); | |
b4c24cb9 | 2552 | } |
d7e50217 | 2553 | tdcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */ |
91447636 A |
2554 | tdcp->c_touch_chgtime = TRUE; |
2555 | tdcp->c_touch_modtime = TRUE; | |
b4c24cb9 | 2556 | |
91447636 A |
2557 | tdcp->c_flag |= C_FORCEUPDATE; // XXXdbg - force it out! |
2558 | (void) hfs_update(tdvp, 0); | |
d7e50217 | 2559 | out: |
91447636 | 2560 | if (got_cookie) { |
55e303ae A |
2561 | cat_postflight(hfsmp, &cookie, p); |
2562 | } | |
b4c24cb9 | 2563 | if (started_tr) { |
91447636 | 2564 | hfs_end_transaction(hfsmp); |
b4c24cb9 A |
2565 | } |
2566 | ||
55e303ae A |
2567 | /* Note that if hfs_removedir or hfs_removefile was invoked above they will already have |
2568 | generated a NOTE_WRITE for tdvp and a NOTE_DELETE for tvp. | |
2569 | */ | |
2570 | if (error == 0) { | |
2571 | HFS_KNOTE(fvp, NOTE_RENAME); | |
2572 | HFS_KNOTE(fdvp, NOTE_WRITE); | |
2573 | if (tdvp != fdvp) HFS_KNOTE(tdvp, NOTE_WRITE); | |
2574 | }; | |
b4c24cb9 | 2575 | |
2d21ac55 A |
2576 | fdcp->c_flag &= ~C_DIR_MODIFICATION; |
2577 | wakeup((caddr_t)&fdcp->c_flag); | |
2578 | if (fdvp != tdvp) { | |
2579 | tdcp->c_flag &= ~C_DIR_MODIFICATION; | |
2580 | wakeup((caddr_t)&tdcp->c_flag); | |
2581 | } | |
2582 | ||
91447636 | 2583 | if (took_trunc_lock) |
2d21ac55 | 2584 | hfs_unlock_truncate(VTOC(tvp), TRUE); |
91447636 A |
2585 | |
2586 | hfs_unlockfour(fdcp, fcp, tdcp, tcp); | |
b4c24cb9 | 2587 | |
d7e50217 | 2588 | /* After tvp is removed the only acceptable error is EIO */ |
55e303ae | 2589 | if (error && tvp_deleted) |
d7e50217 A |
2590 | error = EIO; |
2591 | ||
2592 | return (error); | |
9bccf70c A |
2593 | } |
2594 | ||
2595 | ||
9bccf70c | 2596 | /* |
91447636 A |
2597 | * Make a directory. |
2598 | */ | |
9bccf70c | 2599 | static int |
91447636 | 2600 | hfs_vnop_mkdir(struct vnop_mkdir_args *ap) |
9bccf70c | 2601 | { |
91447636 A |
2602 | /***** HACK ALERT ********/ |
2603 | ap->a_cnp->cn_flags |= MAKEENTRY; | |
2604 | return hfs_makenode(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap, ap->a_context); | |
9bccf70c A |
2605 | } |
2606 | ||
2607 | ||
2608 | /* | |
91447636 A |
2609 | * Create a symbolic link. |
2610 | */ | |
9bccf70c | 2611 | static int |
91447636 | 2612 | hfs_vnop_symlink(struct vnop_symlink_args *ap) |
9bccf70c | 2613 | { |
91447636 A |
2614 | struct vnode **vpp = ap->a_vpp; |
2615 | struct vnode *dvp = ap->a_dvp; | |
2616 | struct vnode *vp = NULL; | |
2d21ac55 | 2617 | struct cnode *cp = NULL; |
b4c24cb9 | 2618 | struct hfsmount *hfsmp; |
9bccf70c | 2619 | struct filefork *fp; |
9bccf70c | 2620 | struct buf *bp = NULL; |
91447636 A |
2621 | char *datap; |
2622 | int started_tr = 0; | |
2d21ac55 A |
2623 | u_int32_t len; |
2624 | int error; | |
9bccf70c A |
2625 | |
2626 | /* HFS standard disks don't support symbolic links */ | |
91447636 A |
2627 | if (VTOVCB(dvp)->vcbSigWord != kHFSPlusSigWord) |
2628 | return (ENOTSUP); | |
9bccf70c A |
2629 | |
2630 | /* Check for empty target name */ | |
91447636 | 2631 | if (ap->a_target[0] == 0) |
9bccf70c | 2632 | return (EINVAL); |
b4c24cb9 | 2633 | |
2d21ac55 A |
2634 | hfsmp = VTOHFS(dvp); |
2635 | len = strlen(ap->a_target); | |
2636 | ||
2637 | /* Check for free space */ | |
2638 | if (((u_int64_t)hfs_freeblks(hfsmp, 0) * (u_int64_t)hfsmp->blockSize) < len) { | |
2639 | return (ENOSPC); | |
2640 | } | |
2641 | ||
9bccf70c | 2642 | /* Create the vnode */ |
91447636 A |
2643 | ap->a_vap->va_mode |= S_IFLNK; |
2644 | if ((error = hfs_makenode(dvp, vpp, ap->a_cnp, ap->a_vap, ap->a_context))) { | |
2645 | goto out; | |
b4c24cb9 | 2646 | } |
9bccf70c | 2647 | vp = *vpp; |
2d21ac55 A |
2648 | if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) { |
2649 | goto out; | |
2650 | } | |
2651 | cp = VTOC(vp); | |
9bccf70c | 2652 | fp = VTOF(vp); |
2d21ac55 A |
2653 | |
2654 | if (cp->c_flag & (C_NOEXISTS | C_DELETED)) { | |
2655 | goto out; | |
2656 | } | |
9bccf70c | 2657 | |
d7e50217 | 2658 | #if QUOTA |
2d21ac55 | 2659 | (void)hfs_getinoquota(cp); |
d7e50217 A |
2660 | #endif /* QUOTA */ |
2661 | ||
91447636 A |
2662 | if ((error = hfs_start_transaction(hfsmp)) != 0) { |
2663 | goto out; | |
b4c24cb9 | 2664 | } |
91447636 | 2665 | started_tr = 1; |
b4c24cb9 | 2666 | |
91447636 A |
2667 | /* |
2668 | * Allocate space for the link. | |
2669 | * | |
2670 | * Since we're already inside a transaction, | |
2671 | * tell hfs_truncate to skip the ubc_setsize. | |
2672 | * | |
2673 | * Don't need truncate lock since a symlink is treated as a system file. | |
2674 | */ | |
2675 | error = hfs_truncate(vp, len, IO_NOZEROFILL, 1, ap->a_context); | |
2d21ac55 A |
2676 | |
2677 | /* On errors, remove the symlink file */ | |
2678 | if (error) { | |
2679 | /* | |
2680 | * End the transaction so we don't re-take the cnode lock | |
2681 | * below while inside a transaction (lock order violation). | |
2682 | */ | |
2683 | hfs_end_transaction(hfsmp); | |
2684 | ||
2685 | /* hfs_removefile() requires holding the truncate lock */ | |
2686 | hfs_unlock(cp); | |
2687 | hfs_lock_truncate(cp, TRUE); | |
2688 | hfs_lock(cp, HFS_FORCE_LOCK); | |
2689 | ||
2690 | if (hfs_start_transaction(hfsmp) != 0) { | |
2691 | started_tr = 0; | |
2692 | hfs_unlock_truncate(cp, TRUE); | |
2693 | goto out; | |
2694 | } | |
2695 | ||
2696 | (void) hfs_removefile(dvp, vp, ap->a_cnp, 0, 0, 0); | |
2697 | hfs_unlock_truncate(cp, TRUE); | |
2698 | goto out; | |
2699 | } | |
9bccf70c A |
2700 | |
2701 | /* Write the link to disk */ | |
91447636 | 2702 | bp = buf_getblk(vp, (daddr64_t)0, roundup((int)fp->ff_size, VTOHFS(vp)->hfs_phys_block_size), |
9bccf70c | 2703 | 0, 0, BLK_META); |
b4c24cb9 A |
2704 | if (hfsmp->jnl) { |
2705 | journal_modify_block_start(hfsmp->jnl, bp); | |
2706 | } | |
91447636 A |
2707 | datap = (char *)buf_dataptr(bp); |
2708 | bzero(datap, buf_size(bp)); | |
2709 | bcopy(ap->a_target, datap, len); | |
2710 | ||
b4c24cb9 | 2711 | if (hfsmp->jnl) { |
2d21ac55 | 2712 | journal_modify_block_end(hfsmp->jnl, bp, NULL, NULL); |
b4c24cb9 | 2713 | } else { |
91447636 | 2714 | buf_bawrite(bp); |
b4c24cb9 | 2715 | } |
91447636 A |
2716 | /* |
2717 | * We defered the ubc_setsize for hfs_truncate | |
2718 | * since we were inside a transaction. | |
2719 | * | |
2720 | * We don't need to drop the cnode lock here | |
2721 | * since this is a symlink. | |
2722 | */ | |
2723 | ubc_setsize(vp, len); | |
9bccf70c | 2724 | out: |
91447636 A |
2725 | if (started_tr) |
2726 | hfs_end_transaction(hfsmp); | |
2d21ac55 A |
2727 | if ((cp != NULL) && (vp != NULL)) { |
2728 | hfs_unlock(cp); | |
2729 | } | |
2730 | if (error) { | |
2731 | if (vp) { | |
2732 | vnode_put(vp); | |
2733 | } | |
2734 | *vpp = NULL; | |
b4c24cb9 | 2735 | } |
9bccf70c A |
2736 | return (error); |
2737 | } | |
2738 | ||
2739 | ||
91447636 A |
2740 | /* structures to hold a "." or ".." directory entry */ |
2741 | struct hfs_stddotentry { | |
2742 | u_int32_t d_fileno; /* unique file number */ | |
2743 | u_int16_t d_reclen; /* length of this structure */ | |
2744 | u_int8_t d_type; /* dirent file type */ | |
2745 | u_int8_t d_namlen; /* len of filename */ | |
2746 | char d_name[4]; /* "." or ".." */ | |
9bccf70c A |
2747 | }; |
2748 | ||
91447636 A |
2749 | struct hfs_extdotentry { |
2750 | u_int64_t d_fileno; /* unique file number */ | |
2751 | u_int64_t d_seekoff; /* seek offset (optional, used by servers) */ | |
2752 | u_int16_t d_reclen; /* length of this structure */ | |
2753 | u_int16_t d_namlen; /* len of filename */ | |
2754 | u_int8_t d_type; /* dirent file type */ | |
2755 | u_char d_name[3]; /* "." or ".." */ | |
2756 | }; | |
9bccf70c | 2757 | |
91447636 A |
2758 | typedef union { |
2759 | struct hfs_stddotentry std; | |
2760 | struct hfs_extdotentry ext; | |
2761 | } hfs_dotentry_t; | |
9bccf70c A |
2762 | |
2763 | /* | |
91447636 A |
2764 | * hfs_vnop_readdir reads directory entries into the buffer pointed |
2765 | * to by uio, in a filesystem independent format. Up to uio_resid | |
2766 | * bytes of data can be transferred. The data in the buffer is a | |
2767 | * series of packed dirent structures where each one contains the | |
2768 | * following entries: | |
2769 | * | |
2770 | * u_int32_t d_fileno; // file number of entry | |
2771 | * u_int16_t d_reclen; // length of this record | |
2772 | * u_int8_t d_type; // file type | |
2773 | * u_int8_t d_namlen; // length of string in d_name | |
2774 | * char d_name[MAXNAMELEN+1]; // null terminated file name | |
2775 | * | |
2776 | * The current position (uio_offset) refers to the next block of | |
2777 | * entries. The offset can only be set to a value previously | |
2778 | * returned by hfs_vnop_readdir or zero. This offset does not have | |
2779 | * to match the number of bytes returned (in uio_resid). | |
2780 | * | |
2781 | * In fact, the offset used by HFS is essentially an index (26 bits) | |
2782 | * with a tag (6 bits). The tag is for associating the next request | |
2783 | * with the current request. This enables us to have multiple threads | |
2784 | * reading the directory while the directory is also being modified. | |
2785 | * | |
2786 | * Each tag/index pair is tied to a unique directory hint. The hint | |
2787 | * contains information (filename) needed to build the catalog b-tree | |
2788 | * key for finding the next set of entries. | |
2d21ac55 A |
2789 | * |
2790 | * If the directory is marked as deleted-but-in-use (cp->c_flag & C_DELETED), | |
2791 | * do NOT synthesize entries for "." and "..". | |
9bccf70c | 2792 | */ |
9bccf70c | 2793 | static int |
91447636 A |
2794 | hfs_vnop_readdir(ap) |
2795 | struct vnop_readdir_args /* { | |
2796 | vnode_t a_vp; | |
2797 | uio_t a_uio; | |
2798 | int a_flags; | |
2799 | int *a_eofflag; | |
2800 | int *a_numdirent; | |
2801 | vfs_context_t a_context; | |
9bccf70c A |
2802 | } */ *ap; |
2803 | { | |
91447636 A |
2804 | struct vnode *vp = ap->a_vp; |
2805 | uio_t uio = ap->a_uio; | |
2806 | struct cnode *cp; | |
2807 | struct hfsmount *hfsmp; | |
2808 | directoryhint_t *dirhint = NULL; | |
2809 | directoryhint_t localhint; | |
2810 | off_t offset; | |
2811 | off_t startoffset; | |
2812 | int error = 0; | |
9bccf70c | 2813 | int eofflag = 0; |
91447636 A |
2814 | user_addr_t user_start = 0; |
2815 | user_size_t user_len = 0; | |
2816 | int index; | |
2817 | unsigned int tag; | |
2818 | int items; | |
2819 | int lockflags; | |
2820 | int extended; | |
2821 | int nfs_cookies; | |
2822 | caddr_t bufstart; | |
2823 | cnid_t cnid_hint = 0; | |
2824 | ||
2825 | items = 0; | |
2826 | startoffset = offset = uio_offset(uio); | |
2827 | bufstart = CAST_DOWN(caddr_t, uio_iov_base(uio)); | |
2828 | extended = (ap->a_flags & VNODE_READDIR_EXTENDED); | |
2829 | nfs_cookies = extended && (ap->a_flags & VNODE_READDIR_REQSEEKOFF); | |
2830 | ||
2831 | /* Sanity check the uio data. */ | |
2832 | if ((uio_iovcnt(uio) > 1) || | |
2833 | (uio_resid(uio) < (int)sizeof(struct dirent))) { | |
2834 | return (EINVAL); | |
2835 | } | |
2836 | /* Note that the dirhint calls require an exclusive lock. */ | |
2837 | if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) | |
2838 | return (error); | |
2839 | cp = VTOC(vp); | |
2840 | hfsmp = VTOHFS(vp); | |
55e303ae | 2841 | |
91447636 A |
2842 | /* Pick up cnid hint (if any). */ |
2843 | if (nfs_cookies) { | |
2844 | cnid_hint = (cnid_t)(uio_offset(uio) >> 32); | |
2845 | uio_setoffset(uio, uio_offset(uio) & 0x00000000ffffffffLL); | |
3a60a9f5 A |
2846 | if (cnid_hint == INT_MAX) { /* searching pass the last item */ |
2847 | eofflag = 1; | |
2848 | goto out; | |
2849 | } | |
91447636 A |
2850 | } |
2851 | /* | |
2d21ac55 A |
2852 | * Synthesize entries for "." and "..", unless the directory has |
2853 | * been deleted, but not closed yet (lazy delete in progress). | |
91447636 | 2854 | */ |
2d21ac55 | 2855 | if (offset == 0 && !(cp->c_flag & C_DELETED)) { |
91447636 A |
2856 | hfs_dotentry_t dotentry[2]; |
2857 | size_t uiosize; | |
2858 | ||
2859 | if (extended) { | |
2860 | struct hfs_extdotentry *entry = &dotentry[0].ext; | |
2861 | ||
2862 | entry->d_fileno = cp->c_cnid; | |
2863 | entry->d_reclen = sizeof(struct hfs_extdotentry); | |
2864 | entry->d_type = DT_DIR; | |
2865 | entry->d_namlen = 1; | |
2866 | entry->d_name[0] = '.'; | |
2867 | entry->d_name[1] = '\0'; | |
2868 | entry->d_name[2] = '\0'; | |
2869 | entry->d_seekoff = 1; | |
2870 | ||
2871 | ++entry; | |
2872 | entry->d_fileno = cp->c_parentcnid; | |
2873 | entry->d_reclen = sizeof(struct hfs_extdotentry); | |
2874 | entry->d_type = DT_DIR; | |
2875 | entry->d_namlen = 2; | |
2876 | entry->d_name[0] = '.'; | |
2877 | entry->d_name[1] = '.'; | |
2878 | entry->d_name[2] = '\0'; | |
2879 | entry->d_seekoff = 2; | |
2880 | uiosize = 2 * sizeof(struct hfs_extdotentry); | |
2881 | } else { | |
2882 | struct hfs_stddotentry *entry = &dotentry[0].std; | |
2883 | ||
2884 | entry->d_fileno = cp->c_cnid; | |
2885 | entry->d_reclen = sizeof(struct hfs_stddotentry); | |
2886 | entry->d_type = DT_DIR; | |
2887 | entry->d_namlen = 1; | |
2888 | *(int *)&entry->d_name[0] = 0; | |
2889 | entry->d_name[0] = '.'; | |
2890 | ||
2891 | ++entry; | |
2892 | entry->d_fileno = cp->c_parentcnid; | |
2893 | entry->d_reclen = sizeof(struct hfs_stddotentry); | |
2894 | entry->d_type = DT_DIR; | |
2895 | entry->d_namlen = 2; | |
2896 | *(int *)&entry->d_name[0] = 0; | |
2897 | entry->d_name[0] = '.'; | |
2898 | entry->d_name[1] = '.'; | |
2899 | uiosize = 2 * sizeof(struct hfs_stddotentry); | |
2900 | } | |
2901 | if ((error = uiomove((caddr_t)&dotentry, uiosize, uio))) { | |
2902 | goto out; | |
2903 | } | |
2904 | offset += 2; | |
2905 | } | |
9bccf70c | 2906 | |
91447636 A |
2907 | /* If there are no real entries then we're done. */ |
2908 | if (cp->c_entries == 0) { | |
2909 | error = 0; | |
2910 | eofflag = 1; | |
2911 | uio_setoffset(uio, offset); | |
2912 | goto seekoffcalc; | |
2913 | } | |
2914 | ||
2915 | // | |
b4c24cb9 A |
2916 | // We have to lock the user's buffer here so that we won't |
2917 | // fault on it after we've acquired a shared lock on the | |
2918 | // catalog file. The issue is that you can get a 3-way | |
2919 | // deadlock if someone else starts a transaction and then | |
2920 | // tries to lock the catalog file but can't because we're | |
2921 | // here and we can't service our page fault because VM is | |
2922 | // blocked trying to start a transaction as a result of | |
2923 | // trying to free up pages for our page fault. It's messy | |
2d21ac55 | 2924 | // but it does happen on dual-processors that are paging |
b4c24cb9 A |
2925 | // heavily (see radar 3082639 for more info). By locking |
2926 | // the buffer up-front we prevent ourselves from faulting | |
2927 | // while holding the shared catalog file lock. | |
2928 | // | |
2929 | // Fortunately this and hfs_search() are the only two places | |
2930 | // currently (10/30/02) that can fault on user data with a | |
2931 | // shared lock on the catalog file. | |
2932 | // | |
91447636 A |
2933 | if (hfsmp->jnl && uio_isuserspace(uio)) { |
2934 | user_start = uio_curriovbase(uio); | |
2935 | user_len = uio_curriovlen(uio); | |
b4c24cb9 | 2936 | |
91447636 A |
2937 | if ((error = vslock(user_start, user_len)) != 0) { |
2938 | user_start = 0; | |
2939 | goto out; | |
b4c24cb9 A |
2940 | } |
2941 | } | |
91447636 A |
2942 | /* Convert offset into a catalog directory index. */ |
2943 | index = (offset & HFS_INDEX_MASK) - 2; | |
2944 | tag = offset & ~HFS_INDEX_MASK; | |
2945 | ||
2946 | /* Lock catalog during cat_findname and cat_getdirentries. */ | |
2947 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); | |
2948 | ||
2949 | /* When called from NFS, try and resolve a cnid hint. */ | |
2950 | if (nfs_cookies && cnid_hint != 0) { | |
2951 | if (cat_findname(hfsmp, cnid_hint, &localhint.dh_desc) == 0) { | |
2d21ac55 | 2952 | if ( localhint.dh_desc.cd_parentcnid == cp->c_fileid) { |
91447636 A |
2953 | localhint.dh_index = index - 1; |
2954 | localhint.dh_time = 0; | |
b36670ce | 2955 | bzero(&localhint.dh_link, sizeof(localhint.dh_link)); |
91447636 A |
2956 | dirhint = &localhint; /* don't forget to release the descriptor */ |
2957 | } else { | |
2958 | cat_releasedesc(&localhint.dh_desc); | |
2959 | } | |
9bccf70c | 2960 | } |
9bccf70c A |
2961 | } |
2962 | ||
91447636 A |
2963 | /* Get a directory hint (cnode must be locked exclusive) */ |
2964 | if (dirhint == NULL) { | |
2d21ac55 | 2965 | dirhint = hfs_getdirhint(cp, ((index - 1) & HFS_INDEX_MASK) | tag, 0); |
55e303ae | 2966 | |
91447636 A |
2967 | /* Hide tag from catalog layer. */ |
2968 | dirhint->dh_index &= HFS_INDEX_MASK; | |
2969 | if (dirhint->dh_index == HFS_INDEX_MASK) { | |
2970 | dirhint->dh_index = -1; | |
ccc36f2f | 2971 | } |
55e303ae | 2972 | } |
91447636 | 2973 | |
2d21ac55 A |
2974 | if (index == 0) { |
2975 | dirhint->dh_threadhint = cp->c_dirthreadhint; | |
2976 | } | |
2977 | ||
91447636 | 2978 | /* Pack the buffer with dirent entries. */ |
3a60a9f5 | 2979 | error = cat_getdirentries(hfsmp, cp->c_entries, dirhint, uio, extended, &items, &eofflag); |
55e303ae | 2980 | |
2d21ac55 A |
2981 | if (index == 0 && error == 0) { |
2982 | cp->c_dirthreadhint = dirhint->dh_threadhint; | |
2983 | } | |
2984 | ||
91447636 A |
2985 | hfs_systemfile_unlock(hfsmp, lockflags); |
2986 | ||
2987 | if (error != 0) { | |
2988 | goto out; | |
2989 | } | |
2990 | ||
2991 | /* Get index to the next item */ | |
2992 | index += items; | |
2993 | ||
2994 | if (items >= (int)cp->c_entries) { | |
9bccf70c | 2995 | eofflag = 1; |
9bccf70c A |
2996 | } |
2997 | ||
91447636 A |
2998 | /* Convert catalog directory index back into an offset. */ |
2999 | while (tag == 0) | |
3000 | tag = (++cp->c_dirhinttag) << HFS_INDEX_BITS; | |
3001 | uio_setoffset(uio, (index + 2) | tag); | |
3002 | dirhint->dh_index |= tag; | |
9bccf70c | 3003 | |
91447636 A |
3004 | seekoffcalc: |
3005 | cp->c_touch_acctime = TRUE; | |
9bccf70c | 3006 | |
91447636 A |
3007 | if (ap->a_numdirent) { |
3008 | if (startoffset == 0) | |
3009 | items += 2; | |
3010 | *ap->a_numdirent = items; | |
9bccf70c | 3011 | } |
9bccf70c | 3012 | |
91447636 | 3013 | out: |
b4c24cb9 A |
3014 | if (hfsmp->jnl && user_start) { |
3015 | vsunlock(user_start, user_len, TRUE); | |
3016 | } | |
91447636 A |
3017 | /* If we didn't do anything then go ahead and dump the hint. */ |
3018 | if ((dirhint != NULL) && | |
3019 | (dirhint != &localhint) && | |
3020 | (uio_offset(uio) == startoffset)) { | |
3021 | hfs_reldirhint(cp, dirhint); | |
3022 | eofflag = 1; | |
3023 | } | |
3024 | if (ap->a_eofflag) { | |
9bccf70c | 3025 | *ap->a_eofflag = eofflag; |
91447636 A |
3026 | } |
3027 | if (dirhint == &localhint) { | |
3028 | cat_releasedesc(&localhint.dh_desc); | |
3029 | } | |
3030 | hfs_unlock(cp); | |
3031 | return (error); | |
9bccf70c A |
3032 | } |
3033 | ||
3034 | ||
3035 | /* | |
91447636 A |
3036 | * Read contents of a symbolic link. |
3037 | */ | |
9bccf70c | 3038 | static int |
91447636 A |
3039 | hfs_vnop_readlink(ap) |
3040 | struct vnop_readlink_args /* { | |
9bccf70c A |
3041 | struct vnode *a_vp; |
3042 | struct uio *a_uio; | |
91447636 | 3043 | vfs_context_t a_context; |
9bccf70c A |
3044 | } */ *ap; |
3045 | { | |
9bccf70c A |
3046 | struct vnode *vp = ap->a_vp; |
3047 | struct cnode *cp; | |
3048 | struct filefork *fp; | |
91447636 | 3049 | int error; |
9bccf70c | 3050 | |
91447636 | 3051 | if (!vnode_islnk(vp)) |
9bccf70c A |
3052 | return (EINVAL); |
3053 | ||
91447636 A |
3054 | if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) |
3055 | return (error); | |
9bccf70c A |
3056 | cp = VTOC(vp); |
3057 | fp = VTOF(vp); | |
3058 | ||
3059 | /* Zero length sym links are not allowed */ | |
3060 | if (fp->ff_size == 0 || fp->ff_size > MAXPATHLEN) { | |
2d21ac55 | 3061 | printf("hfs: zero length symlink on fileid %d\n", cp->c_fileid); |
91447636 A |
3062 | error = EINVAL; |
3063 | goto exit; | |
9bccf70c A |
3064 | } |
3065 | ||
3066 | /* Cache the path so we don't waste buffer cache resources */ | |
3067 | if (fp->ff_symlinkptr == NULL) { | |
3068 | struct buf *bp = NULL; | |
3069 | ||
3070 | MALLOC(fp->ff_symlinkptr, char *, fp->ff_size, M_TEMP, M_WAITOK); | |
91447636 A |
3071 | error = (int)buf_meta_bread(vp, (daddr64_t)0, |
3072 | roundup((int)fp->ff_size, | |
3073 | VTOHFS(vp)->hfs_phys_block_size), | |
3074 | vfs_context_ucred(ap->a_context), &bp); | |
3075 | if (error) { | |
9bccf70c | 3076 | if (bp) |
91447636 | 3077 | buf_brelse(bp); |
9bccf70c A |
3078 | if (fp->ff_symlinkptr) { |
3079 | FREE(fp->ff_symlinkptr, M_TEMP); | |
3080 | fp->ff_symlinkptr = NULL; | |
3081 | } | |
91447636 | 3082 | goto exit; |
9bccf70c | 3083 | } |
91447636 | 3084 | bcopy((char *)buf_dataptr(bp), fp->ff_symlinkptr, (size_t)fp->ff_size); |
9bccf70c | 3085 | |
91447636 A |
3086 | if (VTOHFS(vp)->jnl && (buf_flags(bp) & B_LOCKED) == 0) { |
3087 | buf_markinvalid(bp); /* data no longer needed */ | |
3088 | } | |
3089 | buf_brelse(bp); | |
55e303ae | 3090 | } |
91447636 | 3091 | error = uiomove((caddr_t)fp->ff_symlinkptr, (int)fp->ff_size, ap->a_uio); |
9bccf70c | 3092 | |
91447636 A |
3093 | /* |
3094 | * Keep track blocks read | |
3095 | */ | |
3096 | if ((VTOHFS(vp)->hfc_stage == HFC_RECORDING) && (error == 0)) { | |
3097 | ||
3098 | /* | |
3099 | * If this file hasn't been seen since the start of | |
3100 | * the current sampling period then start over. | |
3101 | */ | |
3102 | if (cp->c_atime < VTOHFS(vp)->hfc_timebase) | |
3103 | VTOF(vp)->ff_bytesread = fp->ff_size; | |
3104 | else | |
3105 | VTOF(vp)->ff_bytesread += fp->ff_size; | |
3106 | ||
3107 | // if (VTOF(vp)->ff_bytesread > fp->ff_size) | |
3108 | // cp->c_touch_acctime = TRUE; | |
3109 | } | |
9bccf70c | 3110 | |
91447636 A |
3111 | exit: |
3112 | hfs_unlock(cp); | |
3113 | return (error); | |
9bccf70c A |
3114 | } |
3115 | ||
3116 | ||
3117 | /* | |
91447636 A |
3118 | * Get configurable pathname variables. |
3119 | */ | |
9bccf70c | 3120 | static int |
91447636 A |
3121 | hfs_vnop_pathconf(ap) |
3122 | struct vnop_pathconf_args /* { | |
9bccf70c A |
3123 | struct vnode *a_vp; |
3124 | int a_name; | |
3125 | int *a_retval; | |
91447636 | 3126 | vfs_context_t a_context; |
9bccf70c A |
3127 | } */ *ap; |
3128 | { | |
9bccf70c A |
3129 | switch (ap->a_name) { |
3130 | case _PC_LINK_MAX: | |
91447636 | 3131 | if (VTOHFS(ap->a_vp)->hfs_flags & HFS_STANDARD) |
9bccf70c | 3132 | *ap->a_retval = 1; |
91447636 A |
3133 | else |
3134 | *ap->a_retval = HFS_LINK_MAX; | |
9bccf70c A |
3135 | break; |
3136 | case _PC_NAME_MAX: | |
91447636 A |
3137 | if (VTOHFS(ap->a_vp)->hfs_flags & HFS_STANDARD) |
3138 | *ap->a_retval = kHFSMaxFileNameChars; /* 255 */ | |
3139 | else | |
3140 | *ap->a_retval = kHFSPlusMaxFileNameChars; /* 31 */ | |
9bccf70c A |
3141 | break; |
3142 | case _PC_PATH_MAX: | |
91447636 | 3143 | *ap->a_retval = PATH_MAX; /* 1024 */ |
9bccf70c | 3144 | break; |
55e303ae A |
3145 | case _PC_PIPE_BUF: |
3146 | *ap->a_retval = PIPE_BUF; | |
3147 | break; | |
9bccf70c | 3148 | case _PC_CHOWN_RESTRICTED: |
2d21ac55 | 3149 | *ap->a_retval = 200112; /* _POSIX_CHOWN_RESTRICTED */ |
9bccf70c A |
3150 | break; |
3151 | case _PC_NO_TRUNC: | |
2d21ac55 | 3152 | *ap->a_retval = 200112; /* _POSIX_NO_TRUNC */ |
9bccf70c A |
3153 | break; |
3154 | case _PC_NAME_CHARS_MAX: | |
3155 | *ap->a_retval = kHFSPlusMaxFileNameChars; | |
3156 | break; | |
3157 | case _PC_CASE_SENSITIVE: | |
55e303ae A |
3158 | if (VTOHFS(ap->a_vp)->hfs_flags & HFS_CASE_SENSITIVE) |
3159 | *ap->a_retval = 1; | |
3160 | else | |
3161 | *ap->a_retval = 0; | |
9bccf70c A |
3162 | break; |
3163 | case _PC_CASE_PRESERVING: | |
3164 | *ap->a_retval = 1; | |
3165 | break; | |
2d21ac55 A |
3166 | case _PC_FILESIZEBITS: |
3167 | *ap->a_retval = 64; /* number of bits to store max file size */ | |
3168 | break; | |
9bccf70c | 3169 | default: |
55e303ae | 3170 | return (EINVAL); |
9bccf70c A |
3171 | } |
3172 | ||
91447636 | 3173 | return (0); |
9bccf70c A |
3174 | } |
3175 | ||
3176 | ||
9bccf70c | 3177 | /* |
91447636 A |
3178 | * Update a cnode's on-disk metadata. |
3179 | * | |
3180 | * If waitfor is set, then wait for the disk write of | |
3181 | * the node to complete. | |
3182 | * | |
3183 | * The cnode must be locked exclusive | |
9bccf70c | 3184 | */ |
91447636 A |
3185 | __private_extern__ |
3186 | int | |
3187 | hfs_update(struct vnode *vp, __unused int waitfor) | |
9bccf70c | 3188 | { |
91447636 | 3189 | struct cnode *cp = VTOC(vp); |
9bccf70c A |
3190 | struct proc *p; |
3191 | struct cat_fork *dataforkp = NULL; | |
3192 | struct cat_fork *rsrcforkp = NULL; | |
3193 | struct cat_fork datafork; | |
b4c24cb9 | 3194 | struct hfsmount *hfsmp; |
91447636 | 3195 | int lockflags; |
9bccf70c A |
3196 | int error; |
3197 | ||
91447636 | 3198 | p = current_proc(); |
b4c24cb9 A |
3199 | hfsmp = VTOHFS(vp); |
3200 | ||
2d21ac55 A |
3201 | if (((vnode_issystem(vp) && (cp->c_cnid < kHFSFirstUserCatalogNodeID))) || |
3202 | hfsmp->hfs_catalog_vp == NULL){ | |
91447636 A |
3203 | return (0); |
3204 | } | |
3205 | if ((hfsmp->hfs_flags & HFS_READ_ONLY) || (cp->c_mode == 0)) { | |
3206 | cp->c_flag &= ~C_MODIFIED; | |
3207 | cp->c_touch_acctime = 0; | |
3208 | cp->c_touch_chgtime = 0; | |
3209 | cp->c_touch_modtime = 0; | |
9bccf70c A |
3210 | return (0); |
3211 | } | |
3212 | ||
91447636 | 3213 | hfs_touchtimes(hfsmp, cp); |
9bccf70c A |
3214 | |
3215 | /* Nothing to update. */ | |
91447636 | 3216 | if ((cp->c_flag & (C_MODIFIED | C_FORCEUPDATE)) == 0) { |
9bccf70c | 3217 | return (0); |
b4c24cb9 | 3218 | } |
9bccf70c A |
3219 | |
3220 | if (cp->c_datafork) | |
3221 | dataforkp = &cp->c_datafork->ff_data; | |
3222 | if (cp->c_rsrcfork) | |
3223 | rsrcforkp = &cp->c_rsrcfork->ff_data; | |
3224 | ||
9bccf70c A |
3225 | /* |
3226 | * For delayed allocations updates are | |
3227 | * postponed until an fsync or the file | |
3228 | * gets written to disk. | |
3229 | * | |
3230 | * Deleted files can defer meta data updates until inactive. | |
55e303ae A |
3231 | * |
3232 | * If we're ever called with the C_FORCEUPDATE flag though | |
3233 | * we have to do the update. | |
9bccf70c | 3234 | */ |
55e303ae | 3235 | if (ISSET(cp->c_flag, C_FORCEUPDATE) == 0 && |
2d21ac55 | 3236 | (ISSET(cp->c_flag, C_DELETED) || |
9bccf70c | 3237 | (dataforkp && cp->c_datafork->ff_unallocblocks) || |
55e303ae | 3238 | (rsrcforkp && cp->c_rsrcfork->ff_unallocblocks))) { |
91447636 | 3239 | // cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_UPDATE); |
9bccf70c | 3240 | cp->c_flag |= C_MODIFIED; |
b4c24cb9 | 3241 | |
55e303ae A |
3242 | HFS_KNOTE(vp, NOTE_ATTRIB); |
3243 | ||
9bccf70c A |
3244 | return (0); |
3245 | } | |
3246 | ||
91447636 A |
3247 | if ((error = hfs_start_transaction(hfsmp)) != 0) { |
3248 | return error; | |
b4c24cb9 | 3249 | } |
b4c24cb9 | 3250 | |
9bccf70c A |
3251 | /* |
3252 | * For files with invalid ranges (holes) the on-disk | |
3253 | * field representing the size of the file (cf_size) | |
3254 | * must be no larger than the start of the first hole. | |
3255 | */ | |
3256 | if (dataforkp && !CIRCLEQ_EMPTY(&cp->c_datafork->ff_invalidranges)) { | |
3257 | bcopy(dataforkp, &datafork, sizeof(datafork)); | |
3258 | datafork.cf_size = CIRCLEQ_FIRST(&cp->c_datafork->ff_invalidranges)->rl_start; | |
3259 | dataforkp = &datafork; | |
55e303ae A |
3260 | } else if (dataforkp && (cp->c_datafork->ff_unallocblocks != 0)) { |
3261 | // always make sure the block count and the size | |
3262 | // of the file match the number of blocks actually | |
3263 | // allocated to the file on disk | |
3264 | bcopy(dataforkp, &datafork, sizeof(datafork)); | |
3265 | // make sure that we don't assign a negative block count | |
3266 | if (cp->c_datafork->ff_blocks < cp->c_datafork->ff_unallocblocks) { | |
3267 | panic("hfs: ff_blocks %d is less than unalloc blocks %d\n", | |
3268 | cp->c_datafork->ff_blocks, cp->c_datafork->ff_unallocblocks); | |
3269 | } | |
3270 | datafork.cf_blocks = (cp->c_datafork->ff_blocks - cp->c_datafork->ff_unallocblocks); | |
3271 | datafork.cf_size = datafork.cf_blocks * HFSTOVCB(hfsmp)->blockSize; | |
3272 | dataforkp = &datafork; | |
9bccf70c A |
3273 | } |
3274 | ||
3275 | /* | |
3276 | * Lock the Catalog b-tree file. | |
9bccf70c | 3277 | */ |
2d21ac55 | 3278 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK); |
9bccf70c A |
3279 | |
3280 | /* XXX - waitfor is not enforced */ | |
b4c24cb9 | 3281 | error = cat_update(hfsmp, &cp->c_desc, &cp->c_attr, dataforkp, rsrcforkp); |
9bccf70c | 3282 | |
91447636 | 3283 | hfs_systemfile_unlock(hfsmp, lockflags); |
b4c24cb9 | 3284 | |
55e303ae | 3285 | /* After the updates are finished, clear the flags */ |
91447636 | 3286 | cp->c_flag &= ~(C_MODIFIED | C_FORCEUPDATE); |
55e303ae | 3287 | |
91447636 | 3288 | hfs_end_transaction(hfsmp); |
9bccf70c | 3289 | |
55e303ae A |
3290 | HFS_KNOTE(vp, NOTE_ATTRIB); |
3291 | ||
9bccf70c A |
3292 | return (error); |
3293 | } | |
3294 | ||
3295 | /* | |
3296 | * Allocate a new node | |
2d21ac55 | 3297 | * Note - Function does not create and return a vnode for whiteout creation. |
9bccf70c A |
3298 | */ |
3299 | static int | |
91447636 A |
3300 | hfs_makenode(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, |
3301 | struct vnode_attr *vap, vfs_context_t ctx) | |
9bccf70c | 3302 | { |
91447636 | 3303 | struct cnode *cp = NULL; |
9bccf70c A |
3304 | struct cnode *dcp; |
3305 | struct vnode *tvp; | |
3306 | struct hfsmount *hfsmp; | |
9bccf70c A |
3307 | struct cat_desc in_desc, out_desc; |
3308 | struct cat_attr attr; | |
91447636 | 3309 | struct timeval tv; |
91447636 | 3310 | int lockflags; |
2d21ac55 | 3311 | int error, started_tr = 0; |
9bccf70c | 3312 | enum vtype vnodetype; |
91447636 | 3313 | int mode; |
9bccf70c | 3314 | |
9bccf70c | 3315 | dcp = VTOC(dvp); |
2d21ac55 A |
3316 | if ((error = hfs_lock(dcp, HFS_EXCLUSIVE_LOCK))) |
3317 | return (error); | |
3318 | ||
3319 | dcp->c_flag |= C_DIR_MODIFICATION; | |
3320 | ||
9bccf70c A |
3321 | hfsmp = VTOHFS(dvp); |
3322 | *vpp = NULL; | |
3323 | tvp = NULL; | |
91447636 A |
3324 | out_desc.cd_flags = 0; |
3325 | out_desc.cd_nameptr = NULL; | |
3326 | ||
2d21ac55 A |
3327 | vnodetype = vap->va_type; |
3328 | if (vnodetype == VNON) | |
3329 | vnodetype = VREG; | |
3330 | mode = MAKEIMODE(vnodetype, vap->va_mode); | |
9bccf70c | 3331 | |
9bccf70c | 3332 | /* Check if were out of usable disk space. */ |
2d21ac55 | 3333 | if ((hfs_freeblks(hfsmp, 1) == 0) && (vfs_context_suser(ctx) != 0)) { |
9bccf70c A |
3334 | error = ENOSPC; |
3335 | goto exit; | |
3336 | } | |
3337 | ||
91447636 A |
3338 | microtime(&tv); |
3339 | ||
9bccf70c A |
3340 | /* Setup the default attributes */ |
3341 | bzero(&attr, sizeof(attr)); | |
3342 | attr.ca_mode = mode; | |
2d21ac55 A |
3343 | attr.ca_linkcount = 1; |
3344 | if (VATTR_IS_ACTIVE(vap, va_rdev)) { | |
3345 | attr.ca_rdev = vap->va_rdev; | |
3346 | } | |
3347 | if (VATTR_IS_ACTIVE(vap, va_create_time)) { | |
3348 | VATTR_SET_SUPPORTED(vap, va_create_time); | |
3349 | attr.ca_itime = vap->va_create_time.tv_sec; | |
3350 | } else { | |
3351 | attr.ca_itime = tv.tv_sec; | |
3352 | } | |
3353 | if ((hfsmp->hfs_flags & HFS_STANDARD) && gTimeZone.tz_dsttime) { | |
3354 | attr.ca_itime += 3600; /* Same as what hfs_update does */ | |
9bccf70c | 3355 | } |
2d21ac55 | 3356 | attr.ca_atime = attr.ca_ctime = attr.ca_mtime = attr.ca_itime; |
91447636 | 3357 | attr.ca_atimeondisk = attr.ca_atime; |
2d21ac55 A |
3358 | if (VATTR_IS_ACTIVE(vap, va_flags)) { |
3359 | VATTR_SET_SUPPORTED(vap, va_flags); | |
3360 | attr.ca_flags = vap->va_flags; | |
3361 | } | |
3362 | ||
3363 | /* | |
3364 | * HFS+ only: all files get ThreadExists | |
3365 | * HFSX only: dirs get HasFolderCount | |
3366 | */ | |
3367 | if (!(hfsmp->hfs_flags & HFS_STANDARD)) { | |
3368 | if (vnodetype == VDIR) { | |
3369 | if (hfsmp->hfs_flags & HFS_FOLDERCOUNT) | |
3370 | attr.ca_recflags = kHFSHasFolderCountMask; | |
3371 | } else { | |
3372 | attr.ca_recflags = kHFSThreadExistsMask; | |
3373 | } | |
3374 | } | |
91447636 A |
3375 | |
3376 | attr.ca_uid = vap->va_uid; | |
3377 | attr.ca_gid = vap->va_gid; | |
3378 | VATTR_SET_SUPPORTED(vap, va_mode); | |
3379 | VATTR_SET_SUPPORTED(vap, va_uid); | |
3380 | VATTR_SET_SUPPORTED(vap, va_gid); | |
9bccf70c A |
3381 | |
3382 | /* Tag symlinks with a type and creator. */ | |
3383 | if (vnodetype == VLNK) { | |
3384 | struct FndrFileInfo *fip; | |
3385 | ||
3386 | fip = (struct FndrFileInfo *)&attr.ca_finderinfo; | |
3387 | fip->fdType = SWAP_BE32(kSymLinkFileType); | |
3388 | fip->fdCreator = SWAP_BE32(kSymLinkCreator); | |
3389 | } | |
9bccf70c A |
3390 | if (cnp->cn_flags & ISWHITEOUT) |
3391 | attr.ca_flags |= UF_OPAQUE; | |
3392 | ||
3393 | /* Setup the descriptor */ | |
2d21ac55 | 3394 | in_desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr; |
9bccf70c | 3395 | in_desc.cd_namelen = cnp->cn_namelen; |
2d21ac55 | 3396 | in_desc.cd_parentcnid = dcp->c_fileid; |
9bccf70c | 3397 | in_desc.cd_flags = S_ISDIR(mode) ? CD_ISDIR : 0; |
91447636 A |
3398 | in_desc.cd_hint = dcp->c_childhint; |
3399 | in_desc.cd_encoding = 0; | |
9bccf70c | 3400 | |
91447636 A |
3401 | if ((error = hfs_start_transaction(hfsmp)) != 0) { |
3402 | goto exit; | |
b4c24cb9 | 3403 | } |
91447636 | 3404 | started_tr = 1; |
b4c24cb9 | 3405 | |
2d21ac55 A |
3406 | // have to also lock the attribute file because cat_create() needs |
3407 | // to check that any fileID it wants to use does not have orphaned | |
3408 | // attributes in it. | |
3409 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK); | |
3410 | ||
3411 | /* Reserve some space in the Catalog file. */ | |
3412 | if ((error = cat_preflight(hfsmp, CAT_CREATE, NULL, 0))) { | |
3413 | hfs_systemfile_unlock(hfsmp, lockflags); | |
55e303ae A |
3414 | goto exit; |
3415 | } | |
9bccf70c | 3416 | error = cat_create(hfsmp, &in_desc, &attr, &out_desc); |
91447636 A |
3417 | if (error == 0) { |
3418 | /* Update the parent directory */ | |
3419 | dcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */ | |
91447636 | 3420 | dcp->c_entries++; |
2d21ac55 A |
3421 | if (vnodetype == VDIR) { |
3422 | INC_FOLDERCOUNT(hfsmp, dcp->c_attr); | |
3423 | } | |
3424 | dcp->c_dirchangecnt++; | |
91447636 A |
3425 | dcp->c_ctime = tv.tv_sec; |
3426 | dcp->c_mtime = tv.tv_sec; | |
3427 | (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL); | |
3428 | HFS_KNOTE(dvp, NOTE_ATTRIB); | |
3429 | } | |
3430 | hfs_systemfile_unlock(hfsmp, lockflags); | |
9bccf70c A |
3431 | if (error) |
3432 | goto exit; | |
3433 | ||
91447636 | 3434 | /* Invalidate negative cache entries in the directory */ |
2d21ac55 | 3435 | if (dcp->c_flag & C_NEG_ENTRIES) { |
91447636 | 3436 | cache_purge_negatives(dvp); |
2d21ac55 A |
3437 | dcp->c_flag &= ~C_NEG_ENTRIES; |
3438 | } | |
91447636 | 3439 | |
55e303ae A |
3440 | if (vnodetype == VDIR) { |
3441 | HFS_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); | |
3442 | } else { | |
3443 | HFS_KNOTE(dvp, NOTE_WRITE); | |
3444 | }; | |
b4c24cb9 | 3445 | |
9bccf70c A |
3446 | hfs_volupdate(hfsmp, vnodetype == VDIR ? VOL_MKDIR : VOL_MKFILE, |
3447 | (dcp->c_cnid == kHFSRootFolderID)); | |
3448 | ||
b4c24cb9 A |
3449 | // XXXdbg |
3450 | // have to end the transaction here before we call hfs_getnewvnode() | |
3451 | // because that can cause us to try and reclaim a vnode on a different | |
3452 | // file system which could cause us to start a transaction which can | |
3453 | // deadlock with someone on that other file system (since we could be | |
3454 | // holding two transaction locks as well as various vnodes and we did | |
3455 | // not obtain the locks on them in the proper order). | |
91447636 | 3456 | // |
b4c24cb9 A |
3457 | // NOTE: this means that if the quota check fails or we have to update |
3458 | // the change time on a block-special device that those changes | |
3459 | // will happen as part of independent transactions. | |
3460 | // | |
3461 | if (started_tr) { | |
91447636 A |
3462 | hfs_end_transaction(hfsmp); |
3463 | started_tr = 0; | |
b4c24cb9 A |
3464 | } |
3465 | ||
2d21ac55 A |
3466 | /* Do not create vnode for whiteouts */ |
3467 | if (S_ISWHT(mode)) { | |
3468 | goto exit; | |
3469 | } | |
3470 | ||
91447636 A |
3471 | /* |
3472 | * Create a vnode for the object just created. | |
3473 | * | |
3474 | * The cnode is locked on successful return. | |
3475 | */ | |
2d21ac55 | 3476 | error = hfs_getnewvnode(hfsmp, dvp, cnp, &out_desc, GNV_CREATE, &attr, NULL, &tvp); |
9bccf70c A |
3477 | if (error) |
3478 | goto exit; | |
3479 | ||
9bccf70c | 3480 | cp = VTOC(tvp); |
91447636 | 3481 | #if QUOTA |
9bccf70c A |
3482 | /* |
3483 | * We call hfs_chkiq with FORCE flag so that if we | |
3484 | * fall through to the rmdir we actually have | |
3485 | * accounted for the inode | |
3486 | */ | |
2d21ac55 | 3487 | if (hfsmp->hfs_flags & HFS_QUOTAS) { |
91447636 A |
3488 | if ((error = hfs_getinoquota(cp)) || |
3489 | (error = hfs_chkiq(cp, 1, vfs_context_ucred(ctx), FORCE))) { | |
3490 | ||
3491 | if (vnode_isdir(tvp)) | |
3492 | (void) hfs_removedir(dvp, tvp, cnp, 0); | |
3493 | else { | |
3494 | hfs_unlock(cp); | |
3495 | hfs_lock_truncate(cp, TRUE); | |
3496 | hfs_lock(cp, HFS_FORCE_LOCK); | |
2d21ac55 A |
3497 | (void) hfs_removefile(dvp, tvp, cnp, 0, 0, 0); |
3498 | hfs_unlock_truncate(cp, TRUE); | |
91447636 A |
3499 | } |
3500 | /* | |
3501 | * we successfully allocated a new vnode, but | |
3502 | * the quota check is telling us we're beyond | |
3503 | * our limit, so we need to dump our lock + reference | |
3504 | */ | |
3505 | hfs_unlock(cp); | |
3506 | vnode_put(tvp); | |
3507 | ||
9bccf70c A |
3508 | goto exit; |
3509 | } | |
3510 | } | |
91447636 | 3511 | #endif /* QUOTA */ |
9bccf70c A |
3512 | |
3513 | *vpp = tvp; | |
3514 | exit: | |
3515 | cat_releasedesc(&out_desc); | |
3516 | ||
90556fb8 A |
3517 | /* |
3518 | * Check if a file is located in the "Cleanup At Startup" | |
3519 | * directory. If it is then tag it as NODUMP so that we | |
3520 | * can be lazy about zero filling data holes. | |
3521 | */ | |
55e303ae | 3522 | if ((error == 0) && dvp && (vnodetype == VREG) && |
90556fb8 | 3523 | (dcp->c_desc.cd_nameptr != NULL) && |
2d21ac55 A |
3524 | (strncmp((const char *)dcp->c_desc.cd_nameptr, |
3525 | CARBON_TEMP_DIR_NAME, | |
3526 | sizeof(CARBON_TEMP_DIR_NAME)) == 0)) { | |
90556fb8 | 3527 | struct vnode *ddvp; |
90556fb8 | 3528 | |
2d21ac55 A |
3529 | dcp->c_flag &= ~C_DIR_MODIFICATION; |
3530 | wakeup((caddr_t)&dcp->c_flag); | |
3531 | ||
91447636 | 3532 | hfs_unlock(dcp); |
90556fb8 A |
3533 | dvp = NULL; |
3534 | ||
3535 | /* | |
3536 | * The parent of "Cleanup At Startup" should | |
3537 | * have the ASCII name of the userid. | |
3538 | */ | |
91447636 A |
3539 | if (hfs_vget(hfsmp, dcp->c_parentcnid, &ddvp, 0) == 0) { |
3540 | if (VTOC(ddvp)->c_desc.cd_nameptr) { | |
3541 | uid_t uid; | |
3542 | ||
2d21ac55 | 3543 | uid = strtoul((const char *)VTOC(ddvp)->c_desc.cd_nameptr, 0, 0); |
91447636 A |
3544 | if ((uid == cp->c_uid) || |
3545 | (uid == vfs_context_ucred(ctx)->cr_uid)) { | |
3546 | cp->c_flags |= UF_NODUMP; | |
3547 | cp->c_touch_chgtime = TRUE; | |
3548 | } | |
90556fb8 | 3549 | } |
91447636 A |
3550 | hfs_unlock(VTOC(ddvp)); |
3551 | vnode_put(ddvp); | |
90556fb8 A |
3552 | } |
3553 | } | |
91447636 | 3554 | if (dvp) { |
2d21ac55 A |
3555 | dcp->c_flag &= ~C_DIR_MODIFICATION; |
3556 | wakeup((caddr_t)&dcp->c_flag); | |
3557 | ||
91447636 A |
3558 | hfs_unlock(dcp); |
3559 | } | |
3560 | if (error == 0 && cp != NULL) { | |
3561 | hfs_unlock(cp); | |
b4c24cb9 | 3562 | } |
91447636 A |
3563 | if (started_tr) { |
3564 | hfs_end_transaction(hfsmp); | |
3565 | started_tr = 0; | |
b4c24cb9 A |
3566 | } |
3567 | ||
9bccf70c A |
3568 | return (error); |
3569 | } | |
3570 | ||
3571 | ||
91447636 | 3572 | /* |
2d21ac55 A |
3573 | * Return a referenced vnode for the resource fork |
3574 | * | |
3575 | * cnode for vnode vp must already be locked. | |
3576 | * | |
3577 | * can_drop_lock is true if its safe to temporally drop/re-acquire the cnode lock | |
91447636 A |
3578 | */ |
3579 | __private_extern__ | |
3580 | int | |
2d21ac55 | 3581 | hfs_vgetrsrc(struct hfsmount *hfsmp, struct vnode *vp, struct vnode **rvpp, int can_drop_lock) |
9bccf70c A |
3582 | { |
3583 | struct vnode *rvp; | |
3584 | struct cnode *cp = VTOC(vp); | |
3585 | int error; | |
91447636 | 3586 | int vid; |
9bccf70c | 3587 | |
2d21ac55 | 3588 | /* Attempt to use exising vnode */ |
9bccf70c | 3589 | if ((rvp = cp->c_rsrc_vp)) { |
91447636 A |
3590 | vid = vnode_vid(rvp); |
3591 | ||
2d21ac55 A |
3592 | /* |
3593 | * It is not safe to hold the cnode lock when calling vnode_getwithvid() | |
3594 | * for the alternate fork -- vnode_getwithvid() could deadlock waiting | |
3595 | * for a VL_WANTTERM while another thread has an iocount on the alternate | |
3596 | * fork vnode and is attempting to acquire the common cnode lock. | |
3597 | * | |
3598 | * But it's also not safe to drop the cnode lock when we're holding | |
3599 | * multiple cnode locks, like during a hfs_removefile() operation | |
3600 | * since we could lock out of order when re-acquiring the cnode lock. | |
3601 | * | |
3602 | * So we can only drop the lock here if its safe to drop it -- which is | |
3603 | * most of the time with the exception being hfs_removefile(). | |
3604 | */ | |
3605 | if (can_drop_lock) | |
3606 | hfs_unlock(cp); | |
3607 | ||
91447636 | 3608 | error = vnode_getwithvid(rvp, vid); |
2d21ac55 A |
3609 | |
3610 | if (can_drop_lock) | |
3611 | (void) hfs_lock(cp, HFS_FORCE_LOCK); | |
3612 | ||
9bccf70c | 3613 | if (error) { |
2d21ac55 | 3614 | const char * name = (const char *)VTOC(vp)->c_desc.cd_nameptr; |
9bccf70c A |
3615 | |
3616 | if (name) | |
3617 | printf("hfs_vgetrsrc: couldn't get" | |
3618 | " resource fork for %s\n", name); | |
3619 | return (error); | |
3620 | } | |
3621 | } else { | |
3622 | struct cat_fork rsrcfork; | |
91447636 A |
3623 | struct componentname cn; |
3624 | int lockflags; | |
9bccf70c | 3625 | |
743b1565 A |
3626 | /* |
3627 | * Make sure cnode lock is exclusive, if not upgrade it. | |
3628 | * | |
3629 | * We assume that we were called from a read-only VNOP (getattr) | |
3630 | * and that its safe to have the cnode lock dropped and reacquired. | |
3631 | */ | |
3632 | if (cp->c_lockowner != current_thread()) { | |
2d21ac55 A |
3633 | if (!can_drop_lock) |
3634 | return (EINVAL); | |
743b1565 A |
3635 | /* |
3636 | * If the upgrade fails we loose the lock and | |
3637 | * have to take the exclusive lock on our own. | |
3638 | */ | |
2d21ac55 | 3639 | if (lck_rw_lock_shared_to_exclusive(&cp->c_rwlock) == FALSE) |
743b1565 A |
3640 | lck_rw_lock_exclusive(&cp->c_rwlock); |
3641 | cp->c_lockowner = current_thread(); | |
3642 | } | |
3643 | ||
91447636 | 3644 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); |
9bccf70c A |
3645 | |
3646 | /* Get resource fork data */ | |
3647 | error = cat_lookup(hfsmp, &cp->c_desc, 1, (struct cat_desc *)0, | |
91447636 | 3648 | (struct cat_attr *)0, &rsrcfork, NULL); |
9bccf70c | 3649 | |
91447636 | 3650 | hfs_systemfile_unlock(hfsmp, lockflags); |
9bccf70c A |
3651 | if (error) |
3652 | return (error); | |
3653 | ||
91447636 A |
3654 | /* |
3655 | * Supply hfs_getnewvnode with a component name. | |
3656 | */ | |
3657 | cn.cn_pnbuf = NULL; | |
3658 | if (cp->c_desc.cd_nameptr) { | |
3659 | MALLOC_ZONE(cn.cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); | |
3660 | cn.cn_nameiop = LOOKUP; | |
3661 | cn.cn_flags = ISLASTCN | HASBUF; | |
3662 | cn.cn_context = NULL; | |
3663 | cn.cn_pnlen = MAXPATHLEN; | |
3664 | cn.cn_nameptr = cn.cn_pnbuf; | |
3665 | cn.cn_hash = 0; | |
3666 | cn.cn_consume = 0; | |
2d21ac55 A |
3667 | cn.cn_namelen = snprintf(cn.cn_nameptr, MAXPATHLEN, |
3668 | "%s%s", cp->c_desc.cd_nameptr, | |
3669 | _PATH_RSRCFORKSPEC); | |
91447636 A |
3670 | } |
3671 | error = hfs_getnewvnode(hfsmp, vnode_parent(vp), cn.cn_pnbuf ? &cn : NULL, | |
2d21ac55 A |
3672 | &cp->c_desc, GNV_WANTRSRC | GNV_SKIPLOCK, &cp->c_attr, |
3673 | &rsrcfork, &rvp); | |
91447636 A |
3674 | if (cn.cn_pnbuf) |
3675 | FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI); | |
9bccf70c A |
3676 | if (error) |
3677 | return (error); | |
3678 | } | |
3679 | ||
3680 | *rvpp = rvp; | |
3681 | return (0); | |
3682 | } | |
3683 | ||
3684 | ||
55e303ae A |
3685 | static void |
3686 | filt_hfsdetach(struct knote *kn) | |
3687 | { | |
3688 | struct vnode *vp; | |
55e303ae A |
3689 | |
3690 | vp = (struct vnode *)kn->kn_hook; | |
91447636 A |
3691 | if (vnode_getwithvid(vp, kn->kn_hookid)) |
3692 | return; | |
55e303ae | 3693 | |
91447636 A |
3694 | if (1) { /* ! KNDETACH_VNLOCKED */ |
3695 | if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) { | |
3696 | (void) KNOTE_DETACH(&VTOC(vp)->c_knotes, kn); | |
3697 | hfs_unlock(VTOC(vp)); | |
3698 | } | |
3699 | } | |
3700 | ||
3701 | vnode_put(vp); | |
55e303ae A |
3702 | } |
3703 | ||
3704 | /*ARGSUSED*/ | |
3705 | static int | |
3706 | filt_hfsread(struct knote *kn, long hint) | |
3707 | { | |
91447636 A |
3708 | struct vnode *vp = (struct vnode *)kn->kn_hook; |
3709 | int dropvp = 0; | |
55e303ae | 3710 | |
91447636 A |
3711 | if (hint == 0) { |
3712 | if ((vnode_getwithvid(vp, kn->kn_hookid) != 0)) { | |
3713 | hint = NOTE_REVOKE; | |
3714 | } else | |
3715 | dropvp = 1; | |
3716 | } | |
55e303ae A |
3717 | if (hint == NOTE_REVOKE) { |
3718 | /* | |
3719 | * filesystem is gone, so set the EOF flag and schedule | |
3720 | * the knote for deletion. | |
3721 | */ | |
3722 | kn->kn_flags |= (EV_EOF | EV_ONESHOT); | |
3723 | return (1); | |
3724 | } | |
3725 | ||
91447636 | 3726 | /* poll(2) semantics dictate always saying there is data */ |
2d21ac55 A |
3727 | if (!(kn->kn_flags & EV_POLL)) { |
3728 | off_t amount; | |
3729 | ||
3730 | amount = VTOF(vp)->ff_size - kn->kn_fp->f_fglob->fg_offset; | |
3731 | if (amount > (off_t)INTPTR_MAX) | |
3732 | kn->kn_data = INTPTR_MAX; | |
3733 | else if (amount < (off_t)INTPTR_MIN) | |
3734 | kn->kn_data = INTPTR_MIN; | |
3735 | else | |
3736 | kn->kn_data = (intptr_t)amount; | |
3737 | } else { | |
3738 | kn->kn_data = 1; | |
3739 | } | |
91447636 A |
3740 | |
3741 | if (dropvp) | |
3742 | vnode_put(vp); | |
3743 | ||
55e303ae A |
3744 | return (kn->kn_data != 0); |
3745 | } | |
3746 | ||
3747 | /*ARGSUSED*/ | |
3748 | static int | |
3749 | filt_hfswrite(struct knote *kn, long hint) | |
3750 | { | |
2d21ac55 A |
3751 | struct vnode *vp = (struct vnode *)kn->kn_hook; |
3752 | ||
91447636 | 3753 | if (hint == 0) { |
2d21ac55 | 3754 | if ((vnode_getwithvid(vp, kn->kn_hookid) != 0)) { |
91447636 A |
3755 | hint = NOTE_REVOKE; |
3756 | } else | |
2d21ac55 | 3757 | vnode_put(vp); |
91447636 | 3758 | } |
55e303ae A |
3759 | if (hint == NOTE_REVOKE) { |
3760 | /* | |
3761 | * filesystem is gone, so set the EOF flag and schedule | |
3762 | * the knote for deletion. | |
3763 | */ | |
91447636 | 3764 | kn->kn_data = 0; |
55e303ae | 3765 | kn->kn_flags |= (EV_EOF | EV_ONESHOT); |
91447636 | 3766 | return (1); |
55e303ae | 3767 | } |
55e303ae A |
3768 | kn->kn_data = 0; |
3769 | return (1); | |
3770 | } | |
3771 | ||
3772 | static int | |
3773 | filt_hfsvnode(struct knote *kn, long hint) | |
3774 | { | |
2d21ac55 | 3775 | struct vnode *vp = (struct vnode *)kn->kn_hook; |
55e303ae | 3776 | |
91447636 | 3777 | if (hint == 0) { |
2d21ac55 | 3778 | if ((vnode_getwithvid(vp, kn->kn_hookid) != 0)) { |
91447636 A |
3779 | hint = NOTE_REVOKE; |
3780 | } else | |
2d21ac55 | 3781 | vnode_put(vp); |
91447636 | 3782 | } |
55e303ae A |
3783 | if (kn->kn_sfflags & hint) |
3784 | kn->kn_fflags |= hint; | |
91447636 A |
3785 | if ((hint == NOTE_REVOKE)) { |
3786 | kn->kn_flags |= (EV_EOF | EV_ONESHOT); | |
55e303ae A |
3787 | return (1); |
3788 | } | |
91447636 | 3789 | |
55e303ae A |
3790 | return (kn->kn_fflags != 0); |
3791 | } | |
3792 | ||
3793 | static struct filterops hfsread_filtops = | |
3794 | { 1, NULL, filt_hfsdetach, filt_hfsread }; | |
3795 | static struct filterops hfswrite_filtops = | |
3796 | { 1, NULL, filt_hfsdetach, filt_hfswrite }; | |
3797 | static struct filterops hfsvnode_filtops = | |
3798 | { 1, NULL, filt_hfsdetach, filt_hfsvnode }; | |
3799 | ||
3800 | /* | |
91447636 | 3801 | * Add a kqueue filter. |
55e303ae A |
3802 | */ |
3803 | static int | |
91447636 A |
3804 | hfs_vnop_kqfiltadd( |
3805 | struct vnop_kqfilt_add_args /* { | |
55e303ae A |
3806 | struct vnode *a_vp; |
3807 | struct knote *a_kn; | |
3808 | struct proc *p; | |
91447636 A |
3809 | vfs_context_t a_context; |
3810 | } */ *ap) | |
55e303ae A |
3811 | { |
3812 | struct vnode *vp = ap->a_vp; | |
3813 | struct knote *kn = ap->a_kn; | |
91447636 | 3814 | int error; |
55e303ae A |
3815 | |
3816 | switch (kn->kn_filter) { | |
3817 | case EVFILT_READ: | |
91447636 | 3818 | if (vnode_isreg(vp)) { |
55e303ae A |
3819 | kn->kn_fop = &hfsread_filtops; |
3820 | } else { | |
3821 | return EINVAL; | |
3822 | }; | |
3823 | break; | |
3824 | case EVFILT_WRITE: | |
91447636 | 3825 | if (vnode_isreg(vp)) { |
55e303ae A |
3826 | kn->kn_fop = &hfswrite_filtops; |
3827 | } else { | |
3828 | return EINVAL; | |
3829 | }; | |
3830 | break; | |
3831 | case EVFILT_VNODE: | |
3832 | kn->kn_fop = &hfsvnode_filtops; | |
3833 | break; | |
3834 | default: | |
3835 | return (1); | |
3836 | } | |
3837 | ||
3838 | kn->kn_hook = (caddr_t)vp; | |
91447636 | 3839 | kn->kn_hookid = vnode_vid(vp); |
55e303ae | 3840 | |
91447636 A |
3841 | if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) |
3842 | return (error); | |
55e303ae | 3843 | KNOTE_ATTACH(&VTOC(vp)->c_knotes, kn); |
91447636 | 3844 | hfs_unlock(VTOC(vp)); |
55e303ae A |
3845 | |
3846 | return (0); | |
3847 | } | |
3848 | ||
3849 | /* | |
91447636 | 3850 | * Remove a kqueue filter |
55e303ae A |
3851 | */ |
3852 | static int | |
91447636 A |
3853 | hfs_vnop_kqfiltremove(ap) |
3854 | struct vnop_kqfilt_remove_args /* { | |
55e303ae A |
3855 | struct vnode *a_vp; |
3856 | uintptr_t ident; | |
91447636 | 3857 | vfs_context_t a_context; |
2d21ac55 | 3858 | } */__unused *ap; |
55e303ae | 3859 | { |
55e303ae A |
3860 | int result; |
3861 | ||
3862 | result = ENOTSUP; /* XXX */ | |
3863 | ||
3864 | return (result); | |
3865 | } | |
3866 | ||
9bccf70c A |
3867 | /* |
3868 | * Wrapper for special device reads | |
3869 | */ | |
3870 | static int | |
3871 | hfsspec_read(ap) | |
91447636 | 3872 | struct vnop_read_args /* { |
9bccf70c A |
3873 | struct vnode *a_vp; |
3874 | struct uio *a_uio; | |
3875 | int a_ioflag; | |
91447636 | 3876 | vfs_context_t a_context; |
9bccf70c A |
3877 | } */ *ap; |
3878 | { | |
3879 | /* | |
3880 | * Set access flag. | |
3881 | */ | |
91447636 A |
3882 | VTOC(ap->a_vp)->c_touch_acctime = TRUE; |
3883 | return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_read), ap)); | |
9bccf70c A |
3884 | } |
3885 | ||
3886 | /* | |
3887 | * Wrapper for special device writes | |
3888 | */ | |
3889 | static int | |
3890 | hfsspec_write(ap) | |
91447636 | 3891 | struct vnop_write_args /* { |
9bccf70c A |
3892 | struct vnode *a_vp; |
3893 | struct uio *a_uio; | |
3894 | int a_ioflag; | |
91447636 | 3895 | vfs_context_t a_context; |
9bccf70c A |
3896 | } */ *ap; |
3897 | { | |
3898 | /* | |
3899 | * Set update and change flags. | |
3900 | */ | |
91447636 A |
3901 | VTOC(ap->a_vp)->c_touch_chgtime = TRUE; |
3902 | VTOC(ap->a_vp)->c_touch_modtime = TRUE; | |
3903 | return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_write), ap)); | |
9bccf70c A |
3904 | } |
3905 | ||
3906 | /* | |
3907 | * Wrapper for special device close | |
3908 | * | |
3909 | * Update the times on the cnode then do device close. | |
3910 | */ | |
3911 | static int | |
3912 | hfsspec_close(ap) | |
91447636 | 3913 | struct vnop_close_args /* { |
9bccf70c A |
3914 | struct vnode *a_vp; |
3915 | int a_fflag; | |
91447636 | 3916 | vfs_context_t a_context; |
9bccf70c A |
3917 | } */ *ap; |
3918 | { | |
3919 | struct vnode *vp = ap->a_vp; | |
91447636 | 3920 | struct cnode *cp; |
9bccf70c | 3921 | |
91447636 A |
3922 | if (vnode_isinuse(ap->a_vp, 1)) { |
3923 | if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) { | |
3924 | cp = VTOC(vp); | |
3925 | hfs_touchtimes(VTOHFS(vp), cp); | |
3926 | hfs_unlock(cp); | |
3927 | } | |
3928 | } | |
3929 | return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_close), ap)); | |
9bccf70c A |
3930 | } |
3931 | ||
3932 | #if FIFO | |
3933 | /* | |
3934 | * Wrapper for fifo reads | |
3935 | */ | |
3936 | static int | |
3937 | hfsfifo_read(ap) | |
91447636 | 3938 | struct vnop_read_args /* { |
9bccf70c A |
3939 | struct vnode *a_vp; |
3940 | struct uio *a_uio; | |
3941 | int a_ioflag; | |
91447636 | 3942 | vfs_context_t a_context; |
9bccf70c A |
3943 | } */ *ap; |
3944 | { | |
9bccf70c A |
3945 | /* |
3946 | * Set access flag. | |
3947 | */ | |
91447636 A |
3948 | VTOC(ap->a_vp)->c_touch_acctime = TRUE; |
3949 | return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_read), ap)); | |
9bccf70c A |
3950 | } |
3951 | ||
3952 | /* | |
3953 | * Wrapper for fifo writes | |
3954 | */ | |
3955 | static int | |
3956 | hfsfifo_write(ap) | |
91447636 | 3957 | struct vnop_write_args /* { |
9bccf70c A |
3958 | struct vnode *a_vp; |
3959 | struct uio *a_uio; | |
3960 | int a_ioflag; | |
91447636 | 3961 | vfs_context_t a_context; |
9bccf70c A |
3962 | } */ *ap; |
3963 | { | |
9bccf70c A |
3964 | /* |
3965 | * Set update and change flags. | |
3966 | */ | |
91447636 A |
3967 | VTOC(ap->a_vp)->c_touch_chgtime = TRUE; |
3968 | VTOC(ap->a_vp)->c_touch_modtime = TRUE; | |
3969 | return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_write), ap)); | |
9bccf70c A |
3970 | } |
3971 | ||
3972 | /* | |
3973 | * Wrapper for fifo close | |
3974 | * | |
3975 | * Update the times on the cnode then do device close. | |
3976 | */ | |
3977 | static int | |
3978 | hfsfifo_close(ap) | |
91447636 | 3979 | struct vnop_close_args /* { |
9bccf70c A |
3980 | struct vnode *a_vp; |
3981 | int a_fflag; | |
91447636 | 3982 | vfs_context_t a_context; |
9bccf70c A |
3983 | } */ *ap; |
3984 | { | |
9bccf70c | 3985 | struct vnode *vp = ap->a_vp; |
91447636 | 3986 | struct cnode *cp; |
9bccf70c | 3987 | |
91447636 A |
3988 | if (vnode_isinuse(ap->a_vp, 1)) { |
3989 | if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) { | |
3990 | cp = VTOC(vp); | |
3991 | hfs_touchtimes(VTOHFS(vp), cp); | |
3992 | hfs_unlock(cp); | |
3993 | } | |
3994 | } | |
3995 | return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_close), ap)); | |
9bccf70c | 3996 | } |
55e303ae A |
3997 | |
3998 | /* | |
3999 | * kqfilt_add wrapper for fifos. | |
4000 | * | |
4001 | * Fall through to hfs kqfilt_add routines if needed | |
4002 | */ | |
4003 | int | |
4004 | hfsfifo_kqfilt_add(ap) | |
91447636 | 4005 | struct vnop_kqfilt_add_args *ap; |
55e303ae | 4006 | { |
55e303ae A |
4007 | int error; |
4008 | ||
91447636 | 4009 | error = VOCALL(fifo_vnodeop_p, VOFFSET(vnop_kqfilt_add), ap); |
55e303ae | 4010 | if (error) |
91447636 | 4011 | error = hfs_vnop_kqfiltadd(ap); |
55e303ae A |
4012 | return (error); |
4013 | } | |
4014 | ||
4015 | /* | |
4016 | * kqfilt_remove wrapper for fifos. | |
4017 | * | |
4018 | * Fall through to hfs kqfilt_remove routines if needed | |
4019 | */ | |
4020 | int | |
4021 | hfsfifo_kqfilt_remove(ap) | |
91447636 | 4022 | struct vnop_kqfilt_remove_args *ap; |
55e303ae | 4023 | { |
55e303ae A |
4024 | int error; |
4025 | ||
91447636 | 4026 | error = VOCALL(fifo_vnodeop_p, VOFFSET(vnop_kqfilt_remove), ap); |
55e303ae | 4027 | if (error) |
91447636 | 4028 | error = hfs_vnop_kqfiltremove(ap); |
55e303ae A |
4029 | return (error); |
4030 | } | |
4031 | ||
9bccf70c A |
4032 | #endif /* FIFO */ |
4033 | ||
91447636 A |
4034 | /* |
4035 | * Synchronize a file's in-core state with that on disk. | |
4036 | */ | |
4037 | static int | |
4038 | hfs_vnop_fsync(ap) | |
4039 | struct vnop_fsync_args /* { | |
4040 | struct vnode *a_vp; | |
4041 | int a_waitfor; | |
4042 | vfs_context_t a_context; | |
4043 | } */ *ap; | |
4044 | { | |
4045 | struct vnode* vp = ap->a_vp; | |
4046 | int error; | |
4047 | ||
4048 | /* | |
4049 | * We need to allow ENOENT lock errors since unlink | |
4050 | * systenm call can call VNOP_FSYNC during vclean. | |
4051 | */ | |
4052 | error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK); | |
4053 | if (error) | |
4054 | return (0); | |
4055 | ||
4056 | error = hfs_fsync(vp, ap->a_waitfor, 0, vfs_context_proc(ap->a_context)); | |
4057 | ||
4058 | hfs_unlock(VTOC(vp)); | |
4059 | return (error); | |
4060 | } | |
9bccf70c | 4061 | |
2d21ac55 A |
4062 | |
4063 | static int | |
4064 | hfs_vnop_whiteout(ap) | |
4065 | struct vnop_whiteout_args /* { | |
4066 | struct vnode *a_dvp; | |
4067 | struct componentname *a_cnp; | |
4068 | int a_flags; | |
4069 | vfs_context_t a_context; | |
4070 | } */ *ap; | |
4071 | { | |
4072 | int error = 0; | |
4073 | struct vnode *vp = NULL; | |
4074 | struct vnode_attr va; | |
4075 | struct vnop_lookup_args lookup_args; | |
4076 | struct vnop_remove_args remove_args; | |
4077 | struct hfsmount *hfsmp; | |
4078 | ||
4079 | hfsmp = VTOHFS(ap->a_dvp); | |
4080 | if (hfsmp->hfs_flags & HFS_STANDARD) { | |
4081 | error = ENOTSUP; | |
4082 | goto exit; | |
4083 | } | |
4084 | ||
4085 | switch (ap->a_flags) { | |
4086 | case LOOKUP: | |
4087 | error = 0; | |
4088 | break; | |
4089 | ||
4090 | case CREATE: | |
4091 | VATTR_INIT(&va); | |
4092 | VATTR_SET(&va, va_type, VREG); | |
4093 | VATTR_SET(&va, va_mode, S_IFWHT); | |
4094 | VATTR_SET(&va, va_uid, 0); | |
4095 | VATTR_SET(&va, va_gid, 0); | |
4096 | ||
4097 | error = hfs_makenode(ap->a_dvp, &vp, ap->a_cnp, &va, ap->a_context); | |
4098 | /* No need to release the vnode as no vnode is created for whiteouts */ | |
4099 | break; | |
4100 | ||
4101 | case DELETE: | |
4102 | lookup_args.a_dvp = ap->a_dvp; | |
4103 | lookup_args.a_vpp = &vp; | |
4104 | lookup_args.a_cnp = ap->a_cnp; | |
4105 | lookup_args.a_context = ap->a_context; | |
4106 | ||
4107 | error = hfs_vnop_lookup(&lookup_args); | |
4108 | if (error) { | |
4109 | break; | |
4110 | } | |
4111 | ||
4112 | remove_args.a_dvp = ap->a_dvp; | |
4113 | remove_args.a_vp = vp; | |
4114 | remove_args.a_cnp = ap->a_cnp; | |
4115 | remove_args.a_flags = 0; | |
4116 | remove_args.a_context = ap->a_context; | |
4117 | ||
4118 | error = hfs_vnop_remove(&remove_args); | |
4119 | vnode_put(vp); | |
4120 | break; | |
4121 | ||
4122 | default: | |
4123 | panic("hfs_vnop_whiteout: unknown operation (flag = %x)\n", ap->a_flags); | |
4124 | }; | |
4125 | ||
4126 | exit: | |
4127 | return (error); | |
4128 | } | |
9bccf70c A |
4129 | |
4130 | int (**hfs_vnodeop_p)(void *); | |
4131 | ||
4132 | #define VOPFUNC int (*)(void *) | |
4133 | ||
4134 | struct vnodeopv_entry_desc hfs_vnodeop_entries[] = { | |
91447636 A |
4135 | { &vnop_default_desc, (VOPFUNC)vn_default_error }, |
4136 | { &vnop_lookup_desc, (VOPFUNC)hfs_vnop_lookup }, /* lookup */ | |
4137 | { &vnop_create_desc, (VOPFUNC)hfs_vnop_create }, /* create */ | |
4138 | { &vnop_mknod_desc, (VOPFUNC)hfs_vnop_mknod }, /* mknod */ | |
4139 | { &vnop_open_desc, (VOPFUNC)hfs_vnop_open }, /* open */ | |
4140 | { &vnop_close_desc, (VOPFUNC)hfs_vnop_close }, /* close */ | |
4141 | { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr }, /* getattr */ | |
4142 | { &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr }, /* setattr */ | |
4143 | { &vnop_read_desc, (VOPFUNC)hfs_vnop_read }, /* read */ | |
4144 | { &vnop_write_desc, (VOPFUNC)hfs_vnop_write }, /* write */ | |
4145 | { &vnop_ioctl_desc, (VOPFUNC)hfs_vnop_ioctl }, /* ioctl */ | |
4146 | { &vnop_select_desc, (VOPFUNC)hfs_vnop_select }, /* select */ | |
4147 | { &vnop_revoke_desc, (VOPFUNC)nop_revoke }, /* revoke */ | |
4148 | { &vnop_exchange_desc, (VOPFUNC)hfs_vnop_exchange }, /* exchange */ | |
4149 | { &vnop_mmap_desc, (VOPFUNC)err_mmap }, /* mmap */ | |
4150 | { &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync }, /* fsync */ | |
4151 | { &vnop_remove_desc, (VOPFUNC)hfs_vnop_remove }, /* remove */ | |
4152 | { &vnop_link_desc, (VOPFUNC)hfs_vnop_link }, /* link */ | |
4153 | { &vnop_rename_desc, (VOPFUNC)hfs_vnop_rename }, /* rename */ | |
4154 | { &vnop_mkdir_desc, (VOPFUNC)hfs_vnop_mkdir }, /* mkdir */ | |
4155 | { &vnop_rmdir_desc, (VOPFUNC)hfs_vnop_rmdir }, /* rmdir */ | |
4156 | { &vnop_symlink_desc, (VOPFUNC)hfs_vnop_symlink }, /* symlink */ | |
4157 | { &vnop_readdir_desc, (VOPFUNC)hfs_vnop_readdir }, /* readdir */ | |
4158 | { &vnop_readdirattr_desc, (VOPFUNC)hfs_vnop_readdirattr }, /* readdirattr */ | |
4159 | { &vnop_readlink_desc, (VOPFUNC)hfs_vnop_readlink }, /* readlink */ | |
4160 | { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive }, /* inactive */ | |
4161 | { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim }, /* reclaim */ | |
4162 | { &vnop_strategy_desc, (VOPFUNC)hfs_vnop_strategy }, /* strategy */ | |
4163 | { &vnop_pathconf_desc, (VOPFUNC)hfs_vnop_pathconf }, /* pathconf */ | |
4164 | { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */ | |
4165 | { &vnop_allocate_desc, (VOPFUNC)hfs_vnop_allocate }, /* allocate */ | |
4166 | { &vnop_searchfs_desc, (VOPFUNC)hfs_vnop_search }, /* search fs */ | |
4167 | { &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite }, /* bwrite */ | |
4168 | { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein }, /* pagein */ | |
4169 | { &vnop_pageout_desc,(VOPFUNC) hfs_vnop_pageout }, /* pageout */ | |
4170 | { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */ | |
4171 | { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff }, /* blktooff */ | |
4172 | { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk }, /* offtoblk */ | |
4173 | { &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap }, /* blockmap */ | |
4174 | { &vnop_kqfilt_add_desc, (VOPFUNC)hfs_vnop_kqfiltadd }, /* kqfilt_add */ | |
4175 | { &vnop_kqfilt_remove_desc, (VOPFUNC)hfs_vnop_kqfiltremove }, /* kqfilt_remove */ | |
4176 | { &vnop_getxattr_desc, (VOPFUNC)hfs_vnop_getxattr}, | |
4177 | { &vnop_setxattr_desc, (VOPFUNC)hfs_vnop_setxattr}, | |
4178 | { &vnop_removexattr_desc, (VOPFUNC)hfs_vnop_removexattr}, | |
4179 | { &vnop_listxattr_desc, (VOPFUNC)hfs_vnop_listxattr}, | |
2d21ac55 A |
4180 | { &vnop_whiteout_desc, (VOPFUNC)hfs_vnop_whiteout}, |
4181 | #if NAMEDSTREAMS | |
4182 | { &vnop_getnamedstream_desc, (VOPFUNC)hfs_vnop_getnamedstream }, | |
4183 | { &vnop_makenamedstream_desc, (VOPFUNC)hfs_vnop_makenamedstream }, | |
4184 | { &vnop_removenamedstream_desc, (VOPFUNC)hfs_vnop_removenamedstream }, | |
4185 | #endif | |
9bccf70c A |
4186 | { NULL, (VOPFUNC)NULL } |
4187 | }; | |
4188 | ||
4189 | struct vnodeopv_desc hfs_vnodeop_opv_desc = | |
4190 | { &hfs_vnodeop_p, hfs_vnodeop_entries }; | |
4191 | ||
4192 | int (**hfs_specop_p)(void *); | |
4193 | struct vnodeopv_entry_desc hfs_specop_entries[] = { | |
91447636 A |
4194 | { &vnop_default_desc, (VOPFUNC)vn_default_error }, |
4195 | { &vnop_lookup_desc, (VOPFUNC)spec_lookup }, /* lookup */ | |
4196 | { &vnop_create_desc, (VOPFUNC)spec_create }, /* create */ | |
4197 | { &vnop_mknod_desc, (VOPFUNC)spec_mknod }, /* mknod */ | |
4198 | { &vnop_open_desc, (VOPFUNC)spec_open }, /* open */ | |
4199 | { &vnop_close_desc, (VOPFUNC)hfsspec_close }, /* close */ | |
4200 | { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr }, /* getattr */ | |
4201 | { &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr }, /* setattr */ | |
4202 | { &vnop_read_desc, (VOPFUNC)hfsspec_read }, /* read */ | |
4203 | { &vnop_write_desc, (VOPFUNC)hfsspec_write }, /* write */ | |
4204 | { &vnop_ioctl_desc, (VOPFUNC)spec_ioctl }, /* ioctl */ | |
4205 | { &vnop_select_desc, (VOPFUNC)spec_select }, /* select */ | |
4206 | { &vnop_revoke_desc, (VOPFUNC)spec_revoke }, /* revoke */ | |
4207 | { &vnop_mmap_desc, (VOPFUNC)spec_mmap }, /* mmap */ | |
4208 | { &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync }, /* fsync */ | |
4209 | { &vnop_remove_desc, (VOPFUNC)spec_remove }, /* remove */ | |
4210 | { &vnop_link_desc, (VOPFUNC)spec_link }, /* link */ | |
4211 | { &vnop_rename_desc, (VOPFUNC)spec_rename }, /* rename */ | |
4212 | { &vnop_mkdir_desc, (VOPFUNC)spec_mkdir }, /* mkdir */ | |
4213 | { &vnop_rmdir_desc, (VOPFUNC)spec_rmdir }, /* rmdir */ | |
4214 | { &vnop_symlink_desc, (VOPFUNC)spec_symlink }, /* symlink */ | |
4215 | { &vnop_readdir_desc, (VOPFUNC)spec_readdir }, /* readdir */ | |
4216 | { &vnop_readlink_desc, (VOPFUNC)spec_readlink }, /* readlink */ | |
4217 | { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive }, /* inactive */ | |
4218 | { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim }, /* reclaim */ | |
4219 | { &vnop_strategy_desc, (VOPFUNC)spec_strategy }, /* strategy */ | |
4220 | { &vnop_pathconf_desc, (VOPFUNC)spec_pathconf }, /* pathconf */ | |
4221 | { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */ | |
4222 | { &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite }, | |
91447636 A |
4223 | { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein }, /* Pagein */ |
4224 | { &vnop_pageout_desc, (VOPFUNC)hfs_vnop_pageout }, /* Pageout */ | |
4225 | { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */ | |
4226 | { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff }, /* blktooff */ | |
4227 | { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk }, /* offtoblk */ | |
9bccf70c A |
4228 | { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL } |
4229 | }; | |
4230 | struct vnodeopv_desc hfs_specop_opv_desc = | |
4231 | { &hfs_specop_p, hfs_specop_entries }; | |
4232 | ||
4233 | #if FIFO | |
4234 | int (**hfs_fifoop_p)(void *); | |
4235 | struct vnodeopv_entry_desc hfs_fifoop_entries[] = { | |
91447636 A |
4236 | { &vnop_default_desc, (VOPFUNC)vn_default_error }, |
4237 | { &vnop_lookup_desc, (VOPFUNC)fifo_lookup }, /* lookup */ | |
4238 | { &vnop_create_desc, (VOPFUNC)fifo_create }, /* create */ | |
4239 | { &vnop_mknod_desc, (VOPFUNC)fifo_mknod }, /* mknod */ | |
4240 | { &vnop_open_desc, (VOPFUNC)fifo_open }, /* open */ | |
4241 | { &vnop_close_desc, (VOPFUNC)hfsfifo_close }, /* close */ | |
4242 | { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr }, /* getattr */ | |
4243 | { &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr }, /* setattr */ | |
4244 | { &vnop_read_desc, (VOPFUNC)hfsfifo_read }, /* read */ | |
4245 | { &vnop_write_desc, (VOPFUNC)hfsfifo_write }, /* write */ | |
4246 | { &vnop_ioctl_desc, (VOPFUNC)fifo_ioctl }, /* ioctl */ | |
4247 | { &vnop_select_desc, (VOPFUNC)fifo_select }, /* select */ | |
4248 | { &vnop_revoke_desc, (VOPFUNC)fifo_revoke }, /* revoke */ | |
4249 | { &vnop_mmap_desc, (VOPFUNC)fifo_mmap }, /* mmap */ | |
4250 | { &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync }, /* fsync */ | |
4251 | { &vnop_remove_desc, (VOPFUNC)fifo_remove }, /* remove */ | |
4252 | { &vnop_link_desc, (VOPFUNC)fifo_link }, /* link */ | |
4253 | { &vnop_rename_desc, (VOPFUNC)fifo_rename }, /* rename */ | |
4254 | { &vnop_mkdir_desc, (VOPFUNC)fifo_mkdir }, /* mkdir */ | |
4255 | { &vnop_rmdir_desc, (VOPFUNC)fifo_rmdir }, /* rmdir */ | |
4256 | { &vnop_symlink_desc, (VOPFUNC)fifo_symlink }, /* symlink */ | |
4257 | { &vnop_readdir_desc, (VOPFUNC)fifo_readdir }, /* readdir */ | |
4258 | { &vnop_readlink_desc, (VOPFUNC)fifo_readlink }, /* readlink */ | |
4259 | { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive }, /* inactive */ | |
4260 | { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim }, /* reclaim */ | |
4261 | { &vnop_strategy_desc, (VOPFUNC)fifo_strategy }, /* strategy */ | |
4262 | { &vnop_pathconf_desc, (VOPFUNC)fifo_pathconf }, /* pathconf */ | |
4263 | { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */ | |
4264 | { &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite }, | |
4265 | { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein }, /* Pagein */ | |
4266 | { &vnop_pageout_desc, (VOPFUNC)hfs_vnop_pageout }, /* Pageout */ | |
4267 | { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */ | |
4268 | { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff }, /* blktooff */ | |
4269 | { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk }, /* offtoblk */ | |
4270 | { &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap }, /* blockmap */ | |
4271 | { &vnop_kqfilt_add_desc, (VOPFUNC)hfsfifo_kqfilt_add }, /* kqfilt_add */ | |
4272 | { &vnop_kqfilt_remove_desc, (VOPFUNC)hfsfifo_kqfilt_remove }, /* kqfilt_remove */ | |
9bccf70c A |
4273 | { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL } |
4274 | }; | |
4275 | struct vnodeopv_desc hfs_fifoop_opv_desc = | |
4276 | { &hfs_fifoop_p, hfs_fifoop_entries }; | |
4277 | #endif /* FIFO */ | |
4278 | ||
4279 | ||
4280 |