Dave Chinner | 95920cd | 2013-04-03 16:11:27 +1100 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (c) 2000-2005 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_trans.h" |
| 24 | #include "xfs_sb.h" |
| 25 | #include "xfs_ag.h" |
| 26 | #include "xfs_mount.h" |
| 27 | #include "xfs_error.h" |
| 28 | #include "xfs_da_btree.h" |
| 29 | #include "xfs_bmap_btree.h" |
| 30 | #include "xfs_dinode.h" |
| 31 | #include "xfs_inode.h" |
| 32 | #include "xfs_alloc.h" |
| 33 | #include "xfs_inode_item.h" |
| 34 | #include "xfs_bmap.h" |
| 35 | #include "xfs_attr.h" |
| 36 | #include "xfs_attr_leaf.h" |
| 37 | #include "xfs_attr_remote.h" |
| 38 | #include "xfs_trans_space.h" |
| 39 | #include "xfs_trace.h" |
| 40 | |
| 41 | |
| 42 | #define ATTR_RMTVALUE_MAPSIZE 1 /* # of map entries at once */ |
| 43 | |
| 44 | /* |
| 45 | * Read the value associated with an attribute from the out-of-line buffer |
| 46 | * that we stored it in. |
| 47 | */ |
| 48 | int |
| 49 | xfs_attr_rmtval_get(xfs_da_args_t *args) |
| 50 | { |
| 51 | xfs_bmbt_irec_t map[ATTR_RMTVALUE_MAPSIZE]; |
| 52 | xfs_mount_t *mp; |
| 53 | xfs_daddr_t dblkno; |
| 54 | void *dst; |
| 55 | xfs_buf_t *bp; |
| 56 | int nmap, error, tmp, valuelen, blkcnt, i; |
| 57 | xfs_dablk_t lblkno; |
| 58 | |
| 59 | trace_xfs_attr_rmtval_get(args); |
| 60 | |
| 61 | ASSERT(!(args->flags & ATTR_KERNOVAL)); |
| 62 | |
| 63 | mp = args->dp->i_mount; |
| 64 | dst = args->value; |
| 65 | valuelen = args->valuelen; |
| 66 | lblkno = args->rmtblkno; |
| 67 | while (valuelen > 0) { |
| 68 | nmap = ATTR_RMTVALUE_MAPSIZE; |
| 69 | error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno, |
| 70 | args->rmtblkcnt, map, &nmap, |
| 71 | XFS_BMAPI_ATTRFORK); |
| 72 | if (error) |
| 73 | return(error); |
| 74 | ASSERT(nmap >= 1); |
| 75 | |
| 76 | for (i = 0; (i < nmap) && (valuelen > 0); i++) { |
| 77 | ASSERT((map[i].br_startblock != DELAYSTARTBLOCK) && |
| 78 | (map[i].br_startblock != HOLESTARTBLOCK)); |
| 79 | dblkno = XFS_FSB_TO_DADDR(mp, map[i].br_startblock); |
| 80 | blkcnt = XFS_FSB_TO_BB(mp, map[i].br_blockcount); |
| 81 | error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, |
| 82 | dblkno, blkcnt, 0, &bp, NULL); |
| 83 | if (error) |
| 84 | return(error); |
| 85 | |
| 86 | tmp = min_t(int, valuelen, BBTOB(bp->b_length)); |
| 87 | xfs_buf_iomove(bp, 0, tmp, dst, XBRW_READ); |
| 88 | xfs_buf_relse(bp); |
| 89 | dst += tmp; |
| 90 | valuelen -= tmp; |
| 91 | |
| 92 | lblkno += map[i].br_blockcount; |
| 93 | } |
| 94 | } |
| 95 | ASSERT(valuelen == 0); |
| 96 | return(0); |
| 97 | } |
| 98 | |
| 99 | /* |
| 100 | * Write the value associated with an attribute into the out-of-line buffer |
| 101 | * that we have defined for it. |
| 102 | */ |
| 103 | int |
| 104 | xfs_attr_rmtval_set(xfs_da_args_t *args) |
| 105 | { |
| 106 | xfs_mount_t *mp; |
| 107 | xfs_fileoff_t lfileoff; |
| 108 | xfs_inode_t *dp; |
| 109 | xfs_bmbt_irec_t map; |
| 110 | xfs_daddr_t dblkno; |
| 111 | void *src; |
| 112 | xfs_buf_t *bp; |
| 113 | xfs_dablk_t lblkno; |
| 114 | int blkcnt, valuelen, nmap, error, tmp, committed; |
| 115 | |
| 116 | trace_xfs_attr_rmtval_set(args); |
| 117 | |
| 118 | dp = args->dp; |
| 119 | mp = dp->i_mount; |
| 120 | src = args->value; |
| 121 | |
| 122 | /* |
| 123 | * Find a "hole" in the attribute address space large enough for |
| 124 | * us to drop the new attribute's value into. |
| 125 | */ |
| 126 | blkcnt = XFS_B_TO_FSB(mp, args->valuelen); |
| 127 | lfileoff = 0; |
| 128 | error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff, |
| 129 | XFS_ATTR_FORK); |
| 130 | if (error) { |
| 131 | return(error); |
| 132 | } |
| 133 | args->rmtblkno = lblkno = (xfs_dablk_t)lfileoff; |
| 134 | args->rmtblkcnt = blkcnt; |
| 135 | |
| 136 | /* |
| 137 | * Roll through the "value", allocating blocks on disk as required. |
| 138 | */ |
| 139 | while (blkcnt > 0) { |
| 140 | /* |
| 141 | * Allocate a single extent, up to the size of the value. |
| 142 | */ |
| 143 | xfs_bmap_init(args->flist, args->firstblock); |
| 144 | nmap = 1; |
| 145 | error = xfs_bmapi_write(args->trans, dp, (xfs_fileoff_t)lblkno, |
| 146 | blkcnt, |
| 147 | XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA, |
| 148 | args->firstblock, args->total, &map, &nmap, |
| 149 | args->flist); |
| 150 | if (!error) { |
| 151 | error = xfs_bmap_finish(&args->trans, args->flist, |
| 152 | &committed); |
| 153 | } |
| 154 | if (error) { |
| 155 | ASSERT(committed); |
| 156 | args->trans = NULL; |
| 157 | xfs_bmap_cancel(args->flist); |
| 158 | return(error); |
| 159 | } |
| 160 | |
| 161 | /* |
| 162 | * bmap_finish() may have committed the last trans and started |
| 163 | * a new one. We need the inode to be in all transactions. |
| 164 | */ |
| 165 | if (committed) |
| 166 | xfs_trans_ijoin(args->trans, dp, 0); |
| 167 | |
| 168 | ASSERT(nmap == 1); |
| 169 | ASSERT((map.br_startblock != DELAYSTARTBLOCK) && |
| 170 | (map.br_startblock != HOLESTARTBLOCK)); |
| 171 | lblkno += map.br_blockcount; |
| 172 | blkcnt -= map.br_blockcount; |
| 173 | |
| 174 | /* |
| 175 | * Start the next trans in the chain. |
| 176 | */ |
| 177 | error = xfs_trans_roll(&args->trans, dp); |
| 178 | if (error) |
| 179 | return (error); |
| 180 | } |
| 181 | |
| 182 | /* |
| 183 | * Roll through the "value", copying the attribute value to the |
| 184 | * already-allocated blocks. Blocks are written synchronously |
| 185 | * so that we can know they are all on disk before we turn off |
| 186 | * the INCOMPLETE flag. |
| 187 | */ |
| 188 | lblkno = args->rmtblkno; |
| 189 | valuelen = args->valuelen; |
| 190 | while (valuelen > 0) { |
| 191 | int buflen; |
| 192 | |
| 193 | /* |
| 194 | * Try to remember where we decided to put the value. |
| 195 | */ |
| 196 | xfs_bmap_init(args->flist, args->firstblock); |
| 197 | nmap = 1; |
| 198 | error = xfs_bmapi_read(dp, (xfs_fileoff_t)lblkno, |
| 199 | args->rmtblkcnt, &map, &nmap, |
| 200 | XFS_BMAPI_ATTRFORK); |
| 201 | if (error) |
| 202 | return(error); |
| 203 | ASSERT(nmap == 1); |
| 204 | ASSERT((map.br_startblock != DELAYSTARTBLOCK) && |
| 205 | (map.br_startblock != HOLESTARTBLOCK)); |
| 206 | |
| 207 | dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock), |
| 208 | blkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount); |
| 209 | |
| 210 | bp = xfs_buf_get(mp->m_ddev_targp, dblkno, blkcnt, 0); |
| 211 | if (!bp) |
| 212 | return ENOMEM; |
| 213 | |
| 214 | buflen = BBTOB(bp->b_length); |
| 215 | tmp = min_t(int, valuelen, buflen); |
| 216 | xfs_buf_iomove(bp, 0, tmp, src, XBRW_WRITE); |
| 217 | if (tmp < buflen) |
| 218 | xfs_buf_zero(bp, tmp, buflen - tmp); |
| 219 | |
| 220 | error = xfs_bwrite(bp); /* GROT: NOTE: synchronous write */ |
| 221 | xfs_buf_relse(bp); |
| 222 | if (error) |
| 223 | return error; |
| 224 | src += tmp; |
| 225 | valuelen -= tmp; |
| 226 | |
| 227 | lblkno += map.br_blockcount; |
| 228 | } |
| 229 | ASSERT(valuelen == 0); |
| 230 | return(0); |
| 231 | } |
| 232 | |
| 233 | /* |
| 234 | * Remove the value associated with an attribute by deleting the |
| 235 | * out-of-line buffer that it is stored on. |
| 236 | */ |
| 237 | int |
| 238 | xfs_attr_rmtval_remove(xfs_da_args_t *args) |
| 239 | { |
| 240 | xfs_mount_t *mp; |
| 241 | xfs_bmbt_irec_t map; |
| 242 | xfs_buf_t *bp; |
| 243 | xfs_daddr_t dblkno; |
| 244 | xfs_dablk_t lblkno; |
| 245 | int valuelen, blkcnt, nmap, error, done, committed; |
| 246 | |
| 247 | trace_xfs_attr_rmtval_remove(args); |
| 248 | |
| 249 | mp = args->dp->i_mount; |
| 250 | |
| 251 | /* |
| 252 | * Roll through the "value", invalidating the attribute value's |
| 253 | * blocks. |
| 254 | */ |
| 255 | lblkno = args->rmtblkno; |
| 256 | valuelen = args->rmtblkcnt; |
| 257 | while (valuelen > 0) { |
| 258 | /* |
| 259 | * Try to remember where we decided to put the value. |
| 260 | */ |
| 261 | nmap = 1; |
| 262 | error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno, |
| 263 | args->rmtblkcnt, &map, &nmap, |
| 264 | XFS_BMAPI_ATTRFORK); |
| 265 | if (error) |
| 266 | return(error); |
| 267 | ASSERT(nmap == 1); |
| 268 | ASSERT((map.br_startblock != DELAYSTARTBLOCK) && |
| 269 | (map.br_startblock != HOLESTARTBLOCK)); |
| 270 | |
| 271 | dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock), |
| 272 | blkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount); |
| 273 | |
| 274 | /* |
| 275 | * If the "remote" value is in the cache, remove it. |
| 276 | */ |
| 277 | bp = xfs_incore(mp->m_ddev_targp, dblkno, blkcnt, XBF_TRYLOCK); |
| 278 | if (bp) { |
| 279 | xfs_buf_stale(bp); |
| 280 | xfs_buf_relse(bp); |
| 281 | bp = NULL; |
| 282 | } |
| 283 | |
| 284 | valuelen -= map.br_blockcount; |
| 285 | |
| 286 | lblkno += map.br_blockcount; |
| 287 | } |
| 288 | |
| 289 | /* |
| 290 | * Keep de-allocating extents until the remote-value region is gone. |
| 291 | */ |
| 292 | lblkno = args->rmtblkno; |
| 293 | blkcnt = args->rmtblkcnt; |
| 294 | done = 0; |
| 295 | while (!done) { |
| 296 | xfs_bmap_init(args->flist, args->firstblock); |
| 297 | error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt, |
| 298 | XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA, |
| 299 | 1, args->firstblock, args->flist, |
| 300 | &done); |
| 301 | if (!error) { |
| 302 | error = xfs_bmap_finish(&args->trans, args->flist, |
| 303 | &committed); |
| 304 | } |
| 305 | if (error) { |
| 306 | ASSERT(committed); |
| 307 | args->trans = NULL; |
| 308 | xfs_bmap_cancel(args->flist); |
| 309 | return(error); |
| 310 | } |
| 311 | |
| 312 | /* |
| 313 | * bmap_finish() may have committed the last trans and started |
| 314 | * a new one. We need the inode to be in all transactions. |
| 315 | */ |
| 316 | if (committed) |
| 317 | xfs_trans_ijoin(args->trans, args->dp, 0); |
| 318 | |
| 319 | /* |
| 320 | * Close out trans and start the next one in the chain. |
| 321 | */ |
| 322 | error = xfs_trans_roll(&args->trans, args->dp); |
| 323 | if (error) |
| 324 | return (error); |
| 325 | } |
| 326 | return(0); |
| 327 | } |
| 328 | |