Blame view

fs/adfs/inode.c 9.31 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
  /*
   *  linux/fs/adfs/inode.c
   *
   *  Copyright (C) 1997-1999 Russell King
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
  #include <linux/buffer_head.h>
a9185b41a   Christoph Hellwig   pass writeback_co...
11
  #include <linux/writeback.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
16
17
18
19
20
21
  #include "adfs.h"
  
  /*
   * Lookup/Create a block at offset 'block' into 'inode'.  We currently do
   * not support creation of new blocks, so we return -EIO for this case.
   */
  static int
  adfs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh,
  	       int create)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
25
26
27
28
29
30
31
32
  	if (!create) {
  		if (block >= inode->i_blocks)
  			goto abort_toobig;
  
  		block = __adfs_block_map(inode->i_sb, inode->i_ino, block);
  		if (block)
  			map_bh(bh, inode->i_sb, block);
  		return 0;
  	}
  	/* don't support allocation of blocks yet */
  	return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
38
39
40
41
42
43
44
45
  abort_toobig:
  	return 0;
  }
  
  static int adfs_writepage(struct page *page, struct writeback_control *wbc)
  {
  	return block_write_full_page(page, adfs_get_block, wbc);
  }
  
  static int adfs_readpage(struct file *file, struct page *page)
  {
  	return block_read_full_page(page, adfs_get_block);
  }
b4585729f   Nick Piggin   fs: adfs convert ...
46
47
48
  static int adfs_write_begin(struct file *file, struct address_space *mapping,
  			loff_t pos, unsigned len, unsigned flags,
  			struct page **pagep, void **fsdata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
  {
282dc1788   Christoph Hellwig   get rid of cont_w...
50
  	int ret;
b4585729f   Nick Piggin   fs: adfs convert ...
51
  	*pagep = NULL;
282dc1788   Christoph Hellwig   get rid of cont_w...
52
  	ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
b4585729f   Nick Piggin   fs: adfs convert ...
53
54
  				adfs_get_block,
  				&ADFS_I(mapping->host)->mmu_private);
282dc1788   Christoph Hellwig   get rid of cont_w...
55
56
57
58
59
60
61
  	if (unlikely(ret)) {
  		loff_t isize = mapping->host->i_size;
  		if (pos + len > isize)
  			vmtruncate(mapping->host, isize);
  	}
  
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
65
66
67
  }
  
  static sector_t _adfs_bmap(struct address_space *mapping, sector_t block)
  {
  	return generic_block_bmap(mapping, block, adfs_get_block);
  }
f5e54d6e5   Christoph Hellwig   [PATCH] mark addr...
68
  static const struct address_space_operations adfs_aops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
  	.readpage	= adfs_readpage,
  	.writepage	= adfs_writepage,
b4585729f   Nick Piggin   fs: adfs convert ...
71
72
  	.write_begin	= adfs_write_begin,
  	.write_end	= generic_write_end,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
  	.bmap		= _adfs_bmap
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
79
80
  /*
   * Convert ADFS attributes and filetype to Linux permission.
   */
  static umode_t
  adfs_atts2mode(struct super_block *sb, struct inode *inode)
  {
da23ef054   Stuart Swales   adfs: add hexadec...
81
  	unsigned int attr = ADFS_I(inode)->attr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
84
85
86
87
88
  	umode_t mode, rmask;
  	struct adfs_sb_info *asb = ADFS_SB(sb);
  
  	if (attr & ADFS_NDA_DIRECTORY) {
  		mode = S_IRUGO & asb->s_owner_mask;
  		return S_IFDIR | S_IXUGO | mode;
  	}
da23ef054   Stuart Swales   adfs: add hexadec...
89
  	switch (ADFS_I(inode)->filetype) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  	case 0xfc0:	/* LinkFS */
  		return S_IFLNK|S_IRWXUGO;
  
  	case 0xfe6:	/* UnixExec */
  		rmask = S_IRUGO | S_IXUGO;
  		break;
  
  	default:
  		rmask = S_IRUGO;
  	}
  
  	mode = S_IFREG;
  
  	if (attr & ADFS_NDA_OWNER_READ)
  		mode |= rmask & asb->s_owner_mask;
  
  	if (attr & ADFS_NDA_OWNER_WRITE)
  		mode |= S_IWUGO & asb->s_owner_mask;
  
  	if (attr & ADFS_NDA_PUBLIC_READ)
  		mode |= rmask & asb->s_other_mask;
  
  	if (attr & ADFS_NDA_PUBLIC_WRITE)
  		mode |= S_IWUGO & asb->s_other_mask;
  	return mode;
  }
  
  /*
   * Convert Linux permission to ADFS attribute.  We try to do the reverse
   * of atts2mode, but there is not a 1:1 translation.
   */
  static int
  adfs_mode2atts(struct super_block *sb, struct inode *inode)
  {
  	umode_t mode;
  	int attr;
  	struct adfs_sb_info *asb = ADFS_SB(sb);
  
  	/* FIXME: should we be able to alter a link? */
  	if (S_ISLNK(inode->i_mode))
  		return ADFS_I(inode)->attr;
  
  	if (S_ISDIR(inode->i_mode))
  		attr = ADFS_NDA_DIRECTORY;
  	else
  		attr = 0;
  
  	mode = inode->i_mode & asb->s_owner_mask;
  	if (mode & S_IRUGO)
  		attr |= ADFS_NDA_OWNER_READ;
  	if (mode & S_IWUGO)
  		attr |= ADFS_NDA_OWNER_WRITE;
  
  	mode = inode->i_mode & asb->s_other_mask;
  	mode &= ~asb->s_owner_mask;
  	if (mode & S_IRUGO)
  		attr |= ADFS_NDA_PUBLIC_READ;
  	if (mode & S_IWUGO)
  		attr |= ADFS_NDA_PUBLIC_WRITE;
  
  	return attr;
  }
  
  /*
   * Convert an ADFS time to Unix time.  ADFS has a 40-bit centi-second time
7a9730af9   Stuart Swales   adfs: improve tim...
155
156
   * referenced to 1 Jan 1900 (til 2248) so we need to discard 2208988800 seconds
   * of time to convert from RISC OS epoch to Unix epoch.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
161
   */
  static void
  adfs_adfs2unix_time(struct timespec *tv, struct inode *inode)
  {
  	unsigned int high, low;
7a9730af9   Stuart Swales   adfs: improve tim...
162
163
164
165
166
167
  	/* 01 Jan 1970 00:00:00 (Unix epoch) as nanoseconds since
  	 * 01 Jan 1900 00:00:00 (RISC OS epoch)
  	 */
  	static const s64 nsec_unix_epoch_diff_risc_os_epoch =
  							2208988800000000000LL;
  	s64 nsec;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
  
  	if (ADFS_I(inode)->stamped == 0)
  		goto cur_time;
7a9730af9   Stuart Swales   adfs: improve tim...
171
172
  	high = ADFS_I(inode)->loadaddr & 0xFF; /* top 8 bits of timestamp */
  	low  = ADFS_I(inode)->execaddr;    /* bottom 32 bits of timestamp */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173

7a9730af9   Stuart Swales   adfs: improve tim...
174
175
176
177
  	/* convert 40-bit centi-seconds to 32-bit seconds
  	 * going via nanoseconds to retain precision
  	 */
  	nsec = (((s64) high << 32) | (s64) low) * 10000000; /* cs to ns */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
  
  	/* Files dated pre  01 Jan 1970 00:00:00. */
7a9730af9   Stuart Swales   adfs: improve tim...
180
  	if (nsec < nsec_unix_epoch_diff_risc_os_epoch)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
  		goto too_early;
7a9730af9   Stuart Swales   adfs: improve tim...
182
183
  	/* convert from RISC OS to Unix epoch */
  	nsec -= nsec_unix_epoch_diff_risc_os_epoch;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184

7a9730af9   Stuart Swales   adfs: improve tim...
185
  	*tv = ns_to_timespec(nsec);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
188
  	return;
  
   cur_time:
7a9730af9   Stuart Swales   adfs: improve tim...
189
  	*tv = CURRENT_TIME;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
194
  	return;
  
   too_early:
  	tv->tv_sec = tv->tv_nsec = 0;
  	return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
  }
  
  /*
   * Convert an Unix time to ADFS time.  We only do this if the entry has a
   * time/date stamp already.
   */
  static void
  adfs_unix2adfs_time(struct inode *inode, unsigned int secs)
  {
  	unsigned int high, low;
  
  	if (ADFS_I(inode)->stamped) {
  		/* convert 32-bit seconds to 40-bit centi-seconds */
  		low  = (secs & 255) * 100;
  		high = (secs / 256) * 100 + (low >> 8) + 0x336e996a;
  
  		ADFS_I(inode)->loadaddr = (high >> 24) |
  				(ADFS_I(inode)->loadaddr & ~0xff);
  		ADFS_I(inode)->execaddr = (low & 255) | (high << 8);
  	}
  }
  
  /*
   * Fill in the inode information from the object information.
   *
   * Note that this is an inode-less filesystem, so we can't use the inode
   * number to reference the metadata on the media.  Instead, we use the
   * inode number to hold the object ID, which in turn will tell us where
   * the data is held.  We also save the parent object ID, and with these
   * two, we can locate the metadata.
   *
   * This does mean that we rely on an objects parent remaining the same at
   * all times - we cannot cope with a cross-directory rename (yet).
   */
  struct inode *
  adfs_iget(struct super_block *sb, struct object_info *obj)
  {
  	struct inode *inode;
  
  	inode = new_inode(sb);
  	if (!inode)
  		goto out;
  
  	inode->i_uid	 = ADFS_SB(sb)->s_uid;
  	inode->i_gid	 = ADFS_SB(sb)->s_gid;
  	inode->i_ino	 = obj->file_id;
  	inode->i_size	 = obj->size;
bfe868486   Miklos Szeredi   filesystems: add ...
242
  	set_nlink(inode, 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
245
246
247
248
249
250
251
252
253
254
255
  	inode->i_blocks	 = (inode->i_size + sb->s_blocksize - 1) >>
  			    sb->s_blocksize_bits;
  
  	/*
  	 * we need to save the parent directory ID so that
  	 * write_inode can update the directory information
  	 * for this file.  This will need special handling
  	 * for cross-directory renames.
  	 */
  	ADFS_I(inode)->parent_id = obj->parent_id;
  	ADFS_I(inode)->loadaddr  = obj->loadaddr;
  	ADFS_I(inode)->execaddr  = obj->execaddr;
  	ADFS_I(inode)->attr      = obj->attr;
da23ef054   Stuart Swales   adfs: add hexadec...
256
257
  	ADFS_I(inode)->filetype  = obj->filetype;
  	ADFS_I(inode)->stamped   = ((obj->loadaddr & 0xfff00000) == 0xfff00000);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
  
  	inode->i_mode	 = adfs_atts2mode(sb, inode);
  	adfs_adfs2unix_time(&inode->i_mtime, inode);
  	inode->i_atime = inode->i_mtime;
  	inode->i_ctime = inode->i_mtime;
  
  	if (S_ISDIR(inode->i_mode)) {
  		inode->i_op	= &adfs_dir_inode_operations;
  		inode->i_fop	= &adfs_dir_operations;
  	} else if (S_ISREG(inode->i_mode)) {
  		inode->i_op	= &adfs_file_inode_operations;
  		inode->i_fop	= &adfs_file_operations;
  		inode->i_mapping->a_ops = &adfs_aops;
  		ADFS_I(inode)->mmu_private = inode->i_size;
  	}
  
  	insert_inode_hash(inode);
  
  out:
  	return inode;
  }
  
  /*
   * Validate and convert a changed access mode/time to their ADFS equivalents.
   * adfs_write_inode will actually write the information back to the directory
   * later.
   */
  int
  adfs_notify_change(struct dentry *dentry, struct iattr *attr)
  {
  	struct inode *inode = dentry->d_inode;
  	struct super_block *sb = inode->i_sb;
  	unsigned int ia_valid = attr->ia_valid;
  	int error;
  	
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
294
295
296
297
298
299
300
301
302
303
304
  	error = inode_change_ok(inode, attr);
  
  	/*
  	 * we can't change the UID or GID of any file -
  	 * we have a global UID/GID in the superblock
  	 */
  	if ((ia_valid & ATTR_UID && attr->ia_uid != ADFS_SB(sb)->s_uid) ||
  	    (ia_valid & ATTR_GID && attr->ia_gid != ADFS_SB(sb)->s_gid))
  		error = -EPERM;
  
  	if (error)
  		goto out;
15c6fd978   npiggin@suse.de   kill spurious ref...
305
  	/* XXX: this is missing some actual on-disk truncation.. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  	if (ia_valid & ATTR_SIZE)
2c27c65ed   Christoph Hellwig   check ATTR_SIZE c...
307
  		truncate_setsize(inode, attr->ia_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
  
  	if (ia_valid & ATTR_MTIME) {
  		inode->i_mtime = attr->ia_mtime;
  		adfs_unix2adfs_time(inode, attr->ia_mtime.tv_sec);
  	}
  	/*
  	 * FIXME: should we make these == to i_mtime since we don't
  	 * have the ability to represent them in our filesystem?
  	 */
  	if (ia_valid & ATTR_ATIME)
  		inode->i_atime = attr->ia_atime;
  	if (ia_valid & ATTR_CTIME)
  		inode->i_ctime = attr->ia_ctime;
  	if (ia_valid & ATTR_MODE) {
  		ADFS_I(inode)->attr = adfs_mode2atts(sb, inode);
  		inode->i_mode = adfs_atts2mode(sb, inode);
  	}
  
  	/*
  	 * FIXME: should we be marking this inode dirty even if
  	 * we don't have any metadata to write back?
  	 */
  	if (ia_valid & (ATTR_SIZE | ATTR_MTIME | ATTR_MODE))
  		mark_inode_dirty(inode);
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
335
336
337
338
339
340
  	return error;
  }
  
  /*
   * write an existing inode back to the directory, and therefore the disk.
   * The adfs-specific inode data has already been updated by
   * adfs_notify_change()
   */
a9185b41a   Christoph Hellwig   pass writeback_co...
341
  int adfs_write_inode(struct inode *inode, struct writeback_control *wbc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
343
344
345
  {
  	struct super_block *sb = inode->i_sb;
  	struct object_info obj;
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
347
348
349
350
351
352
  	obj.file_id	= inode->i_ino;
  	obj.name_len	= 0;
  	obj.parent_id	= ADFS_I(inode)->parent_id;
  	obj.loadaddr	= ADFS_I(inode)->loadaddr;
  	obj.execaddr	= ADFS_I(inode)->execaddr;
  	obj.attr	= ADFS_I(inode)->attr;
  	obj.size	= inode->i_size;
a9185b41a   Christoph Hellwig   pass writeback_co...
353
  	ret = adfs_dir_update(sb, &obj, wbc->sync_mode == WB_SYNC_ALL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
  	return ret;
  }