blob: 68686b79650a842c25f8d4fe2340b2e87c1d51e4 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * namei.c
3 *
4 * PURPOSE
5 * Inode name handling routines for the OSTA-UDF(tm) filesystem.
6 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * COPYRIGHT
8 * This file is distributed under the terms of the GNU General Public
9 * License (GPL). Copies of the GPL can be obtained from:
10 * ftp://prep.ai.mit.edu/pub/gnu/GPL
11 * Each contributing author retains all rights to their own work.
12 *
13 * (C) 1998-2004 Ben Fennema
14 * (C) 1999-2000 Stelias Computing Inc
15 *
16 * HISTORY
17 *
18 * 12/12/98 blf Created. Split out the lookup code from dir.c
19 * 04/19/99 blf link, mknod, symlink support
20 */
21
22#include "udfdecl.h"
23
24#include "udf_i.h"
25#include "udf_sb.h"
26#include <linux/string.h>
27#include <linux/errno.h>
28#include <linux/mm.h>
29#include <linux/slab.h>
30#include <linux/quotaops.h>
31#include <linux/smp_lock.h>
32#include <linux/buffer_head.h>
Alexey Dobriyane8edc6e2007-05-21 01:22:52 +040033#include <linux/sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -070035static inline int udf_match(int len1, const char *name1, int len2,
36 const char *name2)
Linus Torvalds1da177e2005-04-16 15:20:36 -070037{
38 if (len1 != len2)
39 return 0;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070040
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 return !memcmp(name1, name2, len1);
42}
43
44int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi,
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -070045 struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh,
Marcin Slusarz4b111112008-02-08 04:20:36 -080046 uint8_t *impuse, uint8_t *fileident)
Linus Torvalds1da177e2005-04-16 15:20:36 -070047{
48 uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(tag);
49 uint16_t crc;
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 int offset;
51 uint16_t liu = le16_to_cpu(cfi->lengthOfImpUse);
52 uint8_t lfi = cfi->lengthFileIdent;
53 int padlen = fibh->eoffset - fibh->soffset - liu - lfi -
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070054 sizeof(struct fileIdentDesc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 int adinicb = 0;
56
Marcin Slusarzc0b34432008-02-08 04:20:42 -080057 if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 adinicb = 1;
59
60 offset = fibh->soffset + sizeof(struct fileIdentDesc);
61
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -070062 if (impuse) {
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070063 if (adinicb || (offset + liu < 0)) {
64 memcpy((uint8_t *)sfi->impUse, impuse, liu);
65 } else if (offset >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 memcpy(fibh->ebh->b_data + offset, impuse, liu);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070067 } else {
68 memcpy((uint8_t *)sfi->impUse, impuse, -offset);
Marcin Slusarz4b111112008-02-08 04:20:36 -080069 memcpy(fibh->ebh->b_data, impuse - offset,
70 liu + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 }
72 }
73
74 offset += liu;
75
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -070076 if (fileident) {
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070077 if (adinicb || (offset + lfi < 0)) {
78 memcpy((uint8_t *)sfi->fileIdent + liu, fileident, lfi);
79 } else if (offset >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 memcpy(fibh->ebh->b_data + offset, fileident, lfi);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070081 } else {
Marcin Slusarz4b111112008-02-08 04:20:36 -080082 memcpy((uint8_t *)sfi->fileIdent + liu, fileident,
83 -offset);
84 memcpy(fibh->ebh->b_data, fileident - offset,
85 lfi + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 }
87 }
88
89 offset += lfi;
90
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070091 if (adinicb || (offset + padlen < 0)) {
92 memset((uint8_t *)sfi->padding + liu + lfi, 0x00, padlen);
93 } else if (offset >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 memset(fibh->ebh->b_data + offset, 0x00, padlen);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070095 } else {
96 memset((uint8_t *)sfi->padding + liu + lfi, 0x00, -offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 memset(fibh->ebh->b_data, 0x00, padlen + offset);
98 }
99
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700100 crc = udf_crc((uint8_t *)cfi + sizeof(tag),
101 sizeof(struct fileIdentDesc) - sizeof(tag), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700103 if (fibh->sbh == fibh->ebh) {
104 crc = udf_crc((uint8_t *)sfi->impUse,
Marcin Slusarz4b111112008-02-08 04:20:36 -0800105 crclen + sizeof(tag) -
106 sizeof(struct fileIdentDesc), crc);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700107 } else if (sizeof(struct fileIdentDesc) >= -fibh->soffset) {
Marcin Slusarz4b111112008-02-08 04:20:36 -0800108 crc = udf_crc(fibh->ebh->b_data +
109 sizeof(struct fileIdentDesc) +
110 fibh->soffset,
111 crclen + sizeof(tag) -
112 sizeof(struct fileIdentDesc),
113 crc);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700114 } else {
115 crc = udf_crc((uint8_t *)sfi->impUse,
Marcin Slusarz4b111112008-02-08 04:20:36 -0800116 -fibh->soffset - sizeof(struct fileIdentDesc),
117 crc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 crc = udf_crc(fibh->ebh->b_data, fibh->eoffset, crc);
119 }
120
121 cfi->descTag.descCRC = cpu_to_le16(crc);
122 cfi->descTag.descCRCLength = cpu_to_le16(crclen);
Marcin Slusarz3f2587b2008-02-08 04:20:39 -0800123 cfi->descTag.tagChecksum = udf_tag_checksum(&cfi->descTag);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700125 if (adinicb || (sizeof(struct fileIdentDesc) <= -fibh->soffset)) {
Marcin Slusarz4b111112008-02-08 04:20:36 -0800126 memcpy((uint8_t *)sfi, (uint8_t *)cfi,
127 sizeof(struct fileIdentDesc));
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700128 } else {
129 memcpy((uint8_t *)sfi, (uint8_t *)cfi, -fibh->soffset);
130 memcpy(fibh->ebh->b_data, (uint8_t *)cfi - fibh->soffset,
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700131 sizeof(struct fileIdentDesc) + fibh->soffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 }
133
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700134 if (adinicb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 mark_inode_dirty(inode);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700136 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 if (fibh->sbh != fibh->ebh)
138 mark_buffer_dirty_inode(fibh->ebh, inode);
139 mark_buffer_dirty_inode(fibh->sbh, inode);
140 }
141 return 0;
142}
143
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700144static struct fileIdentDesc *udf_find_entry(struct inode *dir,
145 struct dentry *dentry,
146 struct udf_fileident_bh *fibh,
147 struct fileIdentDesc *cfi)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148{
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700149 struct fileIdentDesc *fi = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 loff_t f_pos;
151 int block, flen;
Jan Karab80697c2008-03-04 14:14:05 +0100152 char *fname = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 char *nameptr;
154 uint8_t lfi;
155 uint16_t liu;
KAMBAROV, ZAURec471dc2005-06-28 20:45:10 -0700156 loff_t size;
Jan Karaff116fc2007-05-08 00:35:14 -0700157 kernel_lb_addr eloc;
158 uint32_t elen;
Jan Kara60448b12007-05-08 00:35:13 -0700159 sector_t offset;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700160 struct extent_position epos = {};
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800161 struct udf_inode_info *dinfo = UDF_I(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162
Jan Karaaf793292008-02-08 04:20:50 -0800163 size = udf_ext0_offset(dir) + dir->i_size;
164 f_pos = udf_ext0_offset(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165
Jan Karab80697c2008-03-04 14:14:05 +0100166 fibh->sbh = fibh->ebh = NULL;
Jan Karaaf793292008-02-08 04:20:50 -0800167 fibh->soffset = fibh->eoffset = f_pos & (dir->i_sb->s_blocksize - 1);
Jan Karab80697c2008-03-04 14:14:05 +0100168 if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
169 if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits, &epos,
170 &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30))
171 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700173 if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800174 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
Jan Karaff116fc2007-05-08 00:35:14 -0700175 epos.offset -= sizeof(short_ad);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800176 else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
Jan Karaff116fc2007-05-08 00:35:14 -0700177 epos.offset -= sizeof(long_ad);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800178 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 offset = 0;
180
Marcin Slusarz4b111112008-02-08 04:20:36 -0800181 fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
Jan Karab80697c2008-03-04 14:14:05 +0100182 if (!fibh->sbh)
183 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 }
185
Jan Karab80697c2008-03-04 14:14:05 +0100186 fname = kmalloc(UDF_NAME_LEN, GFP_NOFS);
187 if (!fname)
188 goto out_err;
189
Jan Karaaf793292008-02-08 04:20:50 -0800190 while (f_pos < size) {
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700191 fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc,
192 &elen, &offset);
Jan Karab80697c2008-03-04 14:14:05 +0100193 if (!fi)
194 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195
196 liu = le16_to_cpu(cfi->lengthOfImpUse);
197 lfi = cfi->lengthFileIdent;
198
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700199 if (fibh->sbh == fibh->ebh) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 nameptr = fi->fileIdent + liu;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700201 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 int poffset; /* Unpaded ending offset */
203
Marcin Slusarz4b111112008-02-08 04:20:36 -0800204 poffset = fibh->soffset + sizeof(struct fileIdentDesc) +
205 liu + lfi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206
Marcin Slusarz4b111112008-02-08 04:20:36 -0800207 if (poffset >= lfi)
208 nameptr = (uint8_t *)(fibh->ebh->b_data +
209 poffset - lfi);
210 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 nameptr = fname;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800212 memcpy(nameptr, fi->fileIdent + liu,
213 lfi - poffset);
214 memcpy(nameptr + lfi - poffset,
215 fibh->ebh->b_data, poffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 }
217 }
218
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700219 if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) {
220 if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 continue;
222 }
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700223
224 if ((cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) {
225 if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 continue;
227 }
228
229 if (!lfi)
230 continue;
231
Marcin Slusarz4b111112008-02-08 04:20:36 -0800232 flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
233 if (flen && udf_match(flen, fname, dentry->d_name.len,
Jan Karab80697c2008-03-04 14:14:05 +0100234 dentry->d_name.name))
235 goto out_ok;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 }
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700237
Jan Karab80697c2008-03-04 14:14:05 +0100238out_err:
239 fi = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 if (fibh->sbh != fibh->ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700241 brelse(fibh->ebh);
242 brelse(fibh->sbh);
Jan Karab80697c2008-03-04 14:14:05 +0100243out_ok:
Jan Kara3bf25cb2007-05-08 00:35:16 -0700244 brelse(epos.bh);
Jan Karab80697c2008-03-04 14:14:05 +0100245 kfree(fname);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700246
Jan Karab80697c2008-03-04 14:14:05 +0100247 return fi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248}
249
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700250static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry,
251 struct nameidata *nd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252{
253 struct inode *inode = NULL;
Jayachandran Cdb9a3692006-02-03 03:04:50 -0800254 struct fileIdentDesc cfi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 struct udf_fileident_bh fibh;
256
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700257 if (dentry->d_name.len > UDF_NAME_LEN - 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 return ERR_PTR(-ENAMETOOLONG);
259
260 lock_kernel();
261#ifdef UDF_RECOVERY
262 /* temporary shorthand for specifying files by inode number */
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700263 if (!strncmp(dentry->d_name.name, ".B=", 3)) {
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700264 kernel_lb_addr lb = {
265 .logicalBlockNum = 0,
Marcin Slusarz4b111112008-02-08 04:20:36 -0800266 .partitionReferenceNum =
267 simple_strtoul(dentry->d_name.name + 3,
268 NULL, 0),
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700269 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 inode = udf_iget(dir->i_sb, lb);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700271 if (!inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 unlock_kernel();
273 return ERR_PTR(-EACCES);
274 }
Marcin Slusarz4b111112008-02-08 04:20:36 -0800275 } else
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700276#endif /* UDF_RECOVERY */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700278 if (udf_find_entry(dir, dentry, &fibh, &cfi)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700280 brelse(fibh.ebh);
281 brelse(fibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282
283 inode = udf_iget(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation));
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700284 if (!inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 unlock_kernel();
286 return ERR_PTR(-EACCES);
287 }
288 }
289 unlock_kernel();
290 d_add(dentry, inode);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700291
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 return NULL;
293}
294
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700295static struct fileIdentDesc *udf_add_entry(struct inode *dir,
296 struct dentry *dentry,
297 struct udf_fileident_bh *fibh,
298 struct fileIdentDesc *cfi, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299{
Marcin Slusarz6c79e982008-02-08 04:20:30 -0800300 struct super_block *sb = dir->i_sb;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700301 struct fileIdentDesc *fi = NULL;
Jan Karab80697c2008-03-04 14:14:05 +0100302 char *name = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 int namelen;
304 loff_t f_pos;
Jan Karaaf793292008-02-08 04:20:50 -0800305 loff_t size = udf_ext0_offset(dir) + dir->i_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 int nfidlen;
307 uint8_t lfi;
308 uint16_t liu;
309 int block;
Jan Karaff116fc2007-05-08 00:35:14 -0700310 kernel_lb_addr eloc;
311 uint32_t elen;
Jan Kara60448b12007-05-08 00:35:13 -0700312 sector_t offset;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700313 struct extent_position epos = {};
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800314 struct udf_inode_info *dinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
Jan Karab80697c2008-03-04 14:14:05 +0100316 fibh->sbh = fibh->ebh = NULL;
317 name = kmalloc(UDF_NAME_LEN, GFP_NOFS);
318 if (!name) {
319 *err = -ENOMEM;
320 goto out_err;
321 }
322
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700323 if (dentry) {
324 if (!dentry->d_name.len) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 *err = -EINVAL;
Jan Karab80697c2008-03-04 14:14:05 +0100326 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 }
Marcin Slusarz4b111112008-02-08 04:20:36 -0800328 namelen = udf_put_filename(sb, dentry->d_name.name, name,
329 dentry->d_name.len);
330 if (!namelen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 *err = -ENAMETOOLONG;
Jan Karab80697c2008-03-04 14:14:05 +0100332 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 }
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700334 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 namelen = 0;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700336 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
338 nfidlen = (sizeof(struct fileIdentDesc) + namelen + 3) & ~3;
339
Jan Karaaf793292008-02-08 04:20:50 -0800340 f_pos = udf_ext0_offset(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341
Jan Karaaf793292008-02-08 04:20:50 -0800342 fibh->soffset = fibh->eoffset = f_pos & (dir->i_sb->s_blocksize - 1);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800343 dinfo = UDF_I(dir);
Jan Karab80697c2008-03-04 14:14:05 +0100344 if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
345 if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits, &epos,
346 &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) {
347 block = udf_get_lb_pblock(dir->i_sb,
348 dinfo->i_location, 0);
349 fibh->soffset = fibh->eoffset = sb->s_blocksize;
350 goto add;
351 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700353 if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800354 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
Jan Karaff116fc2007-05-08 00:35:14 -0700355 epos.offset -= sizeof(short_ad);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800356 else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
Jan Karaff116fc2007-05-08 00:35:14 -0700357 epos.offset -= sizeof(long_ad);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800358 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 offset = 0;
360
Marcin Slusarz4b111112008-02-08 04:20:36 -0800361 fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
362 if (!fibh->sbh) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 *err = -EIO;
Jan Karab80697c2008-03-04 14:14:05 +0100364 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 }
366
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800367 block = dinfo->i_location.logicalBlockNum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 }
369
Jan Karaaf793292008-02-08 04:20:50 -0800370 while (f_pos < size) {
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700371 fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc,
372 &elen, &offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700374 if (!fi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 *err = -EIO;
Jan Karab80697c2008-03-04 14:14:05 +0100376 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 }
378
379 liu = le16_to_cpu(cfi->lengthOfImpUse);
380 lfi = cfi->lengthFileIdent;
381
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700382 if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) {
Marcin Slusarz4b111112008-02-08 04:20:36 -0800383 if (((sizeof(struct fileIdentDesc) +
384 liu + lfi + 3) & ~3) == nfidlen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 cfi->descTag.tagSerialNum = cpu_to_le16(1);
386 cfi->fileVersionNum = cpu_to_le16(1);
387 cfi->fileCharacteristics = 0;
388 cfi->lengthFileIdent = namelen;
389 cfi->lengthOfImpUse = cpu_to_le16(0);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800390 if (!udf_write_fi(dir, cfi, fi, fibh, NULL,
391 name))
Jan Karab80697c2008-03-04 14:14:05 +0100392 goto out_ok;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800393 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 *err = -EIO;
Jan Karab80697c2008-03-04 14:14:05 +0100395 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 }
397 }
398 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 }
400
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700401add:
Jan Kara05343c42008-02-08 04:20:51 -0800402 if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
403 elen = (elen + sb->s_blocksize - 1) & ~(sb->s_blocksize - 1);
404 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
405 epos.offset -= sizeof(short_ad);
406 else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
407 epos.offset -= sizeof(long_ad);
408 udf_write_aext(dir, &epos, eloc, elen, 1);
409 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 f_pos += nfidlen;
411
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800412 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB &&
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700413 sb->s_blocksize - fibh->eoffset < nfidlen) {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700414 brelse(epos.bh);
Jan Karaff116fc2007-05-08 00:35:14 -0700415 epos.bh = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 fibh->soffset -= udf_ext0_offset(dir);
417 fibh->eoffset -= udf_ext0_offset(dir);
Jan Karaaf793292008-02-08 04:20:50 -0800418 f_pos -= udf_ext0_offset(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 if (fibh->sbh != fibh->ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700420 brelse(fibh->ebh);
421 brelse(fibh->sbh);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800422 fibh->sbh = fibh->ebh =
423 udf_expand_dir_adinicb(dir, &block, err);
424 if (!fibh->sbh)
Jan Karab80697c2008-03-04 14:14:05 +0100425 goto out_err;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800426 epos.block = dinfo->i_location;
Jan Karaff116fc2007-05-08 00:35:14 -0700427 epos.offset = udf_file_entry_alloc_offset(dir);
Jan Kara05343c42008-02-08 04:20:51 -0800428 /* Load extent udf_expand_dir_adinicb() has created */
429 udf_current_aext(dir, &epos, &eloc, &elen, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 }
431
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700432 if (sb->s_blocksize - fibh->eoffset >= nfidlen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 fibh->soffset = fibh->eoffset;
434 fibh->eoffset += nfidlen;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700435 if (fibh->sbh != fibh->ebh) {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700436 brelse(fibh->sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 fibh->sbh = fibh->ebh;
438 }
439
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800440 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
441 block = dinfo->i_location.logicalBlockNum;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800442 fi = (struct fileIdentDesc *)
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800443 (dinfo->i_ext.i_data +
Marcin Slusarzc0b34432008-02-08 04:20:42 -0800444 fibh->soffset -
Marcin Slusarz4b111112008-02-08 04:20:36 -0800445 udf_ext0_offset(dir) +
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800446 dinfo->i_lenEAttr);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700447 } else {
Marcin Slusarz4b111112008-02-08 04:20:36 -0800448 block = eloc.logicalBlockNum +
449 ((elen - 1) >>
450 dir->i_sb->s_blocksize_bits);
451 fi = (struct fileIdentDesc *)
452 (fibh->sbh->b_data + fibh->soffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 }
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700454 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 fibh->soffset = fibh->eoffset - sb->s_blocksize;
456 fibh->eoffset += nfidlen - sb->s_blocksize;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700457 if (fibh->sbh != fibh->ebh) {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700458 brelse(fibh->sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 fibh->sbh = fibh->ebh;
460 }
461
462 block = eloc.logicalBlockNum + ((elen - 1) >>
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700463 dir->i_sb->s_blocksize_bits);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800464 fibh->ebh = udf_bread(dir,
Jan Karaaf793292008-02-08 04:20:50 -0800465 f_pos >> dir->i_sb->s_blocksize_bits, 1, err);
Jan Karab80697c2008-03-04 14:14:05 +0100466 if (!fibh->ebh)
467 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700469 if (!fibh->soffset) {
Jan Karaff116fc2007-05-08 00:35:14 -0700470 if (udf_next_aext(dir, &epos, &eloc, &elen, 1) ==
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700471 (EXT_RECORDED_ALLOCATED >> 30)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 block = eloc.logicalBlockNum + ((elen - 1) >>
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700473 dir->i_sb->s_blocksize_bits);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800474 } else
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700475 block++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
Jan Kara3bf25cb2007-05-08 00:35:16 -0700477 brelse(fibh->sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 fibh->sbh = fibh->ebh;
479 fi = (struct fileIdentDesc *)(fibh->sbh->b_data);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700480 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 fi = (struct fileIdentDesc *)
Marcin Slusarz4b111112008-02-08 04:20:36 -0800482 (fibh->sbh->b_data + sb->s_blocksize +
483 fibh->soffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 }
485 }
486
487 memset(cfi, 0, sizeof(struct fileIdentDesc));
Marcin Slusarz6c79e982008-02-08 04:20:30 -0800488 if (UDF_SB(sb)->s_udfrev >= 0x0200)
Marcin Slusarz4b111112008-02-08 04:20:36 -0800489 udf_new_tag((char *)cfi, TAG_IDENT_FID, 3, 1, block,
490 sizeof(tag));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 else
Marcin Slusarz4b111112008-02-08 04:20:36 -0800492 udf_new_tag((char *)cfi, TAG_IDENT_FID, 2, 1, block,
493 sizeof(tag));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 cfi->fileVersionNum = cpu_to_le16(1);
495 cfi->lengthFileIdent = namelen;
496 cfi->lengthOfImpUse = cpu_to_le16(0);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700497 if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 dir->i_size += nfidlen;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800499 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
500 dinfo->i_lenAlloc += nfidlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 mark_inode_dirty(dir);
Jan Karab80697c2008-03-04 14:14:05 +0100502 goto out_ok;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700503 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 *err = -EIO;
Jan Karab80697c2008-03-04 14:14:05 +0100505 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 }
Jan Karab80697c2008-03-04 14:14:05 +0100507
508out_err:
509 fi = NULL;
510 if (fibh->sbh != fibh->ebh)
511 brelse(fibh->ebh);
512 brelse(fibh->sbh);
513out_ok:
514 brelse(epos.bh);
515 kfree(name);
516 return fi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517}
518
519static int udf_delete_entry(struct inode *inode, struct fileIdentDesc *fi,
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700520 struct udf_fileident_bh *fibh,
521 struct fileIdentDesc *cfi)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522{
523 cfi->fileCharacteristics |= FID_FILE_CHAR_DELETED;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700524
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT))
526 memset(&(cfi->icb), 0x00, sizeof(long_ad));
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700527
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 return udf_write_fi(inode, cfi, fi, fibh, NULL, NULL);
529}
530
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700531static int udf_create(struct inode *dir, struct dentry *dentry, int mode,
532 struct nameidata *nd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533{
534 struct udf_fileident_bh fibh;
535 struct inode *inode;
536 struct fileIdentDesc cfi, *fi;
537 int err;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800538 struct udf_inode_info *iinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
540 lock_kernel();
541 inode = udf_new_inode(dir, mode, &err);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700542 if (!inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 unlock_kernel();
544 return err;
545 }
546
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800547 iinfo = UDF_I(inode);
548 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 inode->i_data.a_ops = &udf_adinicb_aops;
550 else
551 inode->i_data.a_ops = &udf_aops;
552 inode->i_op = &udf_file_inode_operations;
553 inode->i_fop = &udf_file_operations;
554 inode->i_mode = mode;
555 mark_inode_dirty(inode);
556
Marcin Slusarz4b111112008-02-08 04:20:36 -0800557 fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
558 if (!fi) {
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700559 inode->i_nlink--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 mark_inode_dirty(inode);
561 iput(inode);
562 unlock_kernel();
563 return err;
564 }
565 cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800566 cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700567 *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800568 cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
Marcin Slusarzc0b34432008-02-08 04:20:42 -0800570 if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 mark_inode_dirty(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700573 brelse(fibh.ebh);
574 brelse(fibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 unlock_kernel();
576 d_instantiate(dentry, inode);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 return 0;
579}
580
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700581static int udf_mknod(struct inode *dir, struct dentry *dentry, int mode,
582 dev_t rdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583{
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700584 struct inode *inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 struct udf_fileident_bh fibh;
586 struct fileIdentDesc cfi, *fi;
587 int err;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800588 struct udf_inode_info *iinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
590 if (!old_valid_dev(rdev))
591 return -EINVAL;
592
593 lock_kernel();
594 err = -EIO;
595 inode = udf_new_inode(dir, mode, &err);
596 if (!inode)
597 goto out;
598
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800599 iinfo = UDF_I(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 inode->i_uid = current->fsuid;
601 init_special_inode(inode, mode, rdev);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800602 fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
603 if (!fi) {
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700604 inode->i_nlink--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 mark_inode_dirty(inode);
606 iput(inode);
607 unlock_kernel();
608 return err;
609 }
610 cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800611 cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700612 *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800613 cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
Marcin Slusarzc0b34432008-02-08 04:20:42 -0800615 if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 mark_inode_dirty(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 mark_inode_dirty(inode);
618
619 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700620 brelse(fibh.ebh);
621 brelse(fibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 d_instantiate(dentry, inode);
623 err = 0;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700624
625out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 unlock_kernel();
627 return err;
628}
629
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700630static int udf_mkdir(struct inode *dir, struct dentry *dentry, int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631{
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700632 struct inode *inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 struct udf_fileident_bh fibh;
634 struct fileIdentDesc cfi, *fi;
635 int err;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800636 struct udf_inode_info *dinfo = UDF_I(dir);
637 struct udf_inode_info *iinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
639 lock_kernel();
640 err = -EMLINK;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700641 if (dir->i_nlink >= (256 << sizeof(dir->i_nlink)) - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 goto out;
643
644 err = -EIO;
645 inode = udf_new_inode(dir, S_IFDIR, &err);
646 if (!inode)
647 goto out;
648
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800649 iinfo = UDF_I(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 inode->i_op = &udf_dir_inode_operations;
651 inode->i_fop = &udf_dir_operations;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800652 fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err);
653 if (!fi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 inode->i_nlink--;
655 mark_inode_dirty(inode);
656 iput(inode);
657 goto out;
658 }
659 inode->i_nlink = 2;
660 cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800661 cfi.icb.extLocation = cpu_to_lelb(dinfo->i_location);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700662 *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800663 cpu_to_le32(dinfo->i_unique & 0x00000000FFFFFFFFUL);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800664 cfi.fileCharacteristics =
665 FID_FILE_CHAR_DIRECTORY | FID_FILE_CHAR_PARENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 udf_write_fi(inode, &cfi, fi, &fibh, NULL, NULL);
Jan Kara3bf25cb2007-05-08 00:35:16 -0700667 brelse(fibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 inode->i_mode = S_IFDIR | mode;
669 if (dir->i_mode & S_ISGID)
670 inode->i_mode |= S_ISGID;
671 mark_inode_dirty(inode);
672
Marcin Slusarz4b111112008-02-08 04:20:36 -0800673 fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
674 if (!fi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 inode->i_nlink = 0;
676 mark_inode_dirty(inode);
677 iput(inode);
678 goto out;
679 }
680 cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800681 cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700682 *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800683 cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 cfi.fileCharacteristics |= FID_FILE_CHAR_DIRECTORY;
685 udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
Dave Hansend8c76e62006-09-30 23:29:04 -0700686 inc_nlink(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 mark_inode_dirty(dir);
688 d_instantiate(dentry, inode);
689 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700690 brelse(fibh.ebh);
691 brelse(fibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 err = 0;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700693
694out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 unlock_kernel();
696 return err;
697}
698
699static int empty_dir(struct inode *dir)
700{
701 struct fileIdentDesc *fi, cfi;
702 struct udf_fileident_bh fibh;
703 loff_t f_pos;
Jan Karaaf793292008-02-08 04:20:50 -0800704 loff_t size = udf_ext0_offset(dir) + dir->i_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 int block;
Jan Karaff116fc2007-05-08 00:35:14 -0700706 kernel_lb_addr eloc;
707 uint32_t elen;
Jan Kara60448b12007-05-08 00:35:13 -0700708 sector_t offset;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700709 struct extent_position epos = {};
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800710 struct udf_inode_info *dinfo = UDF_I(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
Jan Karaaf793292008-02-08 04:20:50 -0800712 f_pos = udf_ext0_offset(dir);
713 fibh.soffset = fibh.eoffset = f_pos & (dir->i_sb->s_blocksize - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800715 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 fibh.sbh = fibh.ebh = NULL;
Jan Karaaf793292008-02-08 04:20:50 -0800717 else if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits,
Marcin Slusarz4b111112008-02-08 04:20:36 -0800718 &epos, &eloc, &elen, &offset) ==
719 (EXT_RECORDED_ALLOCATED >> 30)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700721 if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800722 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
Jan Karaff116fc2007-05-08 00:35:14 -0700723 epos.offset -= sizeof(short_ad);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800724 else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
Jan Karaff116fc2007-05-08 00:35:14 -0700725 epos.offset -= sizeof(long_ad);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800726 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 offset = 0;
728
Marcin Slusarz4b111112008-02-08 04:20:36 -0800729 fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block);
730 if (!fibh.sbh) {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700731 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 return 0;
733 }
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700734 } else {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700735 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 return 0;
737 }
738
Jan Karaaf793292008-02-08 04:20:50 -0800739 while (f_pos < size) {
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700740 fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &epos, &eloc,
741 &elen, &offset);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700742 if (!fi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700744 brelse(fibh.ebh);
745 brelse(fibh.sbh);
746 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 return 0;
748 }
749
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700750 if (cfi.lengthFileIdent &&
751 (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700753 brelse(fibh.ebh);
754 brelse(fibh.sbh);
755 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 return 0;
757 }
758 }
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700759
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700761 brelse(fibh.ebh);
762 brelse(fibh.sbh);
763 brelse(epos.bh);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700764
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 return 1;
766}
767
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700768static int udf_rmdir(struct inode *dir, struct dentry *dentry)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769{
770 int retval;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700771 struct inode *inode = dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 struct udf_fileident_bh fibh;
773 struct fileIdentDesc *fi, cfi;
774 kernel_lb_addr tloc;
775
776 retval = -ENOENT;
777 lock_kernel();
778 fi = udf_find_entry(dir, dentry, &fibh, &cfi);
779 if (!fi)
780 goto out;
781
782 retval = -EIO;
783 tloc = lelb_to_cpu(cfi.icb.extLocation);
784 if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino)
785 goto end_rmdir;
786 retval = -ENOTEMPTY;
787 if (!empty_dir(inode))
788 goto end_rmdir;
789 retval = udf_delete_entry(dir, fi, &fibh, &cfi);
790 if (retval)
791 goto end_rmdir;
792 if (inode->i_nlink != 2)
793 udf_warning(inode->i_sb, "udf_rmdir",
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700794 "empty directory has nlink != 2 (%d)",
795 inode->i_nlink);
Dave Hansence71ec32006-09-30 23:29:06 -0700796 clear_nlink(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 inode->i_size = 0;
Stephen Mollettc007c062007-05-08 00:31:31 -0700798 inode_dec_link_count(dir);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800799 inode->i_ctime = dir->i_ctime = dir->i_mtime =
800 current_fs_time(dir->i_sb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 mark_inode_dirty(dir);
802
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700803end_rmdir:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700805 brelse(fibh.ebh);
806 brelse(fibh.sbh);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700807
808out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 unlock_kernel();
810 return retval;
811}
812
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700813static int udf_unlink(struct inode *dir, struct dentry *dentry)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814{
815 int retval;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700816 struct inode *inode = dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 struct udf_fileident_bh fibh;
818 struct fileIdentDesc *fi;
819 struct fileIdentDesc cfi;
820 kernel_lb_addr tloc;
821
822 retval = -ENOENT;
823 lock_kernel();
824 fi = udf_find_entry(dir, dentry, &fibh, &cfi);
825 if (!fi)
826 goto out;
827
828 retval = -EIO;
829 tloc = lelb_to_cpu(cfi.icb.extLocation);
830 if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino)
831 goto end_unlink;
832
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700833 if (!inode->i_nlink) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 udf_debug("Deleting nonexistent file (%lu), %d\n",
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700835 inode->i_ino, inode->i_nlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 inode->i_nlink = 1;
837 }
838 retval = udf_delete_entry(dir, fi, &fibh, &cfi);
839 if (retval)
840 goto end_unlink;
841 dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb);
842 mark_inode_dirty(dir);
Dave Hansen9a53c3a2006-09-30 23:29:03 -0700843 inode_dec_link_count(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 inode->i_ctime = dir->i_ctime;
845 retval = 0;
846
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700847end_unlink:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700849 brelse(fibh.ebh);
850 brelse(fibh.sbh);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700851
852out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 unlock_kernel();
854 return retval;
855}
856
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700857static int udf_symlink(struct inode *dir, struct dentry *dentry,
858 const char *symname)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859{
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700860 struct inode *inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 struct pathComponent *pc;
862 char *compstart;
863 struct udf_fileident_bh fibh;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700864 struct extent_position epos = {};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 int eoffset, elen = 0;
866 struct fileIdentDesc *fi;
867 struct fileIdentDesc cfi;
868 char *ea;
869 int err;
870 int block;
Jan Karab80697c2008-03-04 14:14:05 +0100871 char *name = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 int namelen;
Marcin Slusarz6c79e982008-02-08 04:20:30 -0800873 struct buffer_head *bh;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800874 struct udf_inode_info *iinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875
876 lock_kernel();
Marcin Slusarz4b111112008-02-08 04:20:36 -0800877 inode = udf_new_inode(dir, S_IFLNK, &err);
878 if (!inode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 goto out;
880
Jan Karab80697c2008-03-04 14:14:05 +0100881 name = kmalloc(UDF_NAME_LEN, GFP_NOFS);
882 if (!name) {
883 err = -ENOMEM;
884 goto out_no_entry;
885 }
886
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800887 iinfo = UDF_I(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 inode->i_mode = S_IFLNK | S_IRWXUGO;
889 inode->i_data.a_ops = &udf_symlink_aops;
890 inode->i_op = &page_symlink_inode_operations;
891
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800892 if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
Jan Karaff116fc2007-05-08 00:35:14 -0700893 kernel_lb_addr eloc;
894 uint32_t elen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895
896 block = udf_new_block(inode->i_sb, inode,
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800897 iinfo->i_location.partitionReferenceNum,
898 iinfo->i_location.logicalBlockNum, &err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 if (!block)
900 goto out_no_entry;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800901 epos.block = iinfo->i_location;
Jan Karaff116fc2007-05-08 00:35:14 -0700902 epos.offset = udf_file_entry_alloc_offset(inode);
903 epos.bh = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 eloc.logicalBlockNum = block;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800905 eloc.partitionReferenceNum =
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800906 iinfo->i_location.partitionReferenceNum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 elen = inode->i_sb->s_blocksize;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800908 iinfo->i_lenExtents = elen;
Jan Karaff116fc2007-05-08 00:35:14 -0700909 udf_add_aext(inode, &epos, eloc, elen, 0);
Jan Kara3bf25cb2007-05-08 00:35:16 -0700910 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911
912 block = udf_get_pblock(inode->i_sb, block,
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800913 iinfo->i_location.partitionReferenceNum,
Marcin Slusarz4b111112008-02-08 04:20:36 -0800914 0);
Jan Karaff116fc2007-05-08 00:35:14 -0700915 epos.bh = udf_tread(inode->i_sb, block);
916 lock_buffer(epos.bh);
917 memset(epos.bh->b_data, 0x00, inode->i_sb->s_blocksize);
918 set_buffer_uptodate(epos.bh);
919 unlock_buffer(epos.bh);
920 mark_buffer_dirty_inode(epos.bh, inode);
921 ea = epos.bh->b_data + udf_ext0_offset(inode);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800922 } else
923 ea = iinfo->i_ext.i_data + iinfo->i_lenEAttr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924
925 eoffset = inode->i_sb->s_blocksize - udf_ext0_offset(inode);
926 pc = (struct pathComponent *)ea;
927
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700928 if (*symname == '/') {
929 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 symname++;
931 } while (*symname == '/');
932
933 pc->componentType = 1;
934 pc->lengthComponentIdent = 0;
935 pc->componentFileVersionNum = 0;
936 pc += sizeof(struct pathComponent);
937 elen += sizeof(struct pathComponent);
938 }
939
940 err = -ENAMETOOLONG;
941
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700942 while (*symname) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 if (elen + sizeof(struct pathComponent) > eoffset)
944 goto out_no_entry;
945
946 pc = (struct pathComponent *)(ea + elen);
947
948 compstart = (char *)symname;
949
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700950 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 symname++;
952 } while (*symname && *symname != '/');
953
954 pc->componentType = 5;
955 pc->lengthComponentIdent = 0;
956 pc->componentFileVersionNum = 0;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700957 if (compstart[0] == '.') {
958 if ((symname - compstart) == 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 pc->componentType = 4;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800960 else if ((symname - compstart) == 2 &&
961 compstart[1] == '.')
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 pc->componentType = 3;
963 }
964
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700965 if (pc->componentType == 5) {
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700966 namelen = udf_put_filename(inode->i_sb, compstart, name,
967 symname - compstart);
968 if (!namelen)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 goto out_no_entry;
970
Marcin Slusarz4b111112008-02-08 04:20:36 -0800971 if (elen + sizeof(struct pathComponent) + namelen >
972 eoffset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 goto out_no_entry;
974 else
975 pc->lengthComponentIdent = namelen;
976
977 memcpy(pc->componentIdent, name, namelen);
978 }
979
980 elen += sizeof(struct pathComponent) + pc->lengthComponentIdent;
981
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700982 if (*symname) {
983 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 symname++;
985 } while (*symname == '/');
986 }
987 }
988
Jan Kara3bf25cb2007-05-08 00:35:16 -0700989 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 inode->i_size = elen;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800991 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
992 iinfo->i_lenAlloc = inode->i_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 mark_inode_dirty(inode);
994
Marcin Slusarz4b111112008-02-08 04:20:36 -0800995 fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
996 if (!fi)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 goto out_no_entry;
998 cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800999 cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location);
Marcin Slusarz6c79e982008-02-08 04:20:30 -08001000 bh = UDF_SB(inode->i_sb)->s_lvid_bh;
1001 if (bh) {
Marcin Slusarz4b111112008-02-08 04:20:36 -08001002 struct logicalVolIntegrityDesc *lvid =
1003 (struct logicalVolIntegrityDesc *)bh->b_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 struct logicalVolHeaderDesc *lvhd;
1005 uint64_t uniqueID;
Marcin Slusarz4b111112008-02-08 04:20:36 -08001006 lvhd = (struct logicalVolHeaderDesc *)
1007 lvid->logicalVolContentsUse;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 uniqueID = le64_to_cpu(lvhd->uniqueID);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001009 *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
1010 cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 if (!(++uniqueID & 0x00000000FFFFFFFFUL))
1012 uniqueID += 16;
1013 lvhd->uniqueID = cpu_to_le64(uniqueID);
Marcin Slusarz6c79e982008-02-08 04:20:30 -08001014 mark_buffer_dirty(bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 }
1016 udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
Marcin Slusarzc0b34432008-02-08 04:20:42 -08001017 if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 mark_inode_dirty(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -07001020 brelse(fibh.ebh);
1021 brelse(fibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 d_instantiate(dentry, inode);
1023 err = 0;
1024
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001025out:
Jan Karab80697c2008-03-04 14:14:05 +01001026 kfree(name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 unlock_kernel();
1028 return err;
1029
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001030out_no_entry:
Dave Hansen9a53c3a2006-09-30 23:29:03 -07001031 inode_dec_link_count(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 iput(inode);
1033 goto out;
1034}
1035
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001036static int udf_link(struct dentry *old_dentry, struct inode *dir,
1037 struct dentry *dentry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038{
1039 struct inode *inode = old_dentry->d_inode;
1040 struct udf_fileident_bh fibh;
1041 struct fileIdentDesc cfi, *fi;
1042 int err;
Marcin Slusarz6c79e982008-02-08 04:20:30 -08001043 struct buffer_head *bh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044
1045 lock_kernel();
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001046 if (inode->i_nlink >= (256 << sizeof(inode->i_nlink)) - 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 unlock_kernel();
1048 return -EMLINK;
1049 }
1050
Marcin Slusarz4b111112008-02-08 04:20:36 -08001051 fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
1052 if (!fi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 unlock_kernel();
1054 return err;
1055 }
1056 cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
Marcin Slusarzc0b34432008-02-08 04:20:42 -08001057 cfi.icb.extLocation = cpu_to_lelb(UDF_I(inode)->i_location);
Marcin Slusarz6c79e982008-02-08 04:20:30 -08001058 bh = UDF_SB(inode->i_sb)->s_lvid_bh;
1059 if (bh) {
Marcin Slusarz4b111112008-02-08 04:20:36 -08001060 struct logicalVolIntegrityDesc *lvid =
1061 (struct logicalVolIntegrityDesc *)bh->b_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 struct logicalVolHeaderDesc *lvhd;
1063 uint64_t uniqueID;
Marcin Slusarz4b111112008-02-08 04:20:36 -08001064 lvhd = (struct logicalVolHeaderDesc *)
1065 (lvid->logicalVolContentsUse);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 uniqueID = le64_to_cpu(lvhd->uniqueID);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001067 *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
1068 cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 if (!(++uniqueID & 0x00000000FFFFFFFFUL))
1070 uniqueID += 16;
1071 lvhd->uniqueID = cpu_to_le64(uniqueID);
Marcin Slusarz6c79e982008-02-08 04:20:30 -08001072 mark_buffer_dirty(bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 }
1074 udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
Marcin Slusarzc0b34432008-02-08 04:20:42 -08001075 if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 mark_inode_dirty(dir);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -07001079 brelse(fibh.ebh);
1080 brelse(fibh.sbh);
Dave Hansend8c76e62006-09-30 23:29:04 -07001081 inc_nlink(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 inode->i_ctime = current_fs_time(inode->i_sb);
1083 mark_inode_dirty(inode);
1084 atomic_inc(&inode->i_count);
1085 d_instantiate(dentry, inode);
1086 unlock_kernel();
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001087
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 return 0;
1089}
1090
1091/* Anybody can rename anything with this: the permission checks are left to the
1092 * higher-level routines.
1093 */
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001094static int udf_rename(struct inode *old_dir, struct dentry *old_dentry,
1095 struct inode *new_dir, struct dentry *new_dentry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096{
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001097 struct inode *old_inode = old_dentry->d_inode;
1098 struct inode *new_inode = new_dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 struct udf_fileident_bh ofibh, nfibh;
Marcin Slusarz4b111112008-02-08 04:20:36 -08001100 struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL;
1101 struct fileIdentDesc ocfi, ncfi;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 struct buffer_head *dir_bh = NULL;
1103 int retval = -ENOENT;
1104 kernel_lb_addr tloc;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -08001105 struct udf_inode_info *old_iinfo = UDF_I(old_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106
1107 lock_kernel();
Marcin Slusarz4b111112008-02-08 04:20:36 -08001108 ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi);
1109 if (ofi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 if (ofibh.sbh != ofibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -07001111 brelse(ofibh.ebh);
1112 brelse(ofibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 }
1114 tloc = lelb_to_cpu(ocfi.icb.extLocation);
1115 if (!ofi || udf_get_lb_pblock(old_dir->i_sb, tloc, 0)
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001116 != old_inode->i_ino)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 goto end_rename;
1118
1119 nfi = udf_find_entry(new_dir, new_dentry, &nfibh, &ncfi);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001120 if (nfi) {
1121 if (!new_inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 if (nfibh.sbh != nfibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -07001123 brelse(nfibh.ebh);
1124 brelse(nfibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 nfi = NULL;
1126 }
1127 }
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001128 if (S_ISDIR(old_inode->i_mode)) {
Marcin Slusarz7f3fbd02008-02-08 04:20:49 -08001129 int offset = udf_ext0_offset(old_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001131 if (new_inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 retval = -ENOTEMPTY;
1133 if (!empty_dir(new_inode))
1134 goto end_rename;
1135 }
1136 retval = -EIO;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -08001137 if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
Marcin Slusarz4b111112008-02-08 04:20:36 -08001138 dir_fi = udf_get_fileident(
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -08001139 old_iinfo->i_ext.i_data -
1140 (old_iinfo->i_efe ?
Marcin Slusarz4b111112008-02-08 04:20:36 -08001141 sizeof(struct extendedFileEntry) :
1142 sizeof(struct fileEntry)),
1143 old_inode->i_sb->s_blocksize, &offset);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001144 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 dir_bh = udf_bread(old_inode, 0, 0, &retval);
1146 if (!dir_bh)
1147 goto end_rename;
Marcin Slusarz4b111112008-02-08 04:20:36 -08001148 dir_fi = udf_get_fileident(dir_bh->b_data,
1149 old_inode->i_sb->s_blocksize, &offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 }
1151 if (!dir_fi)
1152 goto end_rename;
1153 tloc = lelb_to_cpu(dir_fi->icb.extLocation);
Marcin Slusarz4b111112008-02-08 04:20:36 -08001154 if (udf_get_lb_pblock(old_inode->i_sb, tloc, 0) !=
1155 old_dir->i_ino)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 goto end_rename;
1157
1158 retval = -EMLINK;
Marcin Slusarz4b111112008-02-08 04:20:36 -08001159 if (!new_inode &&
1160 new_dir->i_nlink >=
1161 (256 << sizeof(new_dir->i_nlink)) - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 goto end_rename;
1163 }
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001164 if (!nfi) {
Marcin Slusarz4b111112008-02-08 04:20:36 -08001165 nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi,
1166 &retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 if (!nfi)
1168 goto end_rename;
1169 }
1170
1171 /*
1172 * Like most other Unix systems, set the ctime for inodes on a
1173 * rename.
1174 */
1175 old_inode->i_ctime = current_fs_time(old_inode->i_sb);
1176 mark_inode_dirty(old_inode);
1177
1178 /*
1179 * ok, that's it
1180 */
1181 ncfi.fileVersionNum = ocfi.fileVersionNum;
1182 ncfi.fileCharacteristics = ocfi.fileCharacteristics;
1183 memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(long_ad));
1184 udf_write_fi(new_dir, &ncfi, nfi, &nfibh, NULL, NULL);
1185
1186 /* The old fid may have moved - find it again */
1187 ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi);
1188 udf_delete_entry(old_dir, ofi, &ofibh, &ocfi);
1189
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001190 if (new_inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 new_inode->i_ctime = current_fs_time(new_inode->i_sb);
Dave Hansen9a53c3a2006-09-30 23:29:03 -07001192 inode_dec_link_count(new_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 }
1194 old_dir->i_ctime = old_dir->i_mtime = current_fs_time(old_dir->i_sb);
1195 mark_inode_dirty(old_dir);
1196
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001197 if (dir_fi) {
Marcin Slusarzc0b34432008-02-08 04:20:42 -08001198 dir_fi->icb.extLocation = cpu_to_lelb(UDF_I(new_dir)->i_location);
Marcin Slusarz4b111112008-02-08 04:20:36 -08001199 udf_update_tag((char *)dir_fi,
1200 (sizeof(struct fileIdentDesc) +
1201 le16_to_cpu(dir_fi->lengthOfImpUse) + 3) & ~3);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -08001202 if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 mark_inode_dirty(old_inode);
Marcin Slusarz4b111112008-02-08 04:20:36 -08001204 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 mark_buffer_dirty_inode(dir_bh, old_inode);
Marcin Slusarz4b111112008-02-08 04:20:36 -08001206
Dave Hansen9a53c3a2006-09-30 23:29:03 -07001207 inode_dec_link_count(old_dir);
Marcin Slusarz4b111112008-02-08 04:20:36 -08001208 if (new_inode)
Dave Hansen9a53c3a2006-09-30 23:29:03 -07001209 inode_dec_link_count(new_inode);
Marcin Slusarz4b111112008-02-08 04:20:36 -08001210 else {
Dave Hansend8c76e62006-09-30 23:29:04 -07001211 inc_nlink(new_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 mark_inode_dirty(new_dir);
1213 }
1214 }
1215
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001216 if (ofi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 if (ofibh.sbh != ofibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -07001218 brelse(ofibh.ebh);
1219 brelse(ofibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 }
1221
1222 retval = 0;
1223
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001224end_rename:
Jan Kara3bf25cb2007-05-08 00:35:16 -07001225 brelse(dir_bh);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001226 if (nfi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 if (nfibh.sbh != nfibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -07001228 brelse(nfibh.ebh);
1229 brelse(nfibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 }
1231 unlock_kernel();
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001232
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 return retval;
1234}
1235
Arjan van de Venc5ef1c42007-02-12 00:55:40 -08001236const struct inode_operations udf_dir_inode_operations = {
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001237 .lookup = udf_lookup,
1238 .create = udf_create,
1239 .link = udf_link,
1240 .unlink = udf_unlink,
1241 .symlink = udf_symlink,
1242 .mkdir = udf_mkdir,
1243 .rmdir = udf_rmdir,
1244 .mknod = udf_mknod,
1245 .rename = udf_rename,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246};