]>
Commit | Line | Data |
---|---|---|
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 | */ | |
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 |