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