Whamcloud - gitweb
current branches now use lnet from HEAD
[fs/lustre-release.git] / lustre / snapfs / symlink.c
1 /*
2  *  fs/snap/snap.c
3  *
4  *  A snap shot file system.
5  *
6  */
7 #define DEBUG_SUBSYSTEM S_SNAP
8
9 #include <linux/kmod.h>
10 #include <linux/init.h>
11 #include <linux/fs.h>
12 #include <linux/slab.h>
13 #include <linux/string.h>
14 #include <linux/jbd.h>
15 #include <linux/ext3_fs.h>
16 #include <linux/snap.h>
17
18 #include "snapfs_internal.h" 
19
20
21 static inline int inode_has_ea(struct inode *inode)
22 {               
23         return (inode->u.ext2_i.i_file_acl != 0); 
24 }               
25
26 static int currentfs_readlink(struct dentry * dentry, char * buffer, int buflen)
27 {
28         struct snap_cache *cache;
29         int rc;
30         struct inode_operations *iops;
31         struct inode * inode = dentry->d_inode;
32         int bpib = inode->i_sb->s_blocksize >> 9;
33         __u32 save_i_blocks;
34
35         ENTRY;
36
37         cache = snap_find_cache(inode->i_dev);
38         if ( !cache ) { 
39                 EXIT;
40                 return -EINVAL;
41         }
42
43         iops = filter_c2csiops(cache->cache_filter); 
44         if (!iops ||
45             !iops->readlink) {
46                 rc = -EINVAL;
47                 goto exit;
48         }
49
50         save_i_blocks = inode->i_blocks;
51         /* If this link has ea and its i_blocks is ea's block, 
52          * then we should treate it as a fast symlink 
53          */
54         if( inode_has_ea(inode) && inode->i_blocks == bpib ) {
55                 inode->i_blocks = 0; 
56         }
57         rc = iops->readlink(dentry, buffer, buflen);
58         
59         if( inode->i_blocks != save_i_blocks ){
60                 inode->i_blocks = save_i_blocks;
61                 mark_inode_dirty(inode);
62         }
63         
64 exit:
65         EXIT;
66         return rc;
67 }
68
69 static int cat_str_ahead(char *buf, int pos, const char* str)
70 {
71         int len = strlen(str);
72
73         if( pos - len -1 < 0 )
74                 return pos;
75
76         buf[--pos] = '/';
77         memcpy(&buf[pos-len], str, len);
78         return pos-len;
79 }
80
81 /*
82  * Adjust the following path if we are under dotsnap (skip .snap/clonexx...)
83  * in following two case, we just return null and let caller do
84  * the normal follow_link:
85  * (1) we are not lies in .snap
86  * (2) we are already in the root's .snap
87  */
88 static int dotsnap_follow_link(struct dentry *dentry,
89                                            struct nameidata *nd)
90 {
91         struct super_block *sb = dentry->d_inode->i_sb;
92         struct dentry *de = dentry, *de_save1=NULL, *de_save2=NULL;
93         char *buf = NULL;
94         int pos = D_MAXLEN, rc;
95
96         SNAP_ALLOC(buf, D_MAXLEN);
97         if( !buf )
98                 RETURN(-ENOMEM);
99
100         /*
101          * iterate upward to construct the path
102          */
103         do {
104                 if( de_save2 )
105                         pos = cat_str_ahead(buf, pos, de_save2->d_name.name);
106
107                 if ( de->d_inode && de->d_inode->i_ino & 0xF0000000 )
108                         goto lookup;
109
110                 de_save2 = de_save1;
111                 de_save1 = de;
112                 de = de->d_parent;
113         } while (de->d_parent != de);
114
115         /* we are not under dotsnap */
116         goto exit; 
117
118 lookup:
119         /* See if we already under root's .snap */
120         de = de->d_parent;
121         if( de == sb->s_root )
122                 goto exit;
123
124         while( (de->d_parent != de) && (de != sb->s_root) ){
125                 pos = cat_str_ahead(buf, pos, de->d_name.name);
126                 de = de->d_parent;
127         }
128         if( de_save1 )
129                 pos = cat_str_ahead(buf, pos, de_save1->d_name.name);
130
131         pos = cat_str_ahead(buf, pos, ".snap");
132         buf[D_MAXLEN-1] = 0;
133         CDEBUG(D_SNAP, "constructed path: %s\n", &buf[pos]);
134
135         /* FIXME lookup_dentry will never return NULL ?? */
136 #if 0
137         rc = lookup_dentry(&buf[pos], dget(sb->s_root), follow);
138         if( !rc ){
139                 rc = ERR_PTR(-ENOENT);
140                 CDEBUG(D_SNAP, "lookup_dentry return NULL~!@#$^&*\n");
141         }
142 #else
143         if (path_init(&buf[pos], LOOKUP_FOLLOW, nd)) {
144                 rc = path_walk(&buf[pos], nd);
145                 if (rc)
146                         GOTO(exit, rc);
147         } 
148 #endif
149 exit:
150         SNAP_FREE(buf, D_MAXLEN);
151         return rc;
152 }
153
154 static int currentfs_follow_link (struct dentry *dentry, struct nameidata *nd)
155 {
156         struct snap_cache *cache;
157         struct inode_operations *iops;
158         struct inode * inode = dentry->d_inode;
159         int bpib = inode->i_sb->s_blocksize >> 9;
160         __u32 save_i_blocks;
161         int     rc;
162         ENTRY;
163
164         cache = snap_find_cache(inode->i_dev);
165         if ( !cache ) { 
166                 RETURN(-EINVAL);
167         }
168
169         iops = filter_c2csiops(cache->cache_filter); 
170         if (!iops ||
171             !iops->follow_link) {
172                 GOTO(exit, rc = -EINVAL);
173         }
174
175         if( currentfs_is_under_dotsnap(dentry) ){
176                 rc = dotsnap_follow_link(dentry, nd);
177                 if( rc )
178                         goto exit;
179         }
180
181         save_i_blocks = inode->i_blocks;
182         /* If this link has ea and its i_blocks is ea's block, 
183          * then we should treate it as a fast symlink 
184          */
185         if( inode_has_ea(inode) && inode->i_blocks == bpib ) {
186                 inode->i_blocks = 0; 
187         }
188         rc = iops->follow_link(dentry, nd);
189         
190         if( inode->i_blocks != save_i_blocks ){
191                 inode->i_blocks = save_i_blocks;
192                 mark_inode_dirty(inode);
193         }
194         
195 exit:
196         RETURN(rc);
197 }
198
199 struct inode_operations currentfs_sym_iops = {
200         readlink:       currentfs_readlink,
201         follow_link:    currentfs_follow_link,
202 };
203
204 struct file_operations currentfs_sym_fops = {
205         ioctl:          NULL,
206 };