darwin-xnu/bsd/hfs/hfs_cnode.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2002-2005 Apple Computer, Inc. All rights reserved.
   3 *
   4 * @APPLE_LICENSE_HEADER_START@
   5 * 
   6 * The contents of this file constitute Original Code as defined in and
   7 * are subject to the Apple Public Source License Version 1.1 (the
   8 * "License").  You may not use this file except in compliance with the
   9 * License.  Please obtain a copy of the License at
  10 * http://www.apple.com/publicsource and read it before using this file.
  11 * 
  12 * This Original Code and all software distributed under the License are
  13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
  17 * License for the specific language governing rights and limitations
  18 * under the License.
  19 * 
  20 * @APPLE_LICENSE_HEADER_END@
  21 */
  22#include <sys/param.h>
  23#include <sys/systm.h>
  24#include <sys/proc.h>
  25#include <sys/vnode.h>
  26#include <sys/mount.h>
  27#include <sys/kernel.h>
  28#include <sys/malloc.h>
  29#include <sys/time.h>
  30#include <sys/ubc.h>
  31#include <sys/quota.h>
  32#include <sys/kdebug.h>
  33
  34#include <kern/locks.h>
  35
  36#include <miscfs/specfs/specdev.h>
  37#include <miscfs/fifofs/fifo.h>
  38
  39#include <hfs/hfs.h>
  40#include <hfs/hfs_catalog.h>
  41#include <hfs/hfs_cnode.h>
  42#include <hfs/hfs_quota.h>
  43
  44extern int prtactive;
  45
  46extern lck_attr_t *  hfs_lock_attr;
  47extern lck_grp_t *  hfs_mutex_group;
  48extern lck_grp_t *  hfs_rwlock_group;
  49
  50static int  hfs_filedone(struct vnode *vp, vfs_context_t context);
  51
  52static void  hfs_reclaim_cnode(struct cnode *);
  53
  54static int  hfs_valid_cnode(struct hfsmount *, struct vnode *, struct componentname *, cnid_t);
  55
  56static int hfs_isordered(struct cnode *, struct cnode *);
  57
  58int hfs_vnop_inactive(struct vnop_inactive_args *);
  59
  60int hfs_vnop_reclaim(struct vnop_reclaim_args *);
  61
  62
  63/*
  64 * Last reference to an cnode.  If necessary, write or delete it.
  65 */
  66__private_extern__
  67int
  68hfs_vnop_inactive(struct vnop_inactive_args *ap)
  69{
  70        struct vnode *vp = ap->a_vp;
  71        struct cnode *cp;
  72        struct hfsmount *hfsmp = VTOHFS(vp);
  73        struct proc *p = vfs_context_proc(ap->a_context);
  74        int error = 0;
  75        int recycle = 0;
  76        int forkcount = 0;
  77        int truncated = 0;
  78        int started_tr = 0;
  79        int took_trunc_lock = 0;
  80        cat_cookie_t cookie;
  81        int cat_reserve = 0;
  82        int lockflags;
  83        enum vtype v_type;
  84
  85        v_type = vnode_vtype(vp);
  86        cp = VTOC(vp);
  87
  88        if ((hfsmp->hfs_flags & HFS_READ_ONLY) || vnode_issystem(vp) ||
  89            (hfsmp->hfs_freezing_proc == p)) {
  90                return (0);
  91        }
  92
  93        /*
  94         * Ignore nodes related to stale file handles.
  95         */
  96        if (cp->c_mode == 0) {
  97                vnode_recycle(vp);
  98                return (0);
  99        }
 100
 101        if ((v_type == VREG) &&
 102            (ISSET(cp->c_flag, C_DELETED) || VTOF(vp)->ff_blocks)) {
 103                hfs_lock_truncate(cp, TRUE);
 104                took_trunc_lock = 1;
 105        }
 106
 107        /*
 108         * We do the ubc_setsize before we take the cnode
 109         * lock and before the hfs_truncate (since we'll
 110         * be inside a transaction).
 111         */
 112        if ((v_type == VREG || v_type == VLNK) &&
 113            (cp->c_flag & C_DELETED) &&
 114            (VTOF(vp)->ff_blocks != 0)) {
 115                ubc_setsize(vp, 0);
 116        }
 117
 118        (void) hfs_lock(cp, HFS_FORCE_LOCK);
 119
 120        if (v_type == VREG && !ISSET(cp->c_flag, C_DELETED) && VTOF(vp)->ff_blocks) {
 121                hfs_filedone(vp, ap->a_context);
 122        }
 123        /* 
 124         * Remove any directory hints
 125         */
 126        if (v_type == VDIR)
 127                hfs_reldirhints(cp, 0);
 128
 129        if (cp->c_datafork)
 130                ++forkcount;
 131        if (cp->c_rsrcfork)
 132                ++forkcount;
 133
 134        /* If needed, get rid of any fork's data for a deleted file */
 135        if ((v_type == VREG || v_type == VLNK) && (cp->c_flag & C_DELETED)) {
 136                if (VTOF(vp)->ff_blocks != 0) {
 137                    // start the transaction out here so that
 138                    // the truncate and the removal of the file
 139                    // are all in one transaction.  otherwise
 140                    // because this cnode is marked for deletion
 141                    // the truncate won't cause the catalog entry
 142                    // to get updated which means that we could
 143                    // free blocks but still keep a reference to
 144                    // them in the catalog entry and then double
 145                    // free them later.
 146                    //
 147//                  if (hfs_start_transaction(hfsmp) != 0) {
 148//                      error = EINVAL;
 149//                      goto out;
 150//                  }
 151//                  started_tr = 1;
 152                    
 153                        /*
 154                         * Since we're already inside a transaction,
 155                         * tell hfs_truncate to skip the ubc_setsize.
 156                         */
 157                        error = hfs_truncate(vp, (off_t)0, IO_NDELAY, 1, ap->a_context);
 158                        if (error)
 159                                goto out;
 160                        truncated = 1;
 161                }
 162                recycle = 1;
 163        }
 164
 165        /*
 166         * Check for a postponed deletion.
 167         * (only delete cnode when the last fork goes inactive)
 168         */
 169        if ((cp->c_flag & C_DELETED) && (forkcount <= 1)) {                     
 170                /*
 171                 * Mark cnode in transit so that no one can get this 
 172                 * cnode from cnode hash.
 173                 */
 174                hfs_chash_mark_in_transit(cp);
 175
 176                cp->c_flag &= ~C_DELETED;
 177                cp->c_flag |= C_NOEXISTS;   // XXXdbg
 178                cp->c_rdev = 0;
 179
 180                if (started_tr == 0) {
 181                    if (hfs_start_transaction(hfsmp) != 0) {
 182                        error = EINVAL;
 183                        goto out;
 184                    }
 185                    started_tr = 1;
 186                }
 187                
 188                /*
 189                 * Reserve some space in the Catalog file.
 190                 */
 191                if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, p))) {
 192                        goto out;
 193                }
 194                cat_reserve = 1;
 195
 196                lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
 197
 198                if (cp->c_blocks > 0)
 199                        printf("hfs_inactive: attempting to delete a non-empty file!");
 200
 201
 202                //
 203                // release the name pointer in the descriptor so that
 204                // cat_delete() will use the file-id to do the deletion.
 205                // in the case of hard links this is imperative (in the
 206                // case of regular files the fileid and cnid are the
 207                // same so it doesn't matter).
 208                //
 209                cat_releasedesc(&cp->c_desc);
 210                
 211                /*
 212                 * The descriptor name may be zero,
 213                 * in which case the fileid is used.
 214                 */
 215                error = cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
 216                
 217                if (error && truncated && (error != ENXIO))
 218                        printf("hfs_inactive: couldn't delete a truncated file!");
 219
 220                /* Update HFS Private Data dir */
 221                if (error == 0) {
 222                        hfsmp->hfs_privdir_attr.ca_entries--;
 223                        (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
 224                                &hfsmp->hfs_privdir_attr, NULL, NULL);
 225                }
 226
 227                if (error == 0) {
 228                        /* Delete any attributes, ignore errors */
 229                        (void) hfs_removeallattr(hfsmp, cp->c_fileid);
 230                }
 231
 232                hfs_systemfile_unlock(hfsmp, lockflags);
 233
 234                if (error)
 235                        goto out;
 236
 237#if QUOTA
 238                (void)hfs_chkiq(cp, -1, NOCRED, 0);
 239#endif /* QUOTA */
 240
 241                cp->c_mode = 0;
 242                cp->c_flag |= C_NOEXISTS;
 243                cp->c_touch_chgtime = TRUE;
 244                cp->c_touch_modtime = TRUE;
 245
 246                if (error == 0)
 247                        hfs_volupdate(hfsmp, VOL_RMFILE, 0);
 248        }
 249
 250        if ((cp->c_flag & C_MODIFIED) ||
 251            cp->c_touch_acctime || cp->c_touch_chgtime || cp->c_touch_modtime) {
 252                hfs_update(vp, 0);
 253        }
 254out:
 255        if (cat_reserve)
 256                cat_postflight(hfsmp, &cookie, p);
 257
 258        // XXXdbg - have to do this because a goto could have come here
 259        if (started_tr) {
 260            hfs_end_transaction(hfsmp);
 261            started_tr = 0;
 262        }
 263
 264        hfs_unlock(cp);
 265
 266        if (took_trunc_lock)
 267                hfs_unlock_truncate(cp);
 268
 269        /*
 270         * If we are done with the vnode, reclaim it
 271         * so that it can be reused immediately.
 272         */
 273        if (cp->c_mode == 0 || recycle)
 274                vnode_recycle(vp);
 275
 276        return (error);
 277}
 278
 279/*
 280 * File clean-up (zero fill and shrink peof).
 281 */
 282static int
 283hfs_filedone(struct vnode *vp, vfs_context_t context)
 284{
 285        struct cnode *cp;
 286        struct filefork *fp;
 287        struct hfsmount *hfsmp;
 288        off_t leof;
 289        u_long blks, blocksize;
 290
 291        cp = VTOC(vp);
 292        fp = VTOF(vp);
 293        hfsmp = VTOHFS(vp);
 294        leof = fp->ff_size;
 295
 296        if ((hfsmp->hfs_flags & HFS_READ_ONLY) || (fp->ff_blocks == 0))
 297                return (0);
 298
 299        hfs_unlock(cp);
 300        (void) cluster_push(vp, IO_CLOSE);
 301        hfs_lock(cp, HFS_FORCE_LOCK);
 302
 303        /*
 304         * Explicitly zero out the areas of file
 305         * that are currently marked invalid.
 306         */
 307        while (!CIRCLEQ_EMPTY(&fp->ff_invalidranges)) {
 308                struct rl_entry *invalid_range = CIRCLEQ_FIRST(&fp->ff_invalidranges);
 309                off_t start = invalid_range->rl_start;
 310                off_t end = invalid_range->rl_end;
 311        
 312                /* The range about to be written must be validated
 313                 * first, so that VNOP_BLOCKMAP() will return the
 314                 * appropriate mapping for the cluster code:
 315                 */
 316                rl_remove(start, end, &fp->ff_invalidranges);
 317
 318                hfs_unlock(cp);
 319                (void) cluster_write(vp, (struct uio *) 0,
 320                                     leof, end + 1, start, (off_t)0,
 321                                     IO_HEADZEROFILL | IO_NOZERODIRTY | IO_NOCACHE);
 322                hfs_lock(cp, HFS_FORCE_LOCK);
 323                cp->c_flag |= C_MODIFIED;
 324        }
 325        cp->c_flag &= ~C_ZFWANTSYNC;
 326        cp->c_zftimeout = 0;
 327        blocksize = VTOVCB(vp)->blockSize;
 328        blks = leof / blocksize;
 329        if (((off_t)blks * (off_t)blocksize) != leof)
 330                blks++;
 331        /*
 332         * Shrink the peof to the smallest size neccessary to contain the leof.
 333         */
 334        if (blks < fp->ff_blocks)
 335                (void) hfs_truncate(vp, leof, IO_NDELAY, 0, context);
 336        hfs_unlock(cp);
 337        (void) cluster_push(vp, IO_CLOSE);
 338        hfs_lock(cp, HFS_FORCE_LOCK);
 339        
 340        /*
 341         * If the hfs_truncate didn't happen to flush the vnode's
 342         * information out to disk, force it to be updated now that
 343         * all invalid ranges have been zero-filled and validated:
 344         */
 345        if (cp->c_flag & C_MODIFIED) {
 346                hfs_update(vp, 0);
 347        }
 348        return (0);
 349}
 350
 351
 352/*
 353 * Reclaim a cnode so that it can be used for other purposes.
 354 */
 355__private_extern__
 356int
 357hfs_vnop_reclaim(struct vnop_reclaim_args *ap)
 358{
 359        struct vnode *vp = ap->a_vp;
 360        struct cnode *cp;
 361        struct filefork *fp = NULL;
 362        struct filefork *altfp = NULL;
 363        int reclaim_cnode = 0;
 364
 365        (void) hfs_lock(VTOC(vp), HFS_FORCE_LOCK);
 366        cp = VTOC(vp);
 367
 368        /*
 369         * Keep track of an inactive hot file.
 370         */
 371        if (!vnode_isdir(vp) && !vnode_issystem(vp))
 372                (void) hfs_addhotfile(vp);
 373
 374        vnode_removefsref(vp);
 375
 376        /*
 377         * Find file fork for this vnode (if any)
 378         * Also check if another fork is active
 379         */
 380        if (cp->c_vp == vp) {
 381                fp = cp->c_datafork;
 382                altfp = cp->c_rsrcfork;
 383
 384                cp->c_datafork = NULL;
 385                cp->c_vp = NULL;
 386        } else if (cp->c_rsrc_vp == vp) {
 387                fp = cp->c_rsrcfork;
 388                altfp = cp->c_datafork;
 389
 390                cp->c_rsrcfork = NULL;
 391                cp->c_rsrc_vp = NULL;
 392        } else {
 393                panic("hfs_vnop_reclaim: vp points to wrong cnode\n");
 394        }
 395        /*
 396         * On the last fork, remove the cnode from its hash chain.
 397         */
 398        if (altfp == NULL) {
 399                /* If we can't remove it then the cnode must persist! */
 400                if (hfs_chashremove(cp) == 0)
 401                        reclaim_cnode = 1;
 402                /* 
 403                 * Remove any directory hints
 404                 */
 405                if (vnode_isdir(vp)) {
 406                        hfs_reldirhints(cp, 0);
 407                }
 408        }
 409        /* Release the file fork and related data */
 410        if (fp) {
 411                /* Dump cached symlink data */
 412                if (vnode_islnk(vp) && (fp->ff_symlinkptr != NULL)) {
 413                        FREE(fp->ff_symlinkptr, M_TEMP);
 414                }               
 415                FREE_ZONE(fp, sizeof(struct filefork), M_HFSFORK);
 416        }
 417
 418        /* 
 419         * If there was only one active fork then we can release the cnode.
 420         */
 421        if (reclaim_cnode) {
 422                hfs_chashwakeup(cp, H_ALLOC | H_TRANSIT);
 423                hfs_reclaim_cnode(cp);
 424        } else /* cnode in use */ {
 425                hfs_unlock(cp);
 426        }
 427
 428        vnode_clearfsnode(vp);
 429        return (0);
 430}
 431
 432
 433extern int (**hfs_vnodeop_p) (void *);
 434extern int (**hfs_specop_p)  (void *);
 435extern int (**hfs_fifoop_p)  (void *);
 436
 437/*
 438 * hfs_getnewvnode - get new default vnode
 439 *
 440 * The vnode is returned with an iocount and the cnode locked
 441 */
 442__private_extern__
 443int
 444hfs_getnewvnode(
 445        struct hfsmount *hfsmp,
 446        struct vnode *dvp,
 447        struct componentname *cnp,
 448        struct cat_desc *descp,
 449        int wantrsrc,
 450        struct cat_attr *attrp,
 451        struct cat_fork *forkp,
 452        struct vnode **vpp)
 453{
 454        struct mount *mp = HFSTOVFS(hfsmp);
 455        struct vnode *vp = NULL;
 456        struct vnode **cvpp;
 457        struct vnode *tvp = NULLVP;
 458        struct cnode *cp = NULL;
 459        struct filefork *fp = NULL;
 460        int i;
 461        int retval;
 462        int issystemfile;
 463        struct vnode_fsparam vfsp;
 464        enum vtype vtype;
 465
 466        if (attrp->ca_fileid == 0) {
 467                *vpp = NULL;
 468                return (ENOENT);
 469        }
 470
 471#if !FIFO
 472        if (IFTOVT(attrp->ca_mode) == VFIFO) {
 473                *vpp = NULL;
 474                return (ENOTSUP);
 475        }
 476#endif
 477        vtype = IFTOVT(attrp->ca_mode);
 478        issystemfile = (descp->cd_flags & CD_ISMETA) && (vtype == VREG);
 479
 480        /*
 481         * Get a cnode (new or existing)
 482         * skip getting the cnode lock if we are getting resource fork (wantrsrc == 2)
 483         */
 484        cp = hfs_chash_getcnode(hfsmp->hfs_raw_dev, attrp->ca_fileid, vpp, wantrsrc, (wantrsrc == 2));
 485
 486        /* Hardlinks may need an updated catalog descriptor */
 487        if ((cp->c_flag & C_HARDLINK) && descp->cd_nameptr && descp->cd_namelen > 0) {
 488                replace_desc(cp, descp);
 489        }
 490        /* Check if we found a matching vnode */
 491        if (*vpp != NULL)
 492                return (0);
 493
 494        /*
 495         * If this is a new cnode then initialize it.
 496         */
 497        if (ISSET(cp->c_hflag, H_ALLOC)) {
 498                lck_rw_init(&cp->c_truncatelock, hfs_rwlock_group, hfs_lock_attr);
 499
 500                /* Make sure its still valid (ie exists on disk). */
 501                if (!hfs_valid_cnode(hfsmp, dvp, (wantrsrc ? NULL : cnp), cp->c_fileid)) {
 502                        hfs_chash_abort(cp);
 503                        hfs_reclaim_cnode(cp);
 504                        *vpp = NULL;
 505                        return (ENOENT);
 506                }
 507                bcopy(attrp, &cp->c_attr, sizeof(struct cat_attr));
 508                bcopy(descp, &cp->c_desc, sizeof(struct cat_desc));
 509
 510                /* The name was inherited so clear descriptor state... */
 511                descp->cd_namelen = 0;
 512                descp->cd_nameptr = NULL;
 513                descp->cd_flags &= ~CD_HASBUF;
 514
 515                /* Tag hardlinks */
 516                if (IFTOVT(cp->c_mode) == VREG &&
 517                    (descp->cd_cnid != attrp->ca_fileid)) {
 518                        cp->c_flag |= C_HARDLINK;
 519                }
 520
 521                /* Take one dev reference for each non-directory cnode */
 522                if (IFTOVT(cp->c_mode) != VDIR) {
 523                        cp->c_devvp = hfsmp->hfs_devvp;
 524                        vnode_ref(cp->c_devvp);
 525                }
 526#if QUOTA
 527                for (i = 0; i < MAXQUOTAS; i++)
 528                        cp->c_dquot[i] = NODQUOT;
 529#endif /* QUOTA */
 530        }
 531
 532        if (IFTOVT(cp->c_mode) == VDIR) {
 533                if (cp->c_vp != NULL)
 534                        panic("hfs_getnewvnode: orphaned vnode (data)");
 535                cvpp = &cp->c_vp;
 536        } else {
 537                if (forkp && attrp->ca_blocks < forkp->cf_blocks)
 538                        panic("hfs_getnewvnode: bad ca_blocks (too small)");
 539                /*
 540                 * Allocate and initialize a file fork...
 541                 */
 542                MALLOC_ZONE(fp, struct filefork *, sizeof(struct filefork),
 543                        M_HFSFORK, M_WAITOK);
 544                fp->ff_cp = cp;
 545                if (forkp)
 546                        bcopy(forkp, &fp->ff_data, sizeof(struct cat_fork));
 547                else
 548                        bzero(&fp->ff_data, sizeof(struct cat_fork));
 549                rl_init(&fp->ff_invalidranges);
 550                fp->ff_sysfileinfo = 0;
 551
 552                if (wantrsrc) {
 553                        if (cp->c_rsrcfork != NULL)
 554                                panic("hfs_getnewvnode: orphaned rsrc fork");
 555                        if (cp->c_rsrc_vp != NULL)
 556                                panic("hfs_getnewvnode: orphaned vnode (rsrc)");
 557                        cp->c_rsrcfork = fp;
 558                        cvpp = &cp->c_rsrc_vp;
 559                        if ( (tvp = cp->c_vp) != NULLVP )
 560                                cp->c_flag |= C_NEED_DVNODE_PUT;
 561                } else {
 562                        if (cp->c_datafork != NULL)
 563                                panic("hfs_getnewvnode: orphaned data fork");
 564                        if (cp->c_vp != NULL)
 565                                panic("hfs_getnewvnode: orphaned vnode (data)");
 566                        cp->c_datafork = fp;
 567                        cvpp = &cp->c_vp;
 568                        if ( (tvp = cp->c_rsrc_vp) != NULLVP)
 569                                cp->c_flag |= C_NEED_RVNODE_PUT;
 570                }
 571        }
 572        if (tvp != NULLVP) {
 573                /*
 574                 * grab an iocount on the vnode we weren't
 575                 * interested in (i.e. we want the resource fork
 576                 * but the cnode already has the data fork)
 577                 * to prevent it from being
 578                 * recycled by us when we call vnode_create
 579                 * which will result in a deadlock when we
 580                 * try to take the cnode lock in hfs_vnop_fsync or
 581                 * hfs_vnop_reclaim... vnode_get can be called here
 582                 * because we already hold the cnode lock which will
 583                 * prevent the vnode from changing identity until
 584                 * we drop it.. vnode_get will not block waiting for
 585                 * a change of state... however, it will return an
 586                 * error if the current iocount == 0 and we've already
 587                 * started to terminate the vnode... we don't need/want to
 588                 * grab an iocount in the case since we can't cause
 589                 * the fileystem to be re-entered on this thread for this vp
 590                 *
 591                 * the matching vnode_put will happen in hfs_unlock
 592                 * after we've dropped the cnode lock
 593                 */
 594                if ( vnode_get(tvp) != 0)
 595                        cp->c_flag &= ~(C_NEED_RVNODE_PUT | C_NEED_DVNODE_PUT);
 596        }
 597        vfsp.vnfs_mp = mp;
 598        vfsp.vnfs_vtype = vtype;
 599        vfsp.vnfs_str = "hfs";
 600        vfsp.vnfs_dvp = dvp;
 601        vfsp.vnfs_fsnode = cp;
 602        vfsp.vnfs_cnp = cnp;
 603        if (vtype == VFIFO )
 604                vfsp.vnfs_vops = hfs_fifoop_p;
 605        else if (vtype == VBLK || vtype == VCHR)
 606                vfsp.vnfs_vops = hfs_specop_p;
 607        else
 608                vfsp.vnfs_vops = hfs_vnodeop_p;
 609                
 610        if (vtype == VBLK || vtype == VCHR)
 611                vfsp.vnfs_rdev = attrp->ca_rdev;
 612        else
 613                vfsp.vnfs_rdev = 0;
 614
 615        if (forkp) 
 616                vfsp.vnfs_filesize = forkp->cf_size;
 617        else
 618                vfsp.vnfs_filesize = 0;
 619
 620        if (dvp && cnp && (cnp->cn_flags & MAKEENTRY))
 621                vfsp.vnfs_flags = 0;
 622        else
 623                vfsp.vnfs_flags = VNFS_NOCACHE;
 624
 625        /* Tag system files */
 626        vfsp.vnfs_marksystem = issystemfile;
 627
 628        /* Tag root directory */
 629        if (descp->cd_cnid == kHFSRootFolderID)
 630                vfsp.vnfs_markroot = 1;
 631        else    
 632                vfsp.vnfs_markroot = 0;
 633
 634        if ((retval = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, cvpp))) {
 635                if (fp) {
 636                        if (fp == cp->c_datafork)
 637                                cp->c_datafork = NULL;
 638                        else
 639                                cp->c_rsrcfork = NULL;
 640
 641                        FREE_ZONE(fp, sizeof(struct filefork), M_HFSFORK);
 642                }
 643                /*
 644                 * If this is a newly created cnode or a vnode reclaim
 645                 * occurred during the attachment, then cleanup the cnode.
 646                 */
 647                if ((cp->c_vp == NULL) && (cp->c_rsrc_vp == NULL)) {
 648                        hfs_chash_abort(cp);
 649                        hfs_reclaim_cnode(cp);
 650                } else {
 651                        hfs_chashwakeup(cp, H_ALLOC | H_ATTACH);
 652                        hfs_unlock(cp);
 653                }
 654                *vpp = NULL;
 655                return (retval);
 656        }
 657        vp = *cvpp;
 658        vnode_addfsref(vp);
 659        vnode_settag(vp, VT_HFS);
 660        if (cp->c_flag & C_HARDLINK)
 661                vnode_set_hard_link(vp);
 662        hfs_chashwakeup(cp, H_ALLOC | H_ATTACH);
 663
 664        /*
 665         * Stop tracking an active hot file.
 666         */
 667        if (!vnode_isdir(vp) && !vnode_issystem(vp))
 668                (void) hfs_removehotfile(vp);
 669
 670        *vpp = vp;
 671        return (0);
 672}
 673
 674
 675static void
 676hfs_reclaim_cnode(struct cnode *cp)
 677{
 678#if QUOTA
 679        int i;
 680
 681        for (i = 0; i < MAXQUOTAS; i++) {
 682                if (cp->c_dquot[i] != NODQUOT) {
 683                        dqreclaim(cp->c_dquot[i]);
 684                        cp->c_dquot[i] = NODQUOT;
 685                }
 686        }
 687#endif /* QUOTA */
 688
 689        if (cp->c_devvp) {
 690                struct vnode *tmp_vp = cp->c_devvp;
 691
 692                cp->c_devvp = NULL;
 693                vnode_rele(tmp_vp);
 694        }
 695
 696        /* 
 697         * If the descriptor has a name then release it
 698         */
 699        if (cp->c_desc.cd_flags & CD_HASBUF) {
 700                char *nameptr;
 701
 702                nameptr = cp->c_desc.cd_nameptr;
 703                cp->c_desc.cd_nameptr = 0;
 704                cp->c_desc.cd_flags &= ~CD_HASBUF;
 705                cp->c_desc.cd_namelen = 0;
 706                vfs_removename(nameptr);
 707        }
 708
 709        lck_rw_destroy(&cp->c_rwlock, hfs_rwlock_group);
 710        lck_rw_destroy(&cp->c_truncatelock, hfs_rwlock_group);
 711        bzero(cp, sizeof(struct cnode));
 712        FREE_ZONE(cp, sizeof(struct cnode), M_HFSNODE);
 713}
 714
 715
 716static int
 717hfs_valid_cnode(struct hfsmount *hfsmp, struct vnode *dvp, struct componentname *cnp, cnid_t cnid)
 718{
 719        struct cat_attr attr;
 720        struct cat_desc cndesc;
 721        int stillvalid = 0;
 722        int lockflags;
 723
 724        /* System files are always valid */
 725        if (cnid < kHFSFirstUserCatalogNodeID)
 726                return (1);
 727
 728        /* XXX optimization:  check write count in dvp */
 729
 730        lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
 731
 732        if (dvp && cnp) {
 733                bzero(&cndesc, sizeof(cndesc));
 734                cndesc.cd_nameptr = cnp->cn_nameptr;
 735                cndesc.cd_namelen = cnp->cn_namelen;
 736                cndesc.cd_parentcnid = VTOC(dvp)->c_cnid;
 737                cndesc.cd_hint = VTOC(dvp)->c_childhint;
 738
 739                if ((cat_lookup(hfsmp, &cndesc, 0, NULL, &attr, NULL, NULL) == 0) &&
 740                    (cnid == attr.ca_fileid)) {
 741                        stillvalid = 1;
 742                }
 743        } else {
 744                if (cat_idlookup(hfsmp, cnid, NULL, NULL, NULL) == 0) {
 745                        stillvalid = 1;
 746                }
 747        }
 748        hfs_systemfile_unlock(hfsmp, lockflags);
 749
 750        return (stillvalid);
 751}
 752
 753/*
 754 * Touch cnode times based on c_touch_xxx flags
 755 *
 756 * cnode must be locked exclusive
 757 *
 758 * This will also update the volume modify time
 759 */
 760__private_extern__
 761void
 762hfs_touchtimes(struct hfsmount *hfsmp, struct cnode* cp)
 763{
 764        /* HFS Standard doesn't support access times */
 765        if (hfsmp->hfs_flags & HFS_STANDARD) {
 766                cp->c_touch_acctime = FALSE;
 767        }
 768
 769        if (cp->c_touch_acctime || cp->c_touch_chgtime || cp->c_touch_modtime) {
 770                struct timeval tv;
 771                int touchvol = 0;
 772
 773                microtime(&tv);
 774                    
 775                if (cp->c_touch_acctime) {
 776                        cp->c_atime = tv.tv_sec;
 777                        /*
 778                         * When the access time is the only thing changing
 779                         * then make sure its sufficiently newer before
 780                         * committing it to disk.
 781                         */
 782                        if ((((u_int32_t)cp->c_atime - (u_int32_t)(cp)->c_attr.ca_atimeondisk) >
 783                              ATIME_ONDISK_ACCURACY)) {
 784                                cp->c_flag |= C_MODIFIED;
 785                        }
 786                        cp->c_touch_acctime = FALSE;
 787                }
 788                if (cp->c_touch_modtime) {
 789                        cp->c_mtime = tv.tv_sec;
 790                        cp->c_touch_modtime = FALSE;
 791                        cp->c_flag |= C_MODIFIED;
 792                        touchvol = 1;
 793#if 1
 794                        /*
 795                         * HFS dates that WE set must be adjusted for DST
 796                         */
 797                        if ((hfsmp->hfs_flags & HFS_STANDARD) && gTimeZone.tz_dsttime) {
 798                                cp->c_mtime += 3600;
 799                        }
 800#endif
 801                }
 802                if (cp->c_touch_chgtime) {
 803                        cp->c_ctime = tv.tv_sec;
 804                        cp->c_touch_chgtime = FALSE;
 805                        cp->c_flag |= C_MODIFIED;
 806                        touchvol = 1;
 807                }
 808
 809                /* Touch the volume modtime if needed */
 810                if (touchvol) {
 811                        HFSTOVCB(hfsmp)->vcbFlags |= 0xFF00;
 812                        HFSTOVCB(hfsmp)->vcbLsMod = tv.tv_sec;
 813                }
 814        }
 815}
 816
 817/*
 818 * Lock a cnode.
 819 */
 820__private_extern__
 821int
 822hfs_lock(struct cnode *cp, enum hfslocktype locktype)
 823{
 824        void * thread = current_thread();
 825
 826        /* System files need to keep track of owner */
 827        if ((cp->c_fileid < kHFSFirstUserCatalogNodeID) &&
 828            (cp->c_fileid > kHFSRootFolderID) &&
 829            (locktype != HFS_SHARED_LOCK)) {
 830
 831                /*
 832                 * The extents and bitmap file locks support
 833                 * recursion and are always taken exclusive.
 834                 */
 835                if (cp->c_fileid == kHFSExtentsFileID ||
 836                    cp->c_fileid == kHFSAllocationFileID) {
 837                        if (cp->c_lockowner == thread) {
 838                                cp->c_syslockcount++;
 839                        } else {
 840                                lck_rw_lock_exclusive(&cp->c_rwlock);
 841                                cp->c_lockowner = thread;
 842                                cp->c_syslockcount = 1;
 843                        }
 844                } else {
 845                        lck_rw_lock_exclusive(&cp->c_rwlock);
 846                        cp->c_lockowner = thread;
 847                }
 848        } else if (locktype == HFS_SHARED_LOCK) {
 849                lck_rw_lock_shared(&cp->c_rwlock);
 850                cp->c_lockowner = HFS_SHARED_OWNER;
 851        } else {
 852                lck_rw_lock_exclusive(&cp->c_rwlock);
 853                cp->c_lockowner = thread;
 854        }
 855        /*
 856         * Skip cnodes that no longer exist (were deleted).
 857         */
 858        if ((locktype != HFS_FORCE_LOCK) &&
 859            ((cp->c_desc.cd_flags & CD_ISMETA) == 0) &&
 860            (cp->c_flag & C_NOEXISTS)) {
 861                hfs_unlock(cp);
 862                return (ENOENT);
 863        }
 864        return (0);
 865}
 866
 867/*
 868 * Lock a pair of cnodes.
 869 */
 870__private_extern__
 871int
 872hfs_lockpair(struct cnode *cp1, struct cnode *cp2, enum hfslocktype locktype)
 873{
 874        struct cnode *first, *last;
 875        int error;
 876
 877        /*
 878         * If cnodes match then just lock one.
 879         */
 880        if (cp1 == cp2) {
 881                return hfs_lock(cp1, locktype);
 882        }
 883
 884        /*
 885         * Lock in cnode parent-child order (if there is a relationship);
 886         * otherwise lock in cnode address order.
 887         */
 888        if ((IFTOVT(cp1->c_mode) == VDIR) && (cp1->c_fileid == cp2->c_parentcnid)) {
 889                first = cp1;
 890                last = cp2;
 891        } else if (cp1 < cp2) {
 892                first = cp1;
 893                last = cp2;
 894        } else {
 895                first = cp2;
 896                last = cp1;
 897        }
 898
 899        if ( (error = hfs_lock(first, locktype))) {
 900                return (error);
 901        }
 902        if ( (error = hfs_lock(last, locktype))) {
 903                hfs_unlock(first);
 904                return (error);
 905        }
 906        return (0);
 907}
 908
 909/*
 910 * Check ordering of two cnodes. Return true if they are are in-order.
 911 */
 912static int
 913hfs_isordered(struct cnode *cp1, struct cnode *cp2)
 914{
 915        if (cp1 == cp2)
 916                return (0);
 917        if (cp1 == NULL || cp2 == (struct cnode *)0xffffffff)
 918                return (1);
 919        if (cp2 == NULL || cp1 == (struct cnode *)0xffffffff)
 920                return (0);
 921        if (cp1->c_fileid == cp2->c_parentcnid)
 922                return (1);  /* cp1 is the parent and should go first */
 923        if (cp2->c_fileid == cp1->c_parentcnid)
 924                return (0);  /* cp1 is the child and should go last */
 925
 926        return (cp1 < cp2);  /* fall-back is to use address order */
 927}
 928
 929/*
 930 * Acquire 4 cnode locks.
 931 *   - locked in cnode parent-child order (if there is a relationship)
 932 *     otherwise lock in cnode address order (lesser address first).
 933 *   - all or none of the locks are taken
 934 *   - only one lock taken per cnode (dup cnodes are skipped)
 935 *   - some of the cnode pointers may be null
 936 */
 937__private_extern__
 938int
 939hfs_lockfour(struct cnode *cp1, struct cnode *cp2, struct cnode *cp3,
 940             struct cnode *cp4, enum hfslocktype locktype)
 941{
 942        struct cnode * a[3];
 943        struct cnode * b[3];
 944        struct cnode * list[4];
 945        struct cnode * tmp;
 946        int i, j, k;
 947        int error;
 948
 949        if (hfs_isordered(cp1, cp2)) {
 950                a[0] = cp1; a[1] = cp2;
 951        } else {
 952                a[0] = cp2; a[1] = cp1;
 953        }
 954        if (hfs_isordered(cp3, cp4)) {
 955                b[0] = cp3; b[1] = cp4;
 956        } else {
 957                b[0] = cp4; b[1] = cp3;
 958        }
 959        a[2] = (struct cnode *)0xffffffff;  /* sentinel value */
 960        b[2] = (struct cnode *)0xffffffff;  /* sentinel value */
 961
 962        /*
 963         * Build the lock list, skipping over duplicates
 964         */
 965        for (i = 0, j = 0, k = 0; (i < 2 || j < 2); ) {
 966                tmp = hfs_isordered(a[i], b[j]) ? a[i++] : b[j++];
 967                if (k == 0 || tmp != list[k-1])
 968                        list[k++] = tmp;
 969        }
 970
 971        /*
 972         * Now we can lock using list[0 - k].
 973         * Skip over NULL entries.
 974         */
 975        for (i = 0; i < k; ++i) {
 976                if (list[i])
 977                        if ((error = hfs_lock(list[i], locktype))) {
 978                                /* Drop any locks we acquired. */
 979                                while (--i >= 0) {
 980                                        if (list[i])
 981                                                hfs_unlock(list[i]);
 982                                }
 983                                return (error);
 984                        }
 985        }
 986        return (0);
 987}
 988
 989
 990/*
 991 * Unlock a cnode.
 992 */
 993__private_extern__
 994void
 995hfs_unlock(struct cnode *cp)
 996{
 997        vnode_t rvp = NULLVP;
 998        vnode_t dvp = NULLVP;
 999
1000        /* System files need to keep track of owner */
1001        if ((cp->c_fileid < kHFSFirstUserCatalogNodeID) &&
1002            (cp->c_fileid > kHFSRootFolderID) &&
1003            (cp->c_datafork != NULL)) {
1004                /*
1005                 * The extents and bitmap file locks support
1006                 * recursion and are always taken exclusive.
1007                 */
1008                if (cp->c_fileid == kHFSExtentsFileID ||
1009                    cp->c_fileid == kHFSAllocationFileID) {
1010                        if (--cp->c_syslockcount > 0) {
1011                                return;
1012                        }
1013                }
1014        }
1015        if (cp->c_flag & C_NEED_DVNODE_PUT)
1016                dvp = cp->c_vp;
1017
1018        if (cp->c_flag & C_NEED_RVNODE_PUT)
1019                rvp = cp->c_rsrc_vp;
1020
1021        cp->c_flag &= ~(C_NEED_DVNODE_PUT | C_NEED_RVNODE_PUT);
1022
1023        cp-> c_lockowner = NULL;
1024        lck_rw_done(&cp->c_rwlock);
1025
1026        if (dvp)
1027                vnode_put(dvp);
1028        if (rvp)
1029                vnode_put(rvp);
1030}
1031
1032/*
1033 * Unlock a pair of cnodes.
1034 */
1035__private_extern__
1036void
1037hfs_unlockpair(struct cnode *cp1, struct cnode *cp2)
1038{
1039        hfs_unlock(cp1);
1040        if (cp2 != cp1)
1041                hfs_unlock(cp2);
1042}
1043
1044/*
1045 * Unlock a group of cnodes.
1046 */
1047__private_extern__
1048void
1049hfs_unlockfour(struct cnode *cp1, struct cnode *cp2, struct cnode *cp3, struct cnode *cp4)
1050{
1051        struct cnode * list[4];
1052        int i, k = 0;
1053
1054        if (cp1) {
1055                hfs_unlock(cp1);
1056                list[k++] = cp1;
1057        }
1058        if (cp2) {
1059                for (i = 0; i < k; ++i) {
1060                        if (list[i] == cp2)
1061                                goto skip1;
1062                }
1063                hfs_unlock(cp2);
1064                list[k++] = cp2;
1065        }
1066skip1:
1067        if (cp3) {
1068                for (i = 0; i < k; ++i) {
1069                        if (list[i] == cp3)
1070                                goto skip2;
1071                }
1072                hfs_unlock(cp3);
1073                list[k++] = cp3;
1074        }
1075skip2:
1076        if (cp4) {
1077                for (i = 0; i < k; ++i) {
1078                        if (list[i] == cp4)
1079                                return;
1080                }
1081                hfs_unlock(cp4);
1082        }
1083}
1084
1085
1086/*
1087 * Protect a cnode against a truncation.
1088 *
1089 * Used mainly by read/write since they don't hold the
1090 * cnode lock across calls to the cluster layer.
1091 *
1092 * The process doing a truncation must take the lock
1093 * exclusive. The read/write processes can take it
1094 * non-exclusive.
1095 */
1096__private_extern__
1097void
1098hfs_lock_truncate(struct cnode *cp, int exclusive)
1099{
1100        if (cp->c_lockowner == current_thread())
1101                panic("hfs_lock_truncate: cnode 0x%08x locked!", cp);
1102
1103        if (exclusive)
1104                lck_rw_lock_exclusive(&cp->c_truncatelock);
1105        else
1106                lck_rw_lock_shared(&cp->c_truncatelock);
1107}
1108
1109__private_extern__
1110void
1111hfs_unlock_truncate(struct cnode *cp)
1112{
1113        lck_rw_done(&cp->c_truncatelock);
1114}
1115
1116
1117
1118
1119
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.