Whamcloud - gitweb
update snapfs 1)add mntget and mntput for snap_current when mount snap_clone 2)get...
[fs/lustre-release.git] / lustre / snapfs / file.c
1 /*
2  * file.c
3  */
4
5 #define DEBUG_SUBSYSTEM S_SNAP
6
7 #include <linux/module.h>
8 #include <linux/kernel.h>
9 #include <linux/string.h>
10 #include <linux/slab.h>
11 #include <linux/stat.h>
12 #include <linux/unistd.h>
13 #include <linux/jbd.h>
14 #include <linux/ext3_fs.h>
15 #include <linux/snap.h>
16
17 #include "snapfs_internal.h" 
18
19 /* instantiate a file handle to the cache file */
20 static void currentfs_prepare_snapfile(struct inode *inode,
21                                      struct file *clone_file, 
22                                      struct inode *cache_inode,
23                                      struct file *cache_file,
24                                      struct dentry *cache_dentry)
25 {
26         cache_file->f_pos = clone_file->f_pos;
27         cache_file->f_mode = clone_file->f_mode;
28         cache_file->f_flags = clone_file->f_flags;
29         cache_file->f_count  = clone_file->f_count;
30         cache_file->f_owner  = clone_file->f_owner;
31         cache_file->f_dentry = cache_dentry;
32         cache_file->f_dentry->d_inode = cache_inode;
33 }
34
35 /* update the currentfs file struct after IO in cache file */
36 static void currentfs_restore_snapfile(struct inode *cache_inode,
37                                    struct file *cache_file, 
38                                    struct inode *clone_inode,
39                                    struct file *clone_file)
40 {
41         cache_file->f_pos = clone_file->f_pos;
42 }
43
44 static ssize_t currentfs_write (struct file *filp, const char *buf, 
45                                 size_t count, loff_t *ppos)
46 {
47         struct snap_cache *cache;
48         struct inode *inode = filp->f_dentry->d_inode;
49         struct file_operations *fops;
50         long block[2]={-1,-1};
51         struct snap_table *table;
52         struct inode *cache_inode = NULL;
53         struct snapshot_operations *snapops;
54         int slot = 0, index = 0, result = 0;
55         long mask, i;
56         ssize_t rc;
57         loff_t pos;
58   
59         ENTRY;
60
61         if (currentfs_is_under_dotsnap(filp->f_dentry)) 
62                 RETURN(-ENOSPC);
63
64         cache = snap_find_cache(inode->i_dev);
65         if ( !cache ) 
66                 RETURN(-EINVAL);
67
68         if ( snap_needs_cow(inode) != -1 ) {
69                 CDEBUG(D_SNAP, "snap_needs_cow for ino %lu \n",inode->i_ino);
70                 snap_do_cow(inode, filp->f_dentry->d_parent->d_inode->i_ino, 0);
71         }
72
73         fops = filter_c2cffops(cache->cache_filter); 
74         if (!fops || !fops->write) 
75                 RETURN(-EINVAL);
76
77         if (filp->f_flags & O_APPEND)
78                 pos = inode->i_size;
79         else {
80                 pos = *ppos;
81                 if (pos != *ppos)
82                         RETURN(-EINVAL);
83         }
84
85         /*
86          * we only need to copy back the first and last blocks
87          */
88         mask = inode->i_sb->s_blocksize-1;
89         if( pos & mask )
90                 block[0] = pos >> inode->i_sb->s_blocksize_bits;
91         pos += count - 1;
92         if( (pos+1) &  mask )
93                 block[1] = pos >> inode->i_sb->s_blocksize_bits;
94         if( block[0] == block[1] )
95                 block[1] = -1;
96         
97         snapops = filter_c2csnapops(cache->cache_filter);
98
99         for (i = 0; i < 2; i++) {
100                 if (block[i] == -1) 
101                         continue;
102                 table = &snap_tables[cache->cache_snap_tableno];
103                 /*Find the nearest block in snaptable and copy back it*/
104                 for (slot = table->tbl_count - 1; slot >= 1; slot--) {
105                         cache_inode = NULL;
106                         index = table->snap_items[slot].index;
107                         cache_inode = snap_get_indirect(inode, NULL, index);
108
109                         if (!cache_inode)  continue;
110
111                         CDEBUG(D_SNAP, "find cache_ino %lu\n", cache_inode->i_ino);
112                 
113                         if (snapops && snapops->copy_block) {
114                                 result = snapops->copy_block(inode, cache_inode, block[i]);
115                                 if (result == 1) {
116                                         CDEBUG(D_SNAP, "copy block %lu back from ind %lu to %lu\n", 
117                                                block[i], cache_inode->i_ino, inode->i_ino);
118                                         iput(cache_inode);
119                                         result = 0;
120                                         break;
121                                 }
122                                 if (result < 0) {
123                                         iput(cache_inode);
124                                         rc = result;
125                                         goto exit;
126                                 }
127                         }
128                         iput(cache_inode);
129                 }
130         }
131         rc = fops->write(filp, buf, count, ppos);
132 exit:
133         RETURN(rc);
134 }
135
136 static int currentfs_readpage(struct file *file, struct page *page)
137 {
138         int result = 0;
139         struct inode *inode = file->f_dentry->d_inode;
140         unsigned long ind_ino = inode->i_ino;
141         struct inode *pri_inode = NULL;
142         struct inode *cache_inode = NULL;
143         struct file open_file;
144         struct dentry open_dentry ;
145         struct address_space_operations *c_aops;
146         struct snap_cache *cache;
147         long block;
148         struct snap_table *table;
149         int slot = 0;
150         int index = 0;
151         int search_older = 0;
152
153         ENTRY;
154
155         cache = snap_find_cache(inode->i_dev);
156         if ( !cache ) { 
157                 RETURN(-EINVAL);
158         }
159         
160         c_aops = filter_c2cfaops(cache->cache_filter);
161
162         block = page->index >> inode->i_sb->s_blocksize_bits;
163
164         /* if there is a block in the cache, return the cache readpage */
165         if( inode->i_blocks && c_aops->bmap(inode->i_mapping, block) ) {
166                 CDEBUG(D_SNAP, "block %lu in cache, ino %lu\n", 
167                                 block, inode->i_ino);
168                 result = c_aops->readpage(file, page);
169                 RETURN(result);
170         }
171
172         /*
173          * clonefs_readpage will fill this with primary ino number
174          * we need it to follow the cloned chain of primary inode
175          */
176         if( file->f_dentry->d_fsdata ){
177                 pri_inode = iget(inode->i_sb, (unsigned long)file->f_dentry->d_fsdata);
178                 if( !pri_inode )
179                         RETURN(-EINVAL);
180                 inode = pri_inode;
181                 search_older = 1;
182         }
183
184         table = &snap_tables[cache->cache_snap_tableno];
185
186         for (slot = table->tbl_count - 1; slot >= 1; slot--) {
187                 cache_inode = NULL;
188                 index = table->snap_items[slot].index;
189                 cache_inode = snap_get_indirect(inode, NULL, index);
190
191                 if (!cache_inode )  continue;
192
193                 /* we only want slots between cache_inode to the oldest one */
194                 if(search_older && cache_inode->i_ino == ind_ino )
195                         search_older = 0;
196
197                 if (!search_older && c_aops->bmap(cache_inode->i_mapping, block)) 
198                         break;
199                 iput(cache_inode);
200         }
201         if( pri_inode )
202                 iput(pri_inode);
203
204         if ( !cache_inode )  
205                 RETURN(-EINVAL);
206
207         currentfs_prepare_snapfile(inode, file, cache_inode, &open_file,
208                               &open_dentry);
209
210         down(&cache_inode->i_sem);
211
212         if( c_aops->readpage ) {
213                 CDEBUG(D_SNAP, "block %lu NOT in cache, use redirected ino %lu\n", 
214                        block, cache_inode->i_ino );
215                 result = c_aops->readpage(&open_file, page);
216         }else {
217                 CDEBUG(D_SNAP, "cache ino %lu, readpage is NULL\n", 
218                        cache_inode->i_ino);
219         }
220         up(&cache_inode->i_sem);
221         currentfs_restore_snapfile(inode, file, cache_inode, &open_file);
222         iput(cache_inode);
223         RETURN(result);
224 }
225 struct address_space_operations currentfs_file_aops = {
226         readpage:       currentfs_readpage,
227 };
228                                                                                                                                                                                                      
229 struct file_operations currentfs_file_fops = {
230         write:          currentfs_write,
231 };
232                                                                                                                                                                                                      
233 struct inode_operations currentfs_file_iops = {
234         revalidate:     NULL,
235 };
236