Blame view

fs/adfs/inode.c 9.4 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
  /*
   *  linux/fs/adfs/inode.c
   *
   *  Copyright (C) 1997-1999 Russell King
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
  #include <linux/buffer_head.h>
a9185b41a   Christoph Hellwig   pass writeback_co...
8
  #include <linux/writeback.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
12
13
14
15
16
17
18
  #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
19
20
21
  	if (!create) {
  		if (block >= inode->i_blocks)
  			goto abort_toobig;
25e5d4df3   Russell King   fs/adfs: mostly d...
22
23
  		block = __adfs_block_map(inode->i_sb, ADFS_I(inode)->indaddr,
  					 block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
28
29
  		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
30
31
32
33
34
35
36
37
38
39
40
41
42
  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);
  }
622951838   Marco Stornelli   adfs: drop vmtrun...
43
44
45
46
47
  static void adfs_write_failed(struct address_space *mapping, loff_t to)
  {
  	struct inode *inode = mapping->host;
  
  	if (to > inode->i_size)
7caef2676   Kirill A. Shutemov   truncate: drop 'o...
48
  		truncate_pagecache(inode, inode->i_size);
622951838   Marco Stornelli   adfs: drop vmtrun...
49
  }
b4585729f   Nick Piggin   fs: adfs convert ...
50
51
52
  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
53
  {
282dc1788   Christoph Hellwig   get rid of cont_w...
54
  	int ret;
b4585729f   Nick Piggin   fs: adfs convert ...
55
  	*pagep = NULL;
282dc1788   Christoph Hellwig   get rid of cont_w...
56
  	ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
b4585729f   Nick Piggin   fs: adfs convert ...
57
58
  				adfs_get_block,
  				&ADFS_I(mapping->host)->mmu_private);
622951838   Marco Stornelli   adfs: drop vmtrun...
59
60
  	if (unlikely(ret))
  		adfs_write_failed(mapping, pos + len);
282dc1788   Christoph Hellwig   get rid of cont_w...
61
62
  
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
66
67
68
  }
  
  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...
69
  static const struct address_space_operations adfs_aops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
  	.readpage	= adfs_readpage,
  	.writepage	= adfs_writepage,
b4585729f   Nick Piggin   fs: adfs convert ...
72
73
  	.write_begin	= adfs_write_begin,
  	.write_end	= generic_write_end,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
  	.bmap		= _adfs_bmap
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
78
79
80
81
  /*
   * 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...
82
  	unsigned int attr = ADFS_I(inode)->attr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
86
87
88
89
  	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;
  	}
b4ed8f75c   Russell King   fs/adfs: add time...
90
  	switch (adfs_filetype(ADFS_I(inode)->loadaddr)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  	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.
   */
81916245c   Russell King   fs/adfs: inode: f...
122
123
  static int adfs_mode2atts(struct super_block *sb, struct inode *inode,
  			  umode_t ia_mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
  {
81916245c   Russell King   fs/adfs: inode: f...
125
  	struct adfs_sb_info *asb = ADFS_SB(sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
  	umode_t mode;
  	int attr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
131
  
  	/* FIXME: should we be able to alter a link? */
  	if (S_ISLNK(inode->i_mode))
  		return ADFS_I(inode)->attr;
81916245c   Russell King   fs/adfs: inode: f...
132
  	/* Directories do not have read/write permissions on the media */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
  	if (S_ISDIR(inode->i_mode))
81916245c   Russell King   fs/adfs: inode: f...
134
  		return ADFS_NDA_DIRECTORY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135

81916245c   Russell King   fs/adfs: inode: f...
136
137
  	attr = 0;
  	mode = ia_mode & asb->s_owner_mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
140
141
  	if (mode & S_IRUGO)
  		attr |= ADFS_NDA_OWNER_READ;
  	if (mode & S_IWUGO)
  		attr |= ADFS_NDA_OWNER_WRITE;
81916245c   Russell King   fs/adfs: inode: f...
142
  	mode = ia_mode & asb->s_other_mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
145
146
147
148
149
150
  	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;
  }
eeeb9dd98   Russell King   fs/adfs: inode: u...
151
  static const s64 nsec_unix_epoch_diff_risc_os_epoch = 2208988800000000000LL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
153
  /*
   * Convert an ADFS time to Unix time.  ADFS has a 40-bit centi-second time
7a9730af9   Stuart Swales   adfs: improve tim...
154
155
   * 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
156
157
   */
  static void
d9edcbc42   Arnd Bergmann   adfs: use timespe...
158
  adfs_adfs2unix_time(struct timespec64 *tv, struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
160
  {
  	unsigned int high, low;
7a9730af9   Stuart Swales   adfs: improve tim...
161
162
163
  	/* 01 Jan 1970 00:00:00 (Unix epoch) as nanoseconds since
  	 * 01 Jan 1900 00:00:00 (RISC OS epoch)
  	 */
7a9730af9   Stuart Swales   adfs: improve tim...
164
  	s64 nsec;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165

b4ed8f75c   Russell King   fs/adfs: add time...
166
  	if (!adfs_inode_is_stamped(inode))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
  		goto cur_time;
7a9730af9   Stuart Swales   adfs: improve tim...
168
169
  	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
170

7a9730af9   Stuart Swales   adfs: improve tim...
171
172
173
174
  	/* 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
175
176
  
  	/* Files dated pre  01 Jan 1970 00:00:00. */
7a9730af9   Stuart Swales   adfs: improve tim...
177
  	if (nsec < nsec_unix_epoch_diff_risc_os_epoch)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
  		goto too_early;
7a9730af9   Stuart Swales   adfs: improve tim...
179
180
  	/* convert from RISC OS to Unix epoch */
  	nsec -= nsec_unix_epoch_diff_risc_os_epoch;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181

d9edcbc42   Arnd Bergmann   adfs: use timespe...
182
  	*tv = ns_to_timespec64(nsec);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
185
  	return;
  
   cur_time:
d9edcbc42   Arnd Bergmann   adfs: use timespe...
186
  	*tv = current_time(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
189
190
191
  	return;
  
   too_early:
  	tv->tv_sec = tv->tv_nsec = 0;
  	return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
  }
eeeb9dd98   Russell King   fs/adfs: inode: u...
193
194
195
  /* Convert an Unix time to ADFS time for an entry that is already stamped. */
  static void adfs_unix2adfs_time(struct inode *inode,
  				const struct timespec64 *ts)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
  {
eeeb9dd98   Russell King   fs/adfs: inode: u...
197
  	s64 cs, nsec = timespec64_to_ns(ts);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198

eeeb9dd98   Russell King   fs/adfs: inode: u...
199
200
  	/* convert from Unix to RISC OS epoch */
  	nsec += nsec_unix_epoch_diff_risc_os_epoch;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201

eeeb9dd98   Russell King   fs/adfs: inode: u...
202
203
204
205
206
207
208
209
  	/* convert from nanoseconds to centiseconds */
  	cs = div_s64(nsec, 10000000);
  
  	cs = clamp_t(s64, cs, 0, 0xffffffffff);
  
  	ADFS_I(inode)->loadaddr &= ~0xff;
  	ADFS_I(inode)->loadaddr |= (cs >> 32) & 0xff;
  	ADFS_I(inode)->execaddr = cs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  }
  
  /*
   * 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;
5ed70bb47   Russell King   fs/adfs: clean up...
235
  	inode->i_ino	 = obj->indaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
  	inode->i_size	 = obj->size;
bfe868486   Miklos Szeredi   filesystems: add ...
237
  	set_nlink(inode, 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
240
241
242
243
244
245
246
247
  	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;
25e5d4df3   Russell King   fs/adfs: mostly d...
248
  	ADFS_I(inode)->indaddr   = obj->indaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
250
251
  	ADFS_I(inode)->loadaddr  = obj->loadaddr;
  	ADFS_I(inode)->execaddr  = obj->execaddr;
  	ADFS_I(inode)->attr      = obj->attr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
  
  	inode->i_mode	 = adfs_atts2mode(sb, inode);
d9edcbc42   Arnd Bergmann   adfs: use timespe...
254
  	adfs_adfs2unix_time(&inode->i_mtime, inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
258
259
260
261
262
263
264
265
266
  	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;
  	}
d8e78da86   Al Viro   adfs: don't put i...
267
  	inode_fake_hash(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
269
270
271
272
273
274
275
276
277
278
279
280
  
  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)
  {
2b0143b5c   David Howells   VFS: normal files...
281
  	struct inode *inode = d_inode(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
285
  	struct super_block *sb = inode->i_sb;
  	unsigned int ia_valid = attr->ia_valid;
  	int error;
  	
31051c85b   Jan Kara   fs: Give dentry t...
286
  	error = setattr_prepare(dentry, attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
290
291
  
  	/*
  	 * we can't change the UID or GID of any file -
  	 * we have a global UID/GID in the superblock
  	 */
c010d1ff4   Eric W. Biederman   userns: Convert a...
292
293
  	if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, ADFS_SB(sb)->s_uid)) ||
  	    (ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, ADFS_SB(sb)->s_gid)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
296
297
  		error = -EPERM;
  
  	if (error)
  		goto out;
15c6fd978   npiggin@suse.de   kill spurious ref...
298
  	/* XXX: this is missing some actual on-disk truncation.. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
  	if (ia_valid & ATTR_SIZE)
2c27c65ed   Christoph Hellwig   check ATTR_SIZE c...
300
  		truncate_setsize(inode, attr->ia_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301

eeeb9dd98   Russell King   fs/adfs: inode: u...
302
303
304
  	if (ia_valid & ATTR_MTIME && adfs_inode_is_stamped(inode)) {
  		adfs_unix2adfs_time(inode, &attr->ia_mtime);
  		adfs_adfs2unix_time(&inode->i_mtime, inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
  	}
eeeb9dd98   Russell King   fs/adfs: inode: u...
306

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
309
310
311
312
313
314
315
  	/*
  	 * 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) {
81916245c   Russell King   fs/adfs: inode: f...
316
  		ADFS_I(inode)->attr = adfs_mode2atts(sb, inode, attr->ia_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
318
319
320
321
322
323
324
325
326
  		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
327
328
329
330
331
332
333
334
  	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...
335
  int adfs_write_inode(struct inode *inode, struct writeback_control *wbc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
338
339
  {
  	struct super_block *sb = inode->i_sb;
  	struct object_info obj;
  	int ret;
25e5d4df3   Russell King   fs/adfs: mostly d...
340
  	obj.indaddr	= ADFS_I(inode)->indaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
342
343
344
345
346
  	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...
347
  	ret = adfs_dir_update(sb, &obj, wbc->sync_mode == WB_SYNC_ALL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
349
  	return ret;
  }