darwin-xnu/bsd/hfs/hfs_link.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 1999-2004 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
  23
  24#include <sys/systm.h>
  25#include <sys/kernel.h>
  26#include <sys/malloc.h>
  27#include <sys/mount.h>
  28#include <sys/stat.h>
  29#include <sys/vnode.h>
  30#include <vfs/vfs_support.h>
  31#include <libkern/libkern.h>
  32
  33#include "hfs.h"
  34#include "hfs_catalog.h"
  35#include "hfs_format.h"
  36#include "hfs_endian.h"
  37
  38
  39static int cur_link_id = 0;
  40
  41
  42/*
  43 * Create a new indirect link
  44 *
  45 * An indirect link is a reference to a data node.  The only useable
  46 * fields in the link are the link number, parentID, name and text
  47 * encoding.  All other catalog fields are ignored.
  48 */
  49static int
  50createindirectlink(struct hfsmount *hfsmp, u_int32_t linknum,
  51                        u_int32_t linkparid, char *linkName, cnid_t *linkcnid)
  52{
  53        struct FndrFileInfo *fip;
  54        struct cat_desc desc;
  55        struct cat_attr attr;
  56        int result;
  57
  58        /* Setup the descriptor */
  59        bzero(&desc, sizeof(desc));
  60        desc.cd_nameptr = linkName;
  61        desc.cd_namelen = strlen(linkName);
  62        desc.cd_parentcnid = linkparid;
  63
  64        /* Setup the default attributes */
  65        bzero(&attr, sizeof(attr));
  66        
  67        /* links are matched to data nodes by link ID and to volumes by create date */
  68        attr.ca_rdev = linknum;  /* note: cat backend overloads ca_rdev to be the linknum when nlink = 0 */
  69        attr.ca_itime = HFSTOVCB(hfsmp)->vcbCrDate;
  70        attr.ca_mode = S_IFREG;
  71
  72        fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
  73        fip->fdType    = SWAP_BE32 (kHardLinkFileType); /* 'hlnk' */
  74        fip->fdCreator = SWAP_BE32 (kHFSPlusCreator);   /* 'hfs+' */
  75        fip->fdFlags   = SWAP_BE16 (kHasBeenInited);
  76
  77        /* Create the indirect link directly in the catalog */
  78        result = cat_create(hfsmp, &desc, &attr, NULL);
  79
  80        if (result == 0 && linkcnid != NULL)
  81                *linkcnid = attr.ca_fileid;
  82
  83        return (result);
  84}
  85
  86
  87/*
  88 * 2 locks are needed (dvp and vp)
  89 * also need catalog lock
  90 *
  91 * caller's responsibility:
  92 *              componentname cleanup
  93 *              unlocking dvp and vp
  94 */
  95static int
  96hfs_makelink(struct hfsmount *hfsmp, struct cnode *cp, struct cnode *dcp,
  97                struct componentname *cnp)
  98{
  99        vfs_context_t ctx = cnp->cn_context;
 100        struct proc *p = vfs_context_proc(ctx);
 101        u_int32_t indnodeno = 0;
 102        char inodename[32]; 
 103        struct cat_desc to_desc;
 104        int newlink = 0;
 105        int lockflags;
 106        int retval;
 107        cat_cookie_t cookie;
 108        cnid_t orig_cnid;
 109
 110        if (cur_link_id == 0) {
 111            cur_link_id = ((random() & 0x3fffffff) + 100);
 112            // printf("hfs: initializing cur link id to: 0x%.8x\n", cur_link_id);
 113        }
 114        
 115        /* We don't allow link nodes in our Private Meta Data folder! */
 116        if (dcp->c_fileid == hfsmp->hfs_privdir_desc.cd_cnid)
 117                return (EPERM);
 118
 119        if (hfs_freeblks(hfsmp, 0) == 0)
 120                return (ENOSPC);
 121
 122        bzero(&cookie, sizeof(cat_cookie_t));
 123        /* Reserve some space in the Catalog file. */
 124        if ((retval = cat_preflight(hfsmp, (2 * CAT_CREATE)+ CAT_RENAME, &cookie, p))) {
 125                return (retval);
 126        }
 127
 128        lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
 129
 130        // save off a copy of the current cnid so we can put 
 131        // it back if we get errors down below
 132        orig_cnid = cp->c_desc.cd_cnid;
 133
 134        /*
 135         * If this is a new hardlink then we need to create the data
 136         * node (inode) and replace the original file with a link node.
 137         */
 138        if (cp->c_nlink == 2 && (cp->c_flag & C_HARDLINK) == 0) {
 139                newlink = 1;
 140                bzero(&to_desc, sizeof(to_desc));
 141                to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
 142                to_desc.cd_cnid = cp->c_fileid;
 143
 144                do {
 145                        /* get a unique indirect node number */
 146                        if (retval == 0) {
 147                            indnodeno = cp->c_fileid;
 148                        } else {
 149                            indnodeno = cur_link_id++;
 150                        }
 151
 152                        MAKE_INODE_NAME(inodename, indnodeno);
 153
 154                        /* move source file to data node directory */
 155                        to_desc.cd_nameptr = inodename;
 156                        to_desc.cd_namelen = strlen(inodename);
 157                
 158                        retval = cat_rename(hfsmp, &cp->c_desc, &hfsmp->hfs_privdir_desc,
 159                                        &to_desc, NULL);
 160
 161                        if (retval != 0 && retval != EEXIST) {
 162                            printf("hfs_makelink: cat_rename to %s failed (%d). fileid %d\n",
 163                                inodename, retval, cp->c_fileid);
 164                        }
 165
 166                } while (retval == EEXIST);
 167                if (retval)
 168                        goto out;
 169
 170                /* Replace source file with link node */
 171                retval = createindirectlink(hfsmp, indnodeno, cp->c_parentcnid,
 172                                cp->c_desc.cd_nameptr, &cp->c_desc.cd_cnid);
 173                if (retval) {
 174                    /* put it source file back */
 175                    int err;
 176
 177                    // Put this back to what it was before.
 178                    cp->c_desc.cd_cnid = orig_cnid;
 179
 180                    err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
 181                    if (err)
 182                        panic("hfs_makelink: error %d from cat_rename backout 1", err);
 183                    goto out;
 184                }
 185                cp->c_rdev = indnodeno;
 186        } else {
 187                indnodeno = cp->c_rdev;
 188        }
 189
 190        /*
 191         * Create a catalog entry for the new link (parentID + name).
 192         */
 193        retval = createindirectlink(hfsmp, indnodeno, dcp->c_fileid, cnp->cn_nameptr, NULL);
 194        if (retval && newlink) {
 195            int err;
 196
 197            /* Get rid of new link */
 198            (void) cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
 199            
 200            // Put this back to what it was before.
 201            cp->c_desc.cd_cnid = orig_cnid;
 202
 203            /* Put the source file back */
 204            err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
 205            if (err)
 206                panic("hfs_makelink: error %d from cat_rename backout 2", err);
 207
 208            goto out;
 209        }
 210
 211        /*
 212         * Finally, if this is a new hardlink then:
 213         *  - update HFS Private Data dir
 214         *  - mark the cnode as a hard link
 215         */
 216        if (newlink) {
 217                vnode_t vp;
 218                
 219            if (retval != 0) {
 220                panic("hfs_makelink: retval %d but newlink = 1!\n", retval);
 221            }
 222            
 223                hfsmp->hfs_privdir_attr.ca_entries++;
 224                retval = cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
 225                                    &hfsmp->hfs_privdir_attr, NULL, NULL);
 226                if (retval != 0) {
 227                    panic("hfs_makelink: cat_update of privdir failed! (%d)\n",
 228                          retval);
 229                }
 230                hfs_volupdate(hfsmp, VOL_MKFILE, 0);
 231                cp->c_flag |= C_HARDLINK;
 232                if ((vp = cp->c_vp) != NULLVP) {
 233                        if (vnode_get(vp) == 0) {
 234                                vnode_set_hard_link(vp);
 235                                vnode_put(vp);
 236                        }
 237                }
 238                if ((vp = cp->c_rsrc_vp) != NULLVP) {
 239                        if (vnode_get(vp) == 0) {
 240                                vnode_set_hard_link(vp);
 241                                vnode_put(vp);
 242                        }
 243                }
 244                cp->c_touch_chgtime = TRUE;
 245                cp->c_flag |= C_FORCEUPDATE;
 246        }
 247        dcp->c_flag |= C_FORCEUPDATE;
 248
 249out:
 250        hfs_systemfile_unlock(hfsmp, lockflags);
 251
 252        cat_postflight(hfsmp, &cookie, p);
 253        return (retval);
 254}
 255
 256
 257/*
 258 * link vnode call
 259#% link         vp      U U U
 260#% link         tdvp    L U U
 261#
 262 vnop_link {
 263     IN WILLRELE struct vnode *vp;
 264     IN struct vnode *targetPar_vp;
 265     IN struct componentname *cnp;
 266     IN vfs_context_t context;
 267
 268     */
 269__private_extern__
 270int
 271hfs_vnop_link(struct vnop_link_args *ap)
 272{
 273        struct hfsmount *hfsmp;
 274        struct vnode *vp = ap->a_vp;
 275        struct vnode *tdvp = ap->a_tdvp;
 276        struct componentname *cnp = ap->a_cnp;
 277        struct cnode *cp;
 278        struct cnode *tdcp;
 279        enum vtype v_type;
 280        int error, ret, lockflags;
 281        struct cat_desc cndesc;
 282
 283        if (VTOVCB(tdvp)->vcbSigWord != kHFSPlusSigWord) {
 284                return err_link(ap);    /* hfs disks don't support hard links */
 285        }
 286        if (VTOHFS(vp)->hfs_privdir_desc.cd_cnid == 0) {
 287                return err_link(ap);    /* no private metadata dir, no links possible */
 288        }
 289        if (vnode_mount(tdvp) != vnode_mount(vp)) {
 290                return (EXDEV);
 291        }
 292        if ((error = hfs_lockpair(VTOC(tdvp), VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
 293                return (error);
 294        }
 295        tdcp = VTOC(tdvp);
 296        cp = VTOC(vp);
 297        hfsmp = VTOHFS(vp);
 298
 299        if (cp->c_nlink >= HFS_LINK_MAX) {
 300                error = EMLINK;
 301                goto out;
 302        }
 303        if (cp->c_flags & (IMMUTABLE | APPEND)) {
 304                error = EPERM;
 305                goto out;
 306        }
 307        if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
 308            error = ENOENT;
 309            goto out;
 310        }
 311        
 312        v_type = vnode_vtype(vp);
 313        if (v_type == VBLK || v_type == VCHR) {
 314                error = EINVAL;  /* cannot link to a special file */
 315                goto out;
 316        }
 317
 318        if (hfs_start_transaction(hfsmp) != 0) {
 319            error = EINVAL;  /* cannot link to a special file */
 320            goto out;
 321        }
 322
 323        cp->c_nlink++;
 324        cp->c_touch_chgtime = TRUE;
 325
 326        error = hfs_makelink(hfsmp, cp, tdcp, cnp);
 327        if (error) {
 328                cp->c_nlink--;
 329                hfs_volupdate(hfsmp, VOL_UPDATE, 0);
 330        } else {
 331                /* Invalidate negative cache entries in the destination directory */
 332                if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
 333                        cache_purge_negatives(tdvp);
 334
 335                /* Update the target directory and volume stats */
 336                tdcp->c_nlink++;
 337                tdcp->c_entries++;
 338                tdcp->c_touch_chgtime = TRUE;
 339                tdcp->c_touch_modtime = TRUE;
 340                tdcp->c_flag |= C_FORCEUPDATE;
 341
 342                error = hfs_update(tdvp, 0);
 343                if (error) {
 344                    panic("hfs_vnop_link: error updating tdvp 0x%x\n", tdvp);
 345                }
 346
 347                hfs_volupdate(hfsmp, VOL_MKFILE,
 348                        (tdcp->c_cnid == kHFSRootFolderID));
 349        }
 350
 351        cp->c_flag |= C_FORCEUPDATE;    // otherwise hfs_update() might skip the update
 352
 353        if ((ret = hfs_update(vp, TRUE)) != 0) {
 354            panic("hfs_vnop_link: error %d updating vp @ 0x%x\n", ret, vp);
 355        }
 356
 357        hfs_end_transaction(hfsmp);
 358        
 359        HFS_KNOTE(vp, NOTE_LINK);
 360        HFS_KNOTE(tdvp, NOTE_WRITE);
 361out:
 362        hfs_unlockpair(tdcp, cp);
 363        return (error);
 364}
 365
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.