linux/fs/xfs/xfs_dfrag.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2000-2006 Silicon Graphics, Inc.
   3 * All Rights Reserved.
   4 *
   5 * This program is free software; you can redistribute it and/or
   6 * modify it under the terms of the GNU General Public License as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it would be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, write the Free Software Foundation,
  16 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17 */
  18#include "xfs.h"
  19#include "xfs_fs.h"
  20#include "xfs_types.h"
  21#include "xfs_bit.h"
  22#include "xfs_log.h"
  23#include "xfs_inum.h"
  24#include "xfs_trans.h"
  25#include "xfs_sb.h"
  26#include "xfs_ag.h"
  27#include "xfs_dir2.h"
  28#include "xfs_dmapi.h"
  29#include "xfs_mount.h"
  30#include "xfs_bmap_btree.h"
  31#include "xfs_alloc_btree.h"
  32#include "xfs_ialloc_btree.h"
  33#include "xfs_dir2_sf.h"
  34#include "xfs_attr_sf.h"
  35#include "xfs_dinode.h"
  36#include "xfs_inode.h"
  37#include "xfs_inode_item.h"
  38#include "xfs_bmap.h"
  39#include "xfs_btree.h"
  40#include "xfs_ialloc.h"
  41#include "xfs_itable.h"
  42#include "xfs_dfrag.h"
  43#include "xfs_error.h"
  44#include "xfs_rw.h"
  45#include "xfs_vnodeops.h"
  46
  47/*
  48 * Syssgi interface for swapext
  49 */
  50int
  51xfs_swapext(
  52        xfs_swapext_t   __user *sxu)
  53{
  54        xfs_swapext_t   *sxp;
  55        xfs_inode_t     *ip, *tip;
  56        struct file     *file, *target_file;
  57        int             error = 0;
  58
  59        sxp = kmem_alloc(sizeof(xfs_swapext_t), KM_MAYFAIL);
  60        if (!sxp) {
  61                error = XFS_ERROR(ENOMEM);
  62                goto out;
  63        }
  64
  65        if (copy_from_user(sxp, sxu, sizeof(xfs_swapext_t))) {
  66                error = XFS_ERROR(EFAULT);
  67                goto out_free_sxp;
  68        }
  69
  70        /* Pull information for the target fd */
  71        file = fget((int)sxp->sx_fdtarget);
  72        if (!file) {
  73                error = XFS_ERROR(EINVAL);
  74                goto out_free_sxp;
  75        }
  76
  77        if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND)) {
  78                error = XFS_ERROR(EBADF);
  79                goto out_put_file;
  80        }
  81
  82        target_file = fget((int)sxp->sx_fdtmp);
  83        if (!target_file) {
  84                error = XFS_ERROR(EINVAL);
  85                goto out_put_file;
  86        }
  87
  88        if (!(target_file->f_mode & FMODE_WRITE) ||
  89            (target_file->f_flags & O_APPEND)) {
  90                error = XFS_ERROR(EBADF);
  91                goto out_put_target_file;
  92        }
  93
  94        ip = XFS_I(file->f_path.dentry->d_inode);
  95        tip = XFS_I(target_file->f_path.dentry->d_inode);
  96
  97        if (ip->i_mount != tip->i_mount) {
  98                error = XFS_ERROR(EINVAL);
  99                goto out_put_target_file;
 100        }
 101
 102        if (ip->i_ino == tip->i_ino) {
 103                error = XFS_ERROR(EINVAL);
 104                goto out_put_target_file;
 105        }
 106
 107        if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
 108                error = XFS_ERROR(EIO);
 109                goto out_put_target_file;
 110        }
 111
 112        error = xfs_swap_extents(ip, tip, sxp);
 113
 114 out_put_target_file:
 115        fput(target_file);
 116 out_put_file:
 117        fput(file);
 118 out_free_sxp:
 119        kmem_free(sxp);
 120 out:
 121        return error;
 122}
 123
 124int
 125xfs_swap_extents(
 126        xfs_inode_t     *ip,
 127        xfs_inode_t     *tip,
 128        xfs_swapext_t   *sxp)
 129{
 130        xfs_mount_t     *mp;
 131        xfs_trans_t     *tp;
 132        xfs_bstat_t     *sbp = &sxp->sx_stat;
 133        xfs_ifork_t     *tempifp, *ifp, *tifp;
 134        int             ilf_fields, tilf_fields;
 135        static uint     lock_flags = XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL;
 136        int             error = 0;
 137        int             aforkblks = 0;
 138        int             taforkblks = 0;
 139        __uint64_t      tmp;
 140        char            locked = 0;
 141
 142        mp = ip->i_mount;
 143
 144        tempifp = kmem_alloc(sizeof(xfs_ifork_t), KM_MAYFAIL);
 145        if (!tempifp) {
 146                error = XFS_ERROR(ENOMEM);
 147                goto error0;
 148        }
 149
 150        sbp = &sxp->sx_stat;
 151
 152        /*
 153         * we have to do two separate lock calls here to keep lockdep
 154         * happy. If we try to get all the locks in one call, lock will
 155         * report false positives when we drop the ILOCK and regain them
 156         * below.
 157         */
 158        xfs_lock_two_inodes(ip, tip, XFS_IOLOCK_EXCL);
 159        xfs_lock_two_inodes(ip, tip, XFS_ILOCK_EXCL);
 160        locked = 1;
 161
 162        /* Verify that both files have the same format */
 163        if ((ip->i_d.di_mode & S_IFMT) != (tip->i_d.di_mode & S_IFMT)) {
 164                error = XFS_ERROR(EINVAL);
 165                goto error0;
 166        }
 167
 168        /* Verify both files are either real-time or non-realtime */
 169        if (XFS_IS_REALTIME_INODE(ip) != XFS_IS_REALTIME_INODE(tip)) {
 170                error = XFS_ERROR(EINVAL);
 171                goto error0;
 172        }
 173
 174        /* Should never get a local format */
 175        if (ip->i_d.di_format == XFS_DINODE_FMT_LOCAL ||
 176            tip->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
 177                error = XFS_ERROR(EINVAL);
 178                goto error0;
 179        }
 180
 181        if (VN_CACHED(VFS_I(tip)) != 0) {
 182                xfs_inval_cached_trace(tip, 0, -1, 0, -1);
 183                error = xfs_flushinval_pages(tip, 0, -1,
 184                                FI_REMAPF_LOCKED);
 185                if (error)
 186                        goto error0;
 187        }
 188
 189        /* Verify O_DIRECT for ftmp */
 190        if (VN_CACHED(VFS_I(tip)) != 0) {
 191                error = XFS_ERROR(EINVAL);
 192                goto error0;
 193        }
 194
 195        /* Verify all data are being swapped */
 196        if (sxp->sx_offset != 0 ||
 197            sxp->sx_length != ip->i_d.di_size ||
 198            sxp->sx_length != tip->i_d.di_size) {
 199                error = XFS_ERROR(EFAULT);
 200                goto error0;
 201        }
 202
 203        /*
 204         * If the target has extended attributes, the tmp file
 205         * must also in order to ensure the correct data fork
 206         * format.
 207         */
 208        if ( XFS_IFORK_Q(ip) != XFS_IFORK_Q(tip) ) {
 209                error = XFS_ERROR(EINVAL);
 210                goto error0;
 211        }
 212
 213        /*
 214         * Compare the current change & modify times with that
 215         * passed in.  If they differ, we abort this swap.
 216         * This is the mechanism used to ensure the calling
 217         * process that the file was not changed out from
 218         * under it.
 219         */
 220        if ((sbp->bs_ctime.tv_sec != ip->i_d.di_ctime.t_sec) ||
 221            (sbp->bs_ctime.tv_nsec != ip->i_d.di_ctime.t_nsec) ||
 222            (sbp->bs_mtime.tv_sec != ip->i_d.di_mtime.t_sec) ||
 223            (sbp->bs_mtime.tv_nsec != ip->i_d.di_mtime.t_nsec)) {
 224                error = XFS_ERROR(EBUSY);
 225                goto error0;
 226        }
 227
 228        /* We need to fail if the file is memory mapped.  Once we have tossed
 229         * all existing pages, the page fault will have no option
 230         * but to go to the filesystem for pages. By making the page fault call
 231         * vop_read (or write in the case of autogrow) they block on the iolock
 232         * until we have switched the extents.
 233         */
 234        if (VN_MAPPED(VFS_I(ip))) {
 235                error = XFS_ERROR(EBUSY);
 236                goto error0;
 237        }
 238
 239        xfs_iunlock(ip, XFS_ILOCK_EXCL);
 240        xfs_iunlock(tip, XFS_ILOCK_EXCL);
 241
 242        /*
 243         * There is a race condition here since we gave up the
 244         * ilock.  However, the data fork will not change since
 245         * we have the iolock (locked for truncation too) so we
 246         * are safe.  We don't really care if non-io related
 247         * fields change.
 248         */
 249
 250        xfs_tosspages(ip, 0, -1, FI_REMAPF);
 251
 252        tp = xfs_trans_alloc(mp, XFS_TRANS_SWAPEXT);
 253        if ((error = xfs_trans_reserve(tp, 0,
 254                                     XFS_ICHANGE_LOG_RES(mp), 0,
 255                                     0, 0))) {
 256                xfs_iunlock(ip,  XFS_IOLOCK_EXCL);
 257                xfs_iunlock(tip, XFS_IOLOCK_EXCL);
 258                xfs_trans_cancel(tp, 0);
 259                locked = 0;
 260                goto error0;
 261        }
 262        xfs_lock_two_inodes(ip, tip, XFS_ILOCK_EXCL);
 263
 264        /*
 265         * Count the number of extended attribute blocks
 266         */
 267        if ( ((XFS_IFORK_Q(ip) != 0) && (ip->i_d.di_anextents > 0)) &&
 268             (ip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) {
 269                error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK, &aforkblks);
 270                if (error) {
 271                        xfs_trans_cancel(tp, 0);
 272                        goto error0;
 273                }
 274        }
 275        if ( ((XFS_IFORK_Q(tip) != 0) && (tip->i_d.di_anextents > 0)) &&
 276             (tip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) {
 277                error = xfs_bmap_count_blocks(tp, tip, XFS_ATTR_FORK,
 278                        &taforkblks);
 279                if (error) {
 280                        xfs_trans_cancel(tp, 0);
 281                        goto error0;
 282                }
 283        }
 284
 285        /*
 286         * Swap the data forks of the inodes
 287         */
 288        ifp = &ip->i_df;
 289        tifp = &tip->i_df;
 290        *tempifp = *ifp;        /* struct copy */
 291        *ifp = *tifp;           /* struct copy */
 292        *tifp = *tempifp;       /* struct copy */
 293
 294        /*
 295         * Fix the on-disk inode values
 296         */
 297        tmp = (__uint64_t)ip->i_d.di_nblocks;
 298        ip->i_d.di_nblocks = tip->i_d.di_nblocks - taforkblks + aforkblks;
 299        tip->i_d.di_nblocks = tmp + taforkblks - aforkblks;
 300
 301        tmp = (__uint64_t) ip->i_d.di_nextents;
 302        ip->i_d.di_nextents = tip->i_d.di_nextents;
 303        tip->i_d.di_nextents = tmp;
 304
 305        tmp = (__uint64_t) ip->i_d.di_format;
 306        ip->i_d.di_format = tip->i_d.di_format;
 307        tip->i_d.di_format = tmp;
 308
 309        ilf_fields = XFS_ILOG_CORE;
 310
 311        switch(ip->i_d.di_format) {
 312        case XFS_DINODE_FMT_EXTENTS:
 313                /* If the extents fit in the inode, fix the
 314                 * pointer.  Otherwise it's already NULL or
 315                 * pointing to the extent.
 316                 */
 317                if (ip->i_d.di_nextents <= XFS_INLINE_EXTS) {
 318                        ifp->if_u1.if_extents =
 319                                ifp->if_u2.if_inline_ext;
 320                }
 321                ilf_fields |= XFS_ILOG_DEXT;
 322                break;
 323        case XFS_DINODE_FMT_BTREE:
 324                ilf_fields |= XFS_ILOG_DBROOT;
 325                break;
 326        }
 327
 328        tilf_fields = XFS_ILOG_CORE;
 329
 330        switch(tip->i_d.di_format) {
 331        case XFS_DINODE_FMT_EXTENTS:
 332                /* If the extents fit in the inode, fix the
 333                 * pointer.  Otherwise it's already NULL or
 334                 * pointing to the extent.
 335                 */
 336                if (tip->i_d.di_nextents <= XFS_INLINE_EXTS) {
 337                        tifp->if_u1.if_extents =
 338                                tifp->if_u2.if_inline_ext;
 339                }
 340                tilf_fields |= XFS_ILOG_DEXT;
 341                break;
 342        case XFS_DINODE_FMT_BTREE:
 343                tilf_fields |= XFS_ILOG_DBROOT;
 344                break;
 345        }
 346
 347
 348        IHOLD(ip);
 349        xfs_trans_ijoin(tp, ip, lock_flags);
 350
 351        IHOLD(tip);
 352        xfs_trans_ijoin(tp, tip, lock_flags);
 353
 354        xfs_trans_log_inode(tp, ip,  ilf_fields);
 355        xfs_trans_log_inode(tp, tip, tilf_fields);
 356
 357        /*
 358         * If this is a synchronous mount, make sure that the
 359         * transaction goes to disk before returning to the user.
 360         */
 361        if (mp->m_flags & XFS_MOUNT_WSYNC) {
 362                xfs_trans_set_sync(tp);
 363        }
 364
 365        error = xfs_trans_commit(tp, XFS_TRANS_SWAPEXT);
 366        locked = 0;
 367
 368 error0:
 369        if (locked) {
 370                xfs_iunlock(ip,  lock_flags);
 371                xfs_iunlock(tip, lock_flags);
 372        }
 373        if (tempifp != NULL)
 374                kmem_free(tempifp);
 375        return error;
 376}
 377
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.