David Teigland | b3b94fa | 2006-01-16 16:50:04 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. |
| 3 | * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. |
| 4 | * |
| 5 | * This copyrighted material is made available to anyone wishing to use, |
| 6 | * modify, copy, or redistribute it subject to the terms and conditions |
| 7 | * of the GNU General Public License v.2. |
| 8 | */ |
| 9 | |
| 10 | #include <linux/sched.h> |
| 11 | #include <linux/slab.h> |
| 12 | #include <linux/spinlock.h> |
| 13 | #include <linux/completion.h> |
| 14 | #include <linux/buffer_head.h> |
| 15 | #include <asm/semaphore.h> |
| 16 | |
| 17 | #include "gfs2.h" |
| 18 | #include "bmap.h" |
| 19 | #include "dir.h" |
| 20 | #include "glock.h" |
| 21 | #include "inode.h" |
| 22 | #include "jdata.h" |
| 23 | #include "meta_io.h" |
| 24 | #include "quota.h" |
| 25 | #include "resize.h" |
| 26 | #include "rgrp.h" |
| 27 | #include "super.h" |
| 28 | #include "trans.h" |
| 29 | |
| 30 | /* A single transaction needs to add the structs to rindex and make the |
| 31 | statfs change. */ |
| 32 | |
| 33 | int gfs2_resize_add_rgrps(struct gfs2_sbd *sdp, char __user *buf, |
| 34 | unsigned int size) |
| 35 | { |
| 36 | unsigned int num = size / sizeof(struct gfs2_rindex); |
| 37 | struct gfs2_inode *ip = sdp->sd_rindex; |
| 38 | struct gfs2_alloc *al = NULL; |
| 39 | struct gfs2_holder i_gh; |
| 40 | unsigned int data_blocks, ind_blocks; |
| 41 | int alloc_required; |
| 42 | unsigned int x; |
| 43 | int error; |
| 44 | |
| 45 | gfs2_write_calc_reserv(ip, size, &data_blocks, &ind_blocks); |
| 46 | |
| 47 | error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, |
| 48 | LM_FLAG_PRIORITY | GL_SYNC, &i_gh); |
| 49 | if (error) |
| 50 | return error; |
| 51 | |
| 52 | if (!gfs2_is_jdata(ip)) { |
| 53 | gfs2_consist_inode(ip); |
| 54 | error = -EIO; |
| 55 | goto out; |
| 56 | } |
| 57 | |
| 58 | error = gfs2_write_alloc_required(ip, ip->i_di.di_size, size, |
| 59 | &alloc_required); |
| 60 | if (error) |
| 61 | goto out; |
| 62 | |
| 63 | if (alloc_required) { |
| 64 | al = gfs2_alloc_get(ip); |
| 65 | |
| 66 | al->al_requested = data_blocks + ind_blocks; |
| 67 | |
| 68 | error = gfs2_inplace_reserve(ip); |
| 69 | if (error) |
| 70 | goto out_alloc; |
| 71 | |
| 72 | error = gfs2_trans_begin(sdp, |
| 73 | al->al_rgd->rd_ri.ri_length + |
| 74 | data_blocks + ind_blocks + |
| 75 | RES_DINODE + RES_STATFS, 0); |
| 76 | if (error) |
| 77 | goto out_relse; |
| 78 | } else { |
| 79 | error = gfs2_trans_begin(sdp, data_blocks + |
| 80 | RES_DINODE + RES_STATFS, 0); |
| 81 | if (error) |
| 82 | goto out; |
| 83 | } |
| 84 | |
| 85 | for (x = 0; x < num; x++) { |
| 86 | struct gfs2_rindex ri; |
| 87 | char ri_buf[sizeof(struct gfs2_rindex)]; |
| 88 | |
| 89 | error = copy_from_user(&ri, buf, sizeof(struct gfs2_rindex)); |
| 90 | if (error) { |
| 91 | error = -EFAULT; |
| 92 | goto out_trans; |
| 93 | } |
| 94 | gfs2_rindex_out(&ri, ri_buf); |
| 95 | |
| 96 | error = gfs2_jdata_write_mem(ip, ri_buf, ip->i_di.di_size, |
| 97 | sizeof(struct gfs2_rindex)); |
| 98 | if (error < 0) |
| 99 | goto out_trans; |
| 100 | gfs2_assert_withdraw(sdp, error == sizeof(struct gfs2_rindex)); |
| 101 | error = 0; |
| 102 | |
| 103 | gfs2_statfs_change(sdp, ri.ri_data, ri.ri_data, 0); |
| 104 | |
| 105 | buf += sizeof(struct gfs2_rindex); |
| 106 | } |
| 107 | |
| 108 | out_trans: |
| 109 | gfs2_trans_end(sdp); |
| 110 | |
| 111 | out_relse: |
| 112 | if (alloc_required) |
| 113 | gfs2_inplace_release(ip); |
| 114 | |
| 115 | out_alloc: |
| 116 | if (alloc_required) |
| 117 | gfs2_alloc_put(ip); |
| 118 | |
| 119 | out: |
| 120 | ip->i_gl->gl_vn++; |
| 121 | gfs2_glock_dq_uninit(&i_gh); |
| 122 | |
| 123 | return error; |
| 124 | } |
| 125 | |
| 126 | static void drop_dentries(struct gfs2_inode *ip) |
| 127 | { |
| 128 | struct inode *inode; |
| 129 | struct dentry *d; |
| 130 | |
| 131 | inode = gfs2_ip2v_lookup(ip); |
| 132 | if (!inode) |
| 133 | return; |
| 134 | |
| 135 | restart: |
| 136 | spin_lock(&dcache_lock); |
| 137 | list_for_each_entry(d, &inode->i_dentry, d_alias) { |
| 138 | if (d_unhashed(d)) |
| 139 | continue; |
| 140 | dget_locked(d); |
| 141 | __d_drop(d); |
| 142 | spin_unlock(&dcache_lock); |
| 143 | dput(d); |
| 144 | goto restart; |
| 145 | } |
| 146 | spin_unlock(&dcache_lock); |
| 147 | |
| 148 | iput(inode); |
| 149 | } |
| 150 | |
| 151 | /* This is called by an ioctl to rename an ordinary file that's represented |
| 152 | in the vfs to a hidden system file that isn't represented in the vfs. It's |
| 153 | used to add journals, along with the associated system files, to a fs. */ |
| 154 | |
| 155 | int gfs2_rename2system(struct gfs2_inode *ip, |
| 156 | struct gfs2_inode *old_dip, char *old_name, |
| 157 | struct gfs2_inode *new_dip, char *new_name) |
| 158 | { |
| 159 | struct gfs2_sbd *sdp = ip->i_sbd; |
| 160 | struct gfs2_holder ghs[3]; |
| 161 | struct qstr old_qstr, new_qstr; |
| 162 | struct gfs2_inum inum; |
| 163 | int alloc_required; |
| 164 | struct buffer_head *dibh; |
| 165 | int error; |
| 166 | |
| 167 | gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_NOCACHE, ghs); |
| 168 | gfs2_holder_init(old_dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1); |
| 169 | gfs2_holder_init(new_dip->i_gl, LM_ST_EXCLUSIVE, GL_SYNC, ghs + 2); |
| 170 | |
| 171 | error = gfs2_glock_nq_m(3, ghs); |
| 172 | if (error) |
| 173 | goto out; |
| 174 | |
| 175 | error = -EMLINK; |
| 176 | if (ip->i_di.di_nlink != 1) |
| 177 | goto out_gunlock; |
| 178 | error = -EINVAL; |
| 179 | if (!S_ISREG(ip->i_di.di_mode)) |
| 180 | goto out_gunlock; |
| 181 | |
| 182 | old_qstr.name = old_name; |
| 183 | old_qstr.len = strlen(old_name); |
| 184 | error = gfs2_dir_search(old_dip, &old_qstr, &inum, NULL); |
| 185 | switch (error) { |
| 186 | case 0: |
| 187 | break; |
| 188 | default: |
| 189 | goto out_gunlock; |
| 190 | } |
| 191 | |
| 192 | error = -EINVAL; |
| 193 | if (!gfs2_inum_equal(&inum, &ip->i_num)) |
| 194 | goto out_gunlock; |
| 195 | |
| 196 | new_qstr.name = new_name; |
| 197 | new_qstr.len = strlen(new_name); |
| 198 | error = gfs2_dir_search(new_dip, &new_qstr, NULL, NULL); |
| 199 | switch (error) { |
| 200 | case -ENOENT: |
| 201 | break; |
| 202 | case 0: |
| 203 | error = -EEXIST; |
| 204 | default: |
| 205 | goto out_gunlock; |
| 206 | } |
| 207 | |
| 208 | gfs2_alloc_get(ip); |
| 209 | |
| 210 | error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); |
| 211 | if (error) |
| 212 | goto out_alloc; |
| 213 | |
| 214 | error = gfs2_diradd_alloc_required(new_dip, &new_qstr, &alloc_required); |
| 215 | if (error) |
| 216 | goto out_unhold; |
| 217 | |
| 218 | if (alloc_required) { |
| 219 | struct gfs2_alloc *al = gfs2_alloc_get(new_dip); |
| 220 | |
| 221 | al->al_requested = sdp->sd_max_dirres; |
| 222 | |
| 223 | error = gfs2_inplace_reserve(new_dip); |
| 224 | if (error) |
| 225 | goto out_alloc2; |
| 226 | |
| 227 | error = gfs2_trans_begin(sdp, |
| 228 | sdp->sd_max_dirres + |
| 229 | al->al_rgd->rd_ri.ri_length + |
| 230 | 3 * RES_DINODE + RES_LEAF + |
| 231 | RES_STATFS + RES_QUOTA, 0); |
| 232 | if (error) |
| 233 | goto out_ipreserv; |
| 234 | } else { |
| 235 | error = gfs2_trans_begin(sdp, |
| 236 | 3 * RES_DINODE + 2 * RES_LEAF + |
| 237 | RES_QUOTA, 0); |
| 238 | if (error) |
| 239 | goto out_unhold; |
| 240 | } |
| 241 | |
| 242 | error = gfs2_dir_del(old_dip, &old_qstr); |
| 243 | if (error) |
| 244 | goto out_trans; |
| 245 | |
| 246 | error = gfs2_dir_add(new_dip, &new_qstr, &ip->i_num, |
| 247 | IF2DT(ip->i_di.di_mode)); |
| 248 | if (error) |
| 249 | goto out_trans; |
| 250 | |
| 251 | gfs2_quota_change(ip, -ip->i_di.di_blocks, ip->i_di.di_uid, |
| 252 | ip->i_di.di_gid); |
| 253 | |
| 254 | error = gfs2_meta_inode_buffer(ip, &dibh); |
| 255 | if (error) |
| 256 | goto out_trans; |
| 257 | ip->i_di.di_flags |= GFS2_DIF_SYSTEM; |
Steven Whitehouse | d4e9c4c | 2006-01-18 11:19:28 +0000 | [diff] [blame^] | 258 | gfs2_trans_add_bh(ip->i_gl, dibh, 1); |
David Teigland | b3b94fa | 2006-01-16 16:50:04 +0000 | [diff] [blame] | 259 | gfs2_dinode_out(&ip->i_di, dibh->b_data); |
| 260 | brelse(dibh); |
| 261 | |
| 262 | drop_dentries(ip); |
| 263 | |
| 264 | out_trans: |
| 265 | gfs2_trans_end(sdp); |
| 266 | |
| 267 | out_ipreserv: |
| 268 | if (alloc_required) |
| 269 | gfs2_inplace_release(new_dip); |
| 270 | |
| 271 | out_alloc2: |
| 272 | if (alloc_required) |
| 273 | gfs2_alloc_put(new_dip); |
| 274 | |
| 275 | out_unhold: |
| 276 | gfs2_quota_unhold(ip); |
| 277 | |
| 278 | out_alloc: |
| 279 | gfs2_alloc_put(ip); |
| 280 | |
| 281 | out_gunlock: |
| 282 | gfs2_glock_dq_m(3, ghs); |
| 283 | |
| 284 | out: |
| 285 | gfs2_holder_uninit(ghs); |
| 286 | gfs2_holder_uninit(ghs + 1); |
| 287 | gfs2_holder_uninit(ghs + 2); |
| 288 | |
| 289 | return error; |
| 290 | } |
| 291 | |