diff options
Diffstat (limited to 'fs/xfs/xfs_attr_leaf.c')
-rw-r--r-- | fs/xfs/xfs_attr_leaf.c | 177 |
1 files changed, 152 insertions, 25 deletions
diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c index e13eaa521436..50598b121683 100644 --- a/fs/xfs/xfs_attr_leaf.c +++ b/fs/xfs/xfs_attr_leaf.c | |||
@@ -118,13 +118,82 @@ STATIC int xfs_attr_put_listent(xfs_attr_list_context_t *context, | |||
118 | 118 | ||
119 | 119 | ||
120 | /*======================================================================== | 120 | /*======================================================================== |
121 | * External routines when dirsize < XFS_LITINO(mp). | 121 | * External routines when attribute fork size < XFS_LITINO(mp). |
122 | *========================================================================*/ | 122 | *========================================================================*/ |
123 | 123 | ||
124 | /* | 124 | /* |
125 | * Create the initial contents of a shortform attribute list. | 125 | * Query whether the requested number of additional bytes of extended |
126 | * attribute space will be able to fit inline. | ||
127 | * Returns zero if not, else the di_forkoff fork offset to be used in the | ||
128 | * literal area for attribute data once the new bytes have been added. | ||
129 | * | ||
130 | * di_forkoff must be 8 byte aligned, hence is stored as a >>3 value; | ||
131 | * special case for dev/uuid inodes, they have fixed size data forks. | ||
126 | */ | 132 | */ |
127 | int | 133 | int |
134 | xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes) | ||
135 | { | ||
136 | int offset; | ||
137 | int minforkoff; /* lower limit on valid forkoff locations */ | ||
138 | int maxforkoff; /* upper limit on valid forkoff locations */ | ||
139 | xfs_mount_t *mp = dp->i_mount; | ||
140 | |||
141 | if (unlikely(mp->m_flags & XFS_MOUNT_COMPAT_ATTR)) { | ||
142 | if (bytes <= XFS_IFORK_ASIZE(dp)) | ||
143 | return mp->m_attroffset >> 3; | ||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | offset = (XFS_LITINO(mp) - bytes) >> 3; /* rounded down */ | ||
148 | |||
149 | switch (dp->i_d.di_format) { | ||
150 | case XFS_DINODE_FMT_DEV: | ||
151 | minforkoff = roundup(sizeof(xfs_dev_t), 8) >> 3; | ||
152 | return (offset >= minforkoff) ? minforkoff : 0; | ||
153 | case XFS_DINODE_FMT_UUID: | ||
154 | minforkoff = roundup(sizeof(uuid_t), 8) >> 3; | ||
155 | return (offset >= minforkoff) ? minforkoff : 0; | ||
156 | } | ||
157 | |||
158 | /* data fork btree root can have at least this many key/ptr pairs */ | ||
159 | minforkoff = MAX(dp->i_df.if_bytes, XFS_BMDR_SPACE_CALC(MINDBTPTRS)); | ||
160 | minforkoff = roundup(minforkoff, 8) >> 3; | ||
161 | |||
162 | /* attr fork btree root can have at least this many key/ptr pairs */ | ||
163 | maxforkoff = XFS_LITINO(mp) - XFS_BMDR_SPACE_CALC(MINABTPTRS); | ||
164 | maxforkoff = maxforkoff >> 3; /* rounded down */ | ||
165 | |||
166 | if (offset >= minforkoff && offset < maxforkoff) | ||
167 | return offset; | ||
168 | if (offset >= maxforkoff) | ||
169 | return maxforkoff; | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | /* | ||
174 | * Switch on the ATTR2 superblock bit (implies also FEATURES2) | ||
175 | */ | ||
176 | STATIC void | ||
177 | xfs_sbversion_add_attr2(xfs_mount_t *mp, xfs_trans_t *tp) | ||
178 | { | ||
179 | unsigned long s; | ||
180 | |||
181 | if (!(mp->m_flags & XFS_MOUNT_COMPAT_ATTR) && | ||
182 | !(XFS_SB_VERSION_HASATTR2(&mp->m_sb))) { | ||
183 | s = XFS_SB_LOCK(mp); | ||
184 | if (!XFS_SB_VERSION_HASATTR2(&mp->m_sb)) { | ||
185 | XFS_SB_VERSION_ADDATTR2(&mp->m_sb); | ||
186 | XFS_SB_UNLOCK(mp, s); | ||
187 | xfs_mod_sb(tp, XFS_SB_VERSIONNUM | XFS_SB_FEATURES2); | ||
188 | } else | ||
189 | XFS_SB_UNLOCK(mp, s); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | /* | ||
194 | * Create the initial contents of a shortform attribute list. | ||
195 | */ | ||
196 | void | ||
128 | xfs_attr_shortform_create(xfs_da_args_t *args) | 197 | xfs_attr_shortform_create(xfs_da_args_t *args) |
129 | { | 198 | { |
130 | xfs_attr_sf_hdr_t *hdr; | 199 | xfs_attr_sf_hdr_t *hdr; |
@@ -148,29 +217,37 @@ xfs_attr_shortform_create(xfs_da_args_t *args) | |||
148 | hdr->count = 0; | 217 | hdr->count = 0; |
149 | INT_SET(hdr->totsize, ARCH_CONVERT, sizeof(*hdr)); | 218 | INT_SET(hdr->totsize, ARCH_CONVERT, sizeof(*hdr)); |
150 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); | 219 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); |
151 | return(0); | ||
152 | } | 220 | } |
153 | 221 | ||
154 | /* | 222 | /* |
155 | * Add a name/value pair to the shortform attribute list. | 223 | * Add a name/value pair to the shortform attribute list. |
156 | * Overflow from the inode has already been checked for. | 224 | * Overflow from the inode has already been checked for. |
157 | */ | 225 | */ |
158 | int | 226 | void |
159 | xfs_attr_shortform_add(xfs_da_args_t *args) | 227 | xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff) |
160 | { | 228 | { |
161 | xfs_attr_shortform_t *sf; | 229 | xfs_attr_shortform_t *sf; |
162 | xfs_attr_sf_entry_t *sfe; | 230 | xfs_attr_sf_entry_t *sfe; |
163 | int i, offset, size; | 231 | int i, offset, size; |
232 | xfs_mount_t *mp; | ||
164 | xfs_inode_t *dp; | 233 | xfs_inode_t *dp; |
165 | xfs_ifork_t *ifp; | 234 | xfs_ifork_t *ifp; |
166 | 235 | ||
167 | dp = args->dp; | 236 | dp = args->dp; |
237 | mp = dp->i_mount; | ||
238 | dp->i_d.di_forkoff = forkoff; | ||
239 | dp->i_df.if_ext_max = | ||
240 | XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t); | ||
241 | dp->i_afp->if_ext_max = | ||
242 | XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t); | ||
243 | |||
168 | ifp = dp->i_afp; | 244 | ifp = dp->i_afp; |
169 | ASSERT(ifp->if_flags & XFS_IFINLINE); | 245 | ASSERT(ifp->if_flags & XFS_IFINLINE); |
170 | sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data; | 246 | sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data; |
171 | sfe = &sf->list[0]; | 247 | sfe = &sf->list[0]; |
172 | for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT); | 248 | for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT); |
173 | sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { | 249 | sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { |
250 | #ifdef DEBUG | ||
174 | if (sfe->namelen != args->namelen) | 251 | if (sfe->namelen != args->namelen) |
175 | continue; | 252 | continue; |
176 | if (memcmp(args->name, sfe->nameval, args->namelen) != 0) | 253 | if (memcmp(args->name, sfe->nameval, args->namelen) != 0) |
@@ -181,7 +258,8 @@ xfs_attr_shortform_add(xfs_da_args_t *args) | |||
181 | if (((args->flags & ATTR_ROOT) != 0) != | 258 | if (((args->flags & ATTR_ROOT) != 0) != |
182 | ((sfe->flags & XFS_ATTR_ROOT) != 0)) | 259 | ((sfe->flags & XFS_ATTR_ROOT) != 0)) |
183 | continue; | 260 | continue; |
184 | return(XFS_ERROR(EEXIST)); | 261 | ASSERT(0); |
262 | #endif | ||
185 | } | 263 | } |
186 | 264 | ||
187 | offset = (char *)sfe - (char *)sf; | 265 | offset = (char *)sfe - (char *)sf; |
@@ -200,11 +278,11 @@ xfs_attr_shortform_add(xfs_da_args_t *args) | |||
200 | INT_MOD(sf->hdr.totsize, ARCH_CONVERT, size); | 278 | INT_MOD(sf->hdr.totsize, ARCH_CONVERT, size); |
201 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); | 279 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); |
202 | 280 | ||
203 | return(0); | 281 | xfs_sbversion_add_attr2(mp, args->trans); |
204 | } | 282 | } |
205 | 283 | ||
206 | /* | 284 | /* |
207 | * Remove a name from the shortform attribute list structure. | 285 | * Remove an attribute from the shortform attribute list structure. |
208 | */ | 286 | */ |
209 | int | 287 | int |
210 | xfs_attr_shortform_remove(xfs_da_args_t *args) | 288 | xfs_attr_shortform_remove(xfs_da_args_t *args) |
@@ -212,17 +290,16 @@ xfs_attr_shortform_remove(xfs_da_args_t *args) | |||
212 | xfs_attr_shortform_t *sf; | 290 | xfs_attr_shortform_t *sf; |
213 | xfs_attr_sf_entry_t *sfe; | 291 | xfs_attr_sf_entry_t *sfe; |
214 | int base, size=0, end, totsize, i; | 292 | int base, size=0, end, totsize, i; |
293 | xfs_mount_t *mp; | ||
215 | xfs_inode_t *dp; | 294 | xfs_inode_t *dp; |
216 | 295 | ||
217 | /* | ||
218 | * Remove the attribute. | ||
219 | */ | ||
220 | dp = args->dp; | 296 | dp = args->dp; |
297 | mp = dp->i_mount; | ||
221 | base = sizeof(xfs_attr_sf_hdr_t); | 298 | base = sizeof(xfs_attr_sf_hdr_t); |
222 | sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data; | 299 | sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data; |
223 | sfe = &sf->list[0]; | 300 | sfe = &sf->list[0]; |
224 | for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT); | 301 | end = INT_GET(sf->hdr.count, ARCH_CONVERT); |
225 | sfe = XFS_ATTR_SF_NEXTENTRY(sfe), | 302 | for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), |
226 | base += size, i++) { | 303 | base += size, i++) { |
227 | size = XFS_ATTR_SF_ENTSIZE(sfe); | 304 | size = XFS_ATTR_SF_ENTSIZE(sfe); |
228 | if (sfe->namelen != args->namelen) | 305 | if (sfe->namelen != args->namelen) |
@@ -237,19 +314,51 @@ xfs_attr_shortform_remove(xfs_da_args_t *args) | |||
237 | continue; | 314 | continue; |
238 | break; | 315 | break; |
239 | } | 316 | } |
240 | if (i == INT_GET(sf->hdr.count, ARCH_CONVERT)) | 317 | if (i == end) |
241 | return(XFS_ERROR(ENOATTR)); | 318 | return(XFS_ERROR(ENOATTR)); |
242 | 319 | ||
320 | /* | ||
321 | * Fix up the attribute fork data, covering the hole | ||
322 | */ | ||
243 | end = base + size; | 323 | end = base + size; |
244 | totsize = INT_GET(sf->hdr.totsize, ARCH_CONVERT); | 324 | totsize = INT_GET(sf->hdr.totsize, ARCH_CONVERT); |
245 | if (end != totsize) { | 325 | if (end != totsize) |
246 | memmove(&((char *)sf)[base], &((char *)sf)[end], | 326 | memmove(&((char *)sf)[base], &((char *)sf)[end], totsize - end); |
247 | totsize - end); | ||
248 | } | ||
249 | INT_MOD(sf->hdr.count, ARCH_CONVERT, -1); | 327 | INT_MOD(sf->hdr.count, ARCH_CONVERT, -1); |
250 | INT_MOD(sf->hdr.totsize, ARCH_CONVERT, -size); | 328 | INT_MOD(sf->hdr.totsize, ARCH_CONVERT, -size); |
251 | xfs_idata_realloc(dp, -size, XFS_ATTR_FORK); | 329 | |
252 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); | 330 | /* |
331 | * Fix up the start offset of the attribute fork | ||
332 | */ | ||
333 | totsize -= size; | ||
334 | if (totsize == sizeof(xfs_attr_sf_hdr_t) && !args->addname) { | ||
335 | /* | ||
336 | * Last attribute now removed, revert to original | ||
337 | * inode format making all literal area available | ||
338 | * to the data fork once more. | ||
339 | */ | ||
340 | xfs_idestroy_fork(dp, XFS_ATTR_FORK); | ||
341 | dp->i_d.di_forkoff = 0; | ||
342 | dp->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS; | ||
343 | ASSERT(dp->i_d.di_anextents == 0); | ||
344 | ASSERT(dp->i_afp == NULL); | ||
345 | dp->i_df.if_ext_max = | ||
346 | XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t); | ||
347 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE); | ||
348 | } else { | ||
349 | xfs_idata_realloc(dp, -size, XFS_ATTR_FORK); | ||
350 | dp->i_d.di_forkoff = xfs_attr_shortform_bytesfit(dp, totsize); | ||
351 | ASSERT(dp->i_d.di_forkoff); | ||
352 | ASSERT(totsize > sizeof(xfs_attr_sf_hdr_t) || args->addname); | ||
353 | dp->i_afp->if_ext_max = | ||
354 | XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t); | ||
355 | dp->i_df.if_ext_max = | ||
356 | XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t); | ||
357 | xfs_trans_log_inode(args->trans, dp, | ||
358 | XFS_ILOG_CORE | XFS_ILOG_ADATA); | ||
359 | } | ||
360 | |||
361 | xfs_sbversion_add_attr2(mp, args->trans); | ||
253 | 362 | ||
254 | return(0); | 363 | return(0); |
255 | } | 364 | } |
@@ -649,14 +758,16 @@ xfs_attr_shortform_allfit(xfs_dabuf_t *bp, xfs_inode_t *dp) | |||
649 | + name_loc->namelen | 758 | + name_loc->namelen |
650 | + INT_GET(name_loc->valuelen, ARCH_CONVERT); | 759 | + INT_GET(name_loc->valuelen, ARCH_CONVERT); |
651 | } | 760 | } |
652 | return( bytes < XFS_IFORK_ASIZE(dp) ); | 761 | if (bytes == sizeof(struct xfs_attr_sf_hdr)) |
762 | return(-1); | ||
763 | return(xfs_attr_shortform_bytesfit(dp, bytes)); | ||
653 | } | 764 | } |
654 | 765 | ||
655 | /* | 766 | /* |
656 | * Convert a leaf attribute list to shortform attribute list | 767 | * Convert a leaf attribute list to shortform attribute list |
657 | */ | 768 | */ |
658 | int | 769 | int |
659 | xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args) | 770 | xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args, int forkoff) |
660 | { | 771 | { |
661 | xfs_attr_leafblock_t *leaf; | 772 | xfs_attr_leafblock_t *leaf; |
662 | xfs_attr_leaf_entry_t *entry; | 773 | xfs_attr_leaf_entry_t *entry; |
@@ -683,9 +794,25 @@ xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args) | |||
683 | error = xfs_da_shrink_inode(args, 0, bp); | 794 | error = xfs_da_shrink_inode(args, 0, bp); |
684 | if (error) | 795 | if (error) |
685 | goto out; | 796 | goto out; |
686 | error = xfs_attr_shortform_create(args); | 797 | |
687 | if (error) | 798 | if (forkoff == -1) { |
799 | /* | ||
800 | * Last attribute was removed, revert to original | ||
801 | * inode format making all literal area available | ||
802 | * to the data fork once more. | ||
803 | */ | ||
804 | xfs_idestroy_fork(dp, XFS_ATTR_FORK); | ||
805 | dp->i_d.di_forkoff = 0; | ||
806 | dp->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS; | ||
807 | ASSERT(dp->i_d.di_anextents == 0); | ||
808 | ASSERT(dp->i_afp == NULL); | ||
809 | dp->i_df.if_ext_max = | ||
810 | XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t); | ||
811 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE); | ||
688 | goto out; | 812 | goto out; |
813 | } | ||
814 | |||
815 | xfs_attr_shortform_create(args); | ||
689 | 816 | ||
690 | /* | 817 | /* |
691 | * Copy the attributes | 818 | * Copy the attributes |
@@ -713,7 +840,7 @@ xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args) | |||
713 | nargs.hashval = INT_GET(entry->hashval, ARCH_CONVERT); | 840 | nargs.hashval = INT_GET(entry->hashval, ARCH_CONVERT); |
714 | nargs.flags = (entry->flags & XFS_ATTR_SECURE) ? ATTR_SECURE : | 841 | nargs.flags = (entry->flags & XFS_ATTR_SECURE) ? ATTR_SECURE : |
715 | ((entry->flags & XFS_ATTR_ROOT) ? ATTR_ROOT : 0); | 842 | ((entry->flags & XFS_ATTR_ROOT) ? ATTR_ROOT : 0); |
716 | xfs_attr_shortform_add(&nargs); | 843 | xfs_attr_shortform_add(&nargs, forkoff); |
717 | } | 844 | } |
718 | error = 0; | 845 | error = 0; |
719 | 846 | ||