1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Copyright (C) 2001 Tacit Networks, Inc.
5  *    Author: Shirish H. Phatak <shirish@tacitnetworks.com>
6  *
7  *   This file is part of InterMezzo, http://www.inter-mezzo.org.
8  *
9  *   InterMezzo is free software; you can redistribute it and/or
10  *   modify it under the terms of version 2 of the GNU General Public
11  *   License as published by the Free Software Foundation.
12  *
13  *   InterMezzo is distributed in the hope that it will be useful,
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *   GNU General Public License for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with InterMezzo; if not, write to the Free Software
20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  * Extended attribute handling for presto.
23  */
24 
25 #define __NO_VERSION__
26 #include <linux/module.h>
27 #include <linux/kernel.h>
28 #include <linux/mm.h>
29 #include <linux/string.h>
30 #include <linux/stat.h>
31 #include <linux/errno.h>
32 #include <linux/locks.h>
33 #include <linux/unistd.h>
34 
35 #include <asm/system.h>
36 #include <asm/uaccess.h>
37 
38 #include <linux/fs.h>
39 #include <linux/stat.h>
40 #include <linux/errno.h>
41 #include <linux/locks.h>
42 #include <linux/string.h>
43 #include <asm/uaccess.h>
44 #include <linux/slab.h>
45 #include <linux/vmalloc.h>
46 #include <asm/segment.h>
47 #include <linux/smp_lock.h>
48 
49 #include <linux/intermezzo_fs.h>
50 #include <linux/intermezzo_psdev.h>
51 
52 #ifdef CONFIG_FS_EXT_ATTR
53 #include <linux/ext_attr.h>
54 
55 extern inline void presto_debug_fail_blkdev(struct presto_file_set *fset,
56                                             unsigned long value);
57 
58 
59 /* VFS interface */
60 /* XXX! Fixme test for user defined attributes */
presto_set_ext_attr(struct inode * inode,const char * name,void * buffer,size_t buffer_len,int flags)61 int presto_set_ext_attr(struct inode *inode,
62                         const char *name, void *buffer,
63                         size_t buffer_len, int flags)
64 {
65         int error;
66         struct presto_cache *cache;
67         struct presto_file_set *fset;
68         struct lento_vfs_context info;
69         struct dentry *dentry;
70         int minor = presto_i2m(inode);
71         char *buf = NULL;
72 
73         ENTRY;
74         if (minor < 0) {
75                 EXIT;
76                 return -1;
77         }
78 
79         if ( ISLENTO(minor) ) {
80                 EXIT;
81                 return -EINVAL;
82         }
83 
84         /* BAD...vfs should really pass down the dentry to use, especially
85          * since every other operation in iops does. But for now
86          * we do a reverse mapping from inode to the first dentry
87          */
88         if (list_empty(&inode->i_dentry)) {
89                 CERROR("No alias for inode %d\n", (int) inode->i_ino);
90                 EXIT;
91                 return -EINVAL;
92         }
93 
94         dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
95 
96         error = presto_prep(dentry, &cache, &fset);
97         if ( error ) {
98                 EXIT;
99                 return error;
100         }
101 
102         if ((buffer != NULL) && (buffer_len != 0)) {
103             /* If buffer is a user space pointer copy it to kernel space
104             * and reset the flag. We do this since the journal functions need
105             * access to the contents of the buffer, and the file system
106             * does not care. When we actually invoke the function, we remove
107             * the EXT_ATTR_FLAG_USER flag.
108             *
109             * XXX:Check if the "fs does not care" assertion is always true -SHP
110             * (works for ext3)
111             */
112             if (flags & EXT_ATTR_FLAG_USER) {
113                 PRESTO_ALLOC(buf, buffer_len);
114                 if (!buf) {
115                         CERROR("InterMezzo: out of memory!!!\n");
116                         return -ENOMEM;
117                 }
118                 error = copy_from_user(buf, buffer, buffer_len);
119                 if (error)
120                         return -EFAULT;
121             } else
122                 buf = buffer;
123         } else
124                 buf = buffer;
125 
126         if ( presto_get_permit(inode) < 0 ) {
127                 EXIT;
128                 if (buffer_len && (flags & EXT_ATTR_FLAG_USER))
129                         PRESTO_FREE(buf, buffer_len);
130                 return -EROFS;
131         }
132 
133         /* Simulate presto_setup_info */
134         memset(&info, 0, sizeof(info));
135         /* For now redundant..but we keep it around just in case */
136         info.flags = LENTO_FL_IGNORE_TIME;
137         if (!ISLENTO(cache->cache_psdev->uc_minor))
138             info.flags |= LENTO_FL_KML;
139 
140         /* We pass in the kernel space pointer and reset the
141          * EXT_ATTR_FLAG_USER flag.
142          * See comments above.
143          */
144         /* Note that mode is already set by VFS so we send in a NULL */
145         error = presto_do_set_ext_attr(fset, dentry, name, buf,
146                                        buffer_len, flags & ~EXT_ATTR_FLAG_USER,
147                                        NULL, &info);
148         presto_put_permit(inode);
149 
150         if (buffer_len && (flags & EXT_ATTR_FLAG_USER))
151                 PRESTO_FREE(buf, buffer_len);
152         EXIT;
153         return error;
154 }
155 
156 /* Lento Interface */
157 /* XXX: ignore flags? We should be forcing these operations through? -SHP*/
lento_set_ext_attr(const char * path,const char * name,void * buffer,size_t buffer_len,int flags,mode_t mode,struct lento_vfs_context * info)158 int lento_set_ext_attr(const char *path, const char *name,
159                        void *buffer, size_t buffer_len, int flags, mode_t mode,
160                        struct lento_vfs_context *info)
161 {
162         int error;
163         char * pathname;
164         struct nameidata nd;
165         struct dentry *dentry;
166         struct presto_file_set *fset;
167 
168         ENTRY;
169         lock_kernel();
170 
171         pathname=getname(path);
172         error = PTR_ERR(pathname);
173         if (IS_ERR(pathname)) {
174                 EXIT;
175                 goto exit;
176         }
177 
178         /* Note that ext_attrs apply to both files and directories..*/
179         error=presto_walk(pathname,&nd);
180         if (error)
181 		goto exit;
182         dentry = nd.dentry;
183 
184         fset = presto_fset(dentry);
185         error = -EINVAL;
186         if ( !fset ) {
187                 CERROR("No fileset!\n");
188                 EXIT;
189                 goto exit_dentry;
190         }
191 
192         if (buffer==NULL) buffer_len=0;
193 
194         error = presto_do_set_ext_attr(fset, dentry, name, buffer,
195                                        buffer_len, flags, &mode, info);
196 exit_dentry:
197         path_release(&nd);
198 exit_path:
199         putname(pathname);
200 exit:
201         unlock_kernel();
202         return error;
203 }
204 
205 #endif /*CONFIG_FS_EXT_ATTR*/
206