]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - fs/xfs/xfs_attr_remote.c
xfs: split remote attribute code out
[mirror_ubuntu-bionic-kernel.git] / fs / xfs / xfs_attr_remote.c
CommitLineData
95920cd6
DC
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 */
48int
49xfs_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 */
103int
104xfs_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 */
237int
238xfs_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