diff options
Diffstat (limited to 'fs/ocfs2/namei.c')
-rw-r--r-- | fs/ocfs2/namei.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 818df582ba06..f010b22b1c44 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c | |||
@@ -2041,6 +2041,274 @@ leave: | |||
2041 | return status; | 2041 | return status; |
2042 | } | 2042 | } |
2043 | 2043 | ||
2044 | int ocfs2_create_inode_in_orphan(struct inode *dir, | ||
2045 | int mode, | ||
2046 | struct inode **new_inode) | ||
2047 | { | ||
2048 | int status, did_quota_inode = 0; | ||
2049 | struct inode *inode = NULL; | ||
2050 | struct inode *orphan_dir = NULL; | ||
2051 | struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); | ||
2052 | struct ocfs2_dinode *di = NULL; | ||
2053 | handle_t *handle = NULL; | ||
2054 | char orphan_name[OCFS2_ORPHAN_NAMELEN + 1]; | ||
2055 | struct buffer_head *parent_di_bh = NULL; | ||
2056 | struct buffer_head *new_di_bh = NULL; | ||
2057 | struct ocfs2_alloc_context *inode_ac = NULL; | ||
2058 | struct ocfs2_dir_lookup_result orphan_insert = { NULL, }; | ||
2059 | |||
2060 | status = ocfs2_inode_lock(dir, &parent_di_bh, 1); | ||
2061 | if (status < 0) { | ||
2062 | if (status != -ENOENT) | ||
2063 | mlog_errno(status); | ||
2064 | return status; | ||
2065 | } | ||
2066 | |||
2067 | /* | ||
2068 | * We give the orphan dir the root blkno to fake an orphan name, | ||
2069 | * and allocate enough space for our insertion. | ||
2070 | */ | ||
2071 | status = ocfs2_prepare_orphan_dir(osb, &orphan_dir, | ||
2072 | osb->root_blkno, | ||
2073 | orphan_name, &orphan_insert); | ||
2074 | if (status < 0) { | ||
2075 | mlog_errno(status); | ||
2076 | goto leave; | ||
2077 | } | ||
2078 | |||
2079 | /* reserve an inode spot */ | ||
2080 | status = ocfs2_reserve_new_inode(osb, &inode_ac); | ||
2081 | if (status < 0) { | ||
2082 | if (status != -ENOSPC) | ||
2083 | mlog_errno(status); | ||
2084 | goto leave; | ||
2085 | } | ||
2086 | |||
2087 | inode = ocfs2_get_init_inode(dir, mode); | ||
2088 | if (!inode) { | ||
2089 | status = -ENOMEM; | ||
2090 | mlog_errno(status); | ||
2091 | goto leave; | ||
2092 | } | ||
2093 | |||
2094 | handle = ocfs2_start_trans(osb, ocfs2_mknod_credits(osb->sb, 0, 0)); | ||
2095 | if (IS_ERR(handle)) { | ||
2096 | status = PTR_ERR(handle); | ||
2097 | handle = NULL; | ||
2098 | mlog_errno(status); | ||
2099 | goto leave; | ||
2100 | } | ||
2101 | |||
2102 | /* We don't use standard VFS wrapper because we don't want vfs_dq_init | ||
2103 | * to be called. */ | ||
2104 | if (sb_any_quota_active(osb->sb) && | ||
2105 | osb->sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA) { | ||
2106 | status = -EDQUOT; | ||
2107 | goto leave; | ||
2108 | } | ||
2109 | did_quota_inode = 1; | ||
2110 | |||
2111 | /* do the real work now. */ | ||
2112 | status = ocfs2_mknod_locked(osb, dir, inode, | ||
2113 | 0, &new_di_bh, parent_di_bh, handle, | ||
2114 | inode_ac); | ||
2115 | if (status < 0) { | ||
2116 | mlog_errno(status); | ||
2117 | goto leave; | ||
2118 | } | ||
2119 | |||
2120 | status = ocfs2_blkno_stringify(OCFS2_I(inode)->ip_blkno, orphan_name); | ||
2121 | if (status < 0) { | ||
2122 | mlog_errno(status); | ||
2123 | goto leave; | ||
2124 | } | ||
2125 | |||
2126 | di = (struct ocfs2_dinode *)new_di_bh->b_data; | ||
2127 | status = ocfs2_orphan_add(osb, handle, inode, di, orphan_name, | ||
2128 | &orphan_insert, orphan_dir); | ||
2129 | if (status < 0) { | ||
2130 | mlog_errno(status); | ||
2131 | goto leave; | ||
2132 | } | ||
2133 | |||
2134 | /* get open lock so that only nodes can't remove it from orphan dir. */ | ||
2135 | status = ocfs2_open_lock(inode); | ||
2136 | if (status < 0) | ||
2137 | mlog_errno(status); | ||
2138 | |||
2139 | leave: | ||
2140 | if (status < 0 && did_quota_inode) | ||
2141 | vfs_dq_free_inode(inode); | ||
2142 | if (handle) | ||
2143 | ocfs2_commit_trans(osb, handle); | ||
2144 | |||
2145 | if (orphan_dir) { | ||
2146 | /* This was locked for us in ocfs2_prepare_orphan_dir() */ | ||
2147 | ocfs2_inode_unlock(orphan_dir, 1); | ||
2148 | mutex_unlock(&orphan_dir->i_mutex); | ||
2149 | iput(orphan_dir); | ||
2150 | } | ||
2151 | |||
2152 | if (status == -ENOSPC) | ||
2153 | mlog(0, "Disk is full\n"); | ||
2154 | |||
2155 | if ((status < 0) && inode) { | ||
2156 | clear_nlink(inode); | ||
2157 | iput(inode); | ||
2158 | } | ||
2159 | |||
2160 | if (inode_ac) | ||
2161 | ocfs2_free_alloc_context(inode_ac); | ||
2162 | |||
2163 | brelse(new_di_bh); | ||
2164 | |||
2165 | if (!status) | ||
2166 | *new_inode = inode; | ||
2167 | |||
2168 | ocfs2_free_dir_lookup_result(&orphan_insert); | ||
2169 | |||
2170 | ocfs2_inode_unlock(dir, 1); | ||
2171 | brelse(parent_di_bh); | ||
2172 | return status; | ||
2173 | } | ||
2174 | |||
2175 | int ocfs2_mv_orphaned_inode_to_new(struct inode *dir, | ||
2176 | struct inode *inode, | ||
2177 | struct dentry *dentry) | ||
2178 | { | ||
2179 | int status = 0; | ||
2180 | struct buffer_head *parent_di_bh = NULL; | ||
2181 | handle_t *handle = NULL; | ||
2182 | struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); | ||
2183 | struct ocfs2_dinode *dir_di, *di; | ||
2184 | struct inode *orphan_dir_inode = NULL; | ||
2185 | struct buffer_head *orphan_dir_bh = NULL; | ||
2186 | struct buffer_head *di_bh = NULL; | ||
2187 | struct ocfs2_dir_lookup_result lookup = { NULL, }; | ||
2188 | |||
2189 | mlog_entry("(0x%p, 0x%p, %.*s')\n", dir, dentry, | ||
2190 | dentry->d_name.len, dentry->d_name.name); | ||
2191 | |||
2192 | status = ocfs2_inode_lock(dir, &parent_di_bh, 1); | ||
2193 | if (status < 0) { | ||
2194 | if (status != -ENOENT) | ||
2195 | mlog_errno(status); | ||
2196 | return status; | ||
2197 | } | ||
2198 | |||
2199 | dir_di = (struct ocfs2_dinode *) parent_di_bh->b_data; | ||
2200 | if (!dir_di->i_links_count) { | ||
2201 | /* can't make a file in a deleted directory. */ | ||
2202 | status = -ENOENT; | ||
2203 | goto leave; | ||
2204 | } | ||
2205 | |||
2206 | status = ocfs2_check_dir_for_entry(dir, dentry->d_name.name, | ||
2207 | dentry->d_name.len); | ||
2208 | if (status) | ||
2209 | goto leave; | ||
2210 | |||
2211 | /* get a spot inside the dir. */ | ||
2212 | status = ocfs2_prepare_dir_for_insert(osb, dir, parent_di_bh, | ||
2213 | dentry->d_name.name, | ||
2214 | dentry->d_name.len, &lookup); | ||
2215 | if (status < 0) { | ||
2216 | mlog_errno(status); | ||
2217 | goto leave; | ||
2218 | } | ||
2219 | |||
2220 | orphan_dir_inode = ocfs2_get_system_file_inode(osb, | ||
2221 | ORPHAN_DIR_SYSTEM_INODE, | ||
2222 | osb->slot_num); | ||
2223 | if (!orphan_dir_inode) { | ||
2224 | status = -EEXIST; | ||
2225 | mlog_errno(status); | ||
2226 | goto leave; | ||
2227 | } | ||
2228 | |||
2229 | mutex_lock(&orphan_dir_inode->i_mutex); | ||
2230 | |||
2231 | status = ocfs2_inode_lock(orphan_dir_inode, &orphan_dir_bh, 1); | ||
2232 | if (status < 0) { | ||
2233 | mlog_errno(status); | ||
2234 | mutex_unlock(&orphan_dir_inode->i_mutex); | ||
2235 | iput(orphan_dir_inode); | ||
2236 | goto leave; | ||
2237 | } | ||
2238 | |||
2239 | status = ocfs2_read_inode_block(inode, &di_bh); | ||
2240 | if (status < 0) { | ||
2241 | mlog_errno(status); | ||
2242 | goto orphan_unlock; | ||
2243 | } | ||
2244 | |||
2245 | handle = ocfs2_start_trans(osb, ocfs2_rename_credits(osb->sb)); | ||
2246 | if (IS_ERR(handle)) { | ||
2247 | status = PTR_ERR(handle); | ||
2248 | handle = NULL; | ||
2249 | mlog_errno(status); | ||
2250 | goto orphan_unlock; | ||
2251 | } | ||
2252 | |||
2253 | status = ocfs2_journal_access_di(handle, INODE_CACHE(inode), | ||
2254 | di_bh, OCFS2_JOURNAL_ACCESS_WRITE); | ||
2255 | if (status < 0) { | ||
2256 | mlog_errno(status); | ||
2257 | goto out_commit; | ||
2258 | } | ||
2259 | |||
2260 | status = ocfs2_orphan_del(osb, handle, orphan_dir_inode, inode, | ||
2261 | orphan_dir_bh); | ||
2262 | if (status < 0) { | ||
2263 | mlog_errno(status); | ||
2264 | goto out_commit; | ||
2265 | } | ||
2266 | |||
2267 | di = (struct ocfs2_dinode *)di_bh->b_data; | ||
2268 | le32_add_cpu(&di->i_flags, -OCFS2_ORPHANED_FL); | ||
2269 | di->i_orphaned_slot = 0; | ||
2270 | ocfs2_journal_dirty(handle, di_bh); | ||
2271 | |||
2272 | status = ocfs2_add_entry(handle, dentry, inode, | ||
2273 | OCFS2_I(inode)->ip_blkno, parent_di_bh, | ||
2274 | &lookup); | ||
2275 | if (status < 0) { | ||
2276 | mlog_errno(status); | ||
2277 | goto out_commit; | ||
2278 | } | ||
2279 | |||
2280 | status = ocfs2_dentry_attach_lock(dentry, inode, | ||
2281 | OCFS2_I(dir)->ip_blkno); | ||
2282 | if (status) { | ||
2283 | mlog_errno(status); | ||
2284 | goto out_commit; | ||
2285 | } | ||
2286 | |||
2287 | insert_inode_hash(inode); | ||
2288 | dentry->d_op = &ocfs2_dentry_ops; | ||
2289 | d_instantiate(dentry, inode); | ||
2290 | status = 0; | ||
2291 | out_commit: | ||
2292 | ocfs2_commit_trans(osb, handle); | ||
2293 | orphan_unlock: | ||
2294 | ocfs2_inode_unlock(orphan_dir_inode, 1); | ||
2295 | mutex_unlock(&orphan_dir_inode->i_mutex); | ||
2296 | iput(orphan_dir_inode); | ||
2297 | leave: | ||
2298 | |||
2299 | ocfs2_inode_unlock(dir, 1); | ||
2300 | |||
2301 | brelse(di_bh); | ||
2302 | brelse(parent_di_bh); | ||
2303 | brelse(orphan_dir_bh); | ||
2304 | |||
2305 | ocfs2_free_dir_lookup_result(&lookup); | ||
2306 | |||
2307 | mlog_exit(status); | ||
2308 | |||
2309 | return status; | ||
2310 | } | ||
2311 | |||
2044 | const struct inode_operations ocfs2_dir_iops = { | 2312 | const struct inode_operations ocfs2_dir_iops = { |
2045 | .create = ocfs2_create, | 2313 | .create = ocfs2_create, |
2046 | .lookup = ocfs2_lookup, | 2314 | .lookup = ocfs2_lookup, |