blob: 2cf2ebecb55f30993eda3dd4dac1dca5b2aa3584 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * inode.c
3 *
4 * Copyright (C) 1995, 1996 by Volker Lendecke
5 * Modified for big endian by J.F. Chadima and David S. Miller
6 * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
7 * Modified 1998 Wolfram Pienkoss for NLS
8 * Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info
9 *
10 */
11
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/module.h>
13
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <asm/uaccess.h>
15#include <asm/byteorder.h>
16
17#include <linux/time.h>
18#include <linux/kernel.h>
19#include <linux/mm.h>
20#include <linux/string.h>
21#include <linux/stat.h>
22#include <linux/errno.h>
23#include <linux/file.h>
24#include <linux/fcntl.h>
25#include <linux/slab.h>
26#include <linux/vmalloc.h>
27#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/vfs.h>
Miklos Szeredi564cd132008-02-08 04:21:46 -080029#include <linux/mount.h>
30#include <linux/seq_file.h>
Nick Piggin34286d62011-01-07 17:49:57 +110031#include <linux/namei.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <net/sock.h>
34
Al Viro32c419d2011-01-12 17:37:47 -050035#include "ncp_fs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include "getopt.h"
37
Miklos Szeredi564cd132008-02-08 04:21:46 -080038#define NCP_DEFAULT_FILE_MODE 0600
39#define NCP_DEFAULT_DIR_MODE 0700
40#define NCP_DEFAULT_TIME_OUT 10
41#define NCP_DEFAULT_RETRY_COUNT 20
42
Al Viro94ee8492010-06-07 00:45:56 -040043static void ncp_evict_inode(struct inode *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070044static void ncp_put_super(struct super_block *);
David Howells726c3342006-06-23 02:02:58 -070045static int ncp_statfs(struct dentry *, struct kstatfs *);
Al Viro34c80b12011-12-08 21:32:45 -050046static int ncp_show_options(struct seq_file *, struct dentry *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070047
Christoph Lametere18b8902006-12-06 20:33:20 -080048static struct kmem_cache * ncp_inode_cachep;
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
50static struct inode *ncp_alloc_inode(struct super_block *sb)
51{
52 struct ncp_inode_info *ei;
Christoph Lametere94b1762006-12-06 20:33:17 -080053 ei = (struct ncp_inode_info *)kmem_cache_alloc(ncp_inode_cachep, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 if (!ei)
55 return NULL;
56 return &ei->vfs_inode;
57}
58
Nick Pigginfa0d7e3d2011-01-07 17:49:49 +110059static void ncp_i_callback(struct rcu_head *head)
60{
61 struct inode *inode = container_of(head, struct inode, i_rcu);
Nick Pigginfa0d7e3d2011-01-07 17:49:49 +110062 kmem_cache_free(ncp_inode_cachep, NCP_FINFO(inode));
63}
64
Linus Torvalds1da177e2005-04-16 15:20:36 -070065static void ncp_destroy_inode(struct inode *inode)
66{
Nick Pigginfa0d7e3d2011-01-07 17:49:49 +110067 call_rcu(&inode->i_rcu, ncp_i_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -070068}
69
Alexey Dobriyan51cc5062008-07-25 19:45:34 -070070static void init_once(void *foo)
Linus Torvalds1da177e2005-04-16 15:20:36 -070071{
72 struct ncp_inode_info *ei = (struct ncp_inode_info *) foo;
73
Christoph Lametera35afb82007-05-16 22:10:57 -070074 mutex_init(&ei->open_mutex);
75 inode_init_once(&ei->vfs_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -070076}
Paul Mundt20c2df82007-07-20 10:11:58 +090077
Linus Torvalds1da177e2005-04-16 15:20:36 -070078static int init_inodecache(void)
79{
80 ncp_inode_cachep = kmem_cache_create("ncp_inode_cache",
81 sizeof(struct ncp_inode_info),
Paul Jacksonfffb60f2006-03-24 03:16:06 -080082 0, (SLAB_RECLAIM_ACCOUNT|
83 SLAB_MEM_SPREAD),
Paul Mundt20c2df82007-07-20 10:11:58 +090084 init_once);
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 if (ncp_inode_cachep == NULL)
86 return -ENOMEM;
87 return 0;
88}
89
90static void destroy_inodecache(void)
91{
Kirill A. Shutemov8c0a8532012-09-26 11:33:07 +100092 /*
93 * Make sure all delayed rcu free inodes are flushed before we
94 * destroy cache.
95 */
96 rcu_barrier();
Alexey Dobriyan1a1d92c2006-09-27 01:49:40 -070097 kmem_cache_destroy(ncp_inode_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -070098}
99
100static int ncp_remount(struct super_block *sb, int *flags, char* data)
101{
102 *flags |= MS_NODIRATIME;
103 return 0;
104}
105
Josef 'Jeff' Sipekee9b6d62007-02-12 00:55:41 -0800106static const struct super_operations ncp_sops =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107{
108 .alloc_inode = ncp_alloc_inode,
109 .destroy_inode = ncp_destroy_inode,
110 .drop_inode = generic_delete_inode,
Al Viro94ee8492010-06-07 00:45:56 -0400111 .evict_inode = ncp_evict_inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 .put_super = ncp_put_super,
113 .statfs = ncp_statfs,
114 .remount_fs = ncp_remount,
Miklos Szeredi564cd132008-02-08 04:21:46 -0800115 .show_options = ncp_show_options,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116};
117
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118/*
119 * Fill in the ncpfs-specific information in the inode.
120 */
121static void ncp_update_dirent(struct inode *inode, struct ncp_entry_info *nwinfo)
122{
123 NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum;
124 NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum;
125 NCP_FINFO(inode)->volNumber = nwinfo->volume;
126}
127
128void ncp_update_inode(struct inode *inode, struct ncp_entry_info *nwinfo)
129{
130 ncp_update_dirent(inode, nwinfo);
131 NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
132 NCP_FINFO(inode)->access = nwinfo->access;
133 memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle,
134 sizeof(nwinfo->file_handle));
135 DPRINTK("ncp_update_inode: updated %s, volnum=%d, dirent=%u\n",
136 nwinfo->i.entryName, NCP_FINFO(inode)->volNumber,
137 NCP_FINFO(inode)->dirEntNum);
138}
139
140static void ncp_update_dates(struct inode *inode, struct nw_info_struct *nwi)
141{
142 /* NFS namespace mode overrides others if it's set. */
143 DPRINTK(KERN_DEBUG "ncp_update_dates_and_mode: (%s) nfs.mode=0%o\n",
144 nwi->entryName, nwi->nfs.mode);
145 if (nwi->nfs.mode) {
146 /* XXX Security? */
147 inode->i_mode = nwi->nfs.mode;
148 }
149
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200150 inode->i_blocks = (i_size_read(inode) + NCP_BLOCK_SIZE - 1) >> NCP_BLOCK_SHIFT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151
152 inode->i_mtime.tv_sec = ncp_date_dos2unix(nwi->modifyTime, nwi->modifyDate);
153 inode->i_ctime.tv_sec = ncp_date_dos2unix(nwi->creationTime, nwi->creationDate);
154 inode->i_atime.tv_sec = ncp_date_dos2unix(0, nwi->lastAccessDate);
155 inode->i_atime.tv_nsec = 0;
156 inode->i_mtime.tv_nsec = 0;
157 inode->i_ctime.tv_nsec = 0;
158}
159
160static void ncp_update_attrs(struct inode *inode, struct ncp_entry_info *nwinfo)
161{
162 struct nw_info_struct *nwi = &nwinfo->i;
163 struct ncp_server *server = NCP_SERVER(inode);
164
165 if (nwi->attributes & aDIR) {
166 inode->i_mode = server->m.dir_mode;
167 /* for directories dataStreamSize seems to be some
168 Object ID ??? */
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200169 i_size_write(inode, NCP_BLOCK_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 } else {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200171 u32 size;
172
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 inode->i_mode = server->m.file_mode;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200174 size = le32_to_cpu(nwi->dataStreamSize);
175 i_size_write(inode, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176#ifdef CONFIG_NCPFS_EXTRAS
177 if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS))
178 && (nwi->attributes & aSHARED)) {
179 switch (nwi->attributes & (aHIDDEN|aSYSTEM)) {
180 case aHIDDEN:
181 if (server->m.flags & NCP_MOUNT_SYMLINKS) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200182 if (/* (size >= NCP_MIN_SYMLINK_SIZE)
183 && */ (size <= NCP_MAX_SYMLINK_SIZE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK;
185 NCP_FINFO(inode)->flags |= NCPI_KLUDGE_SYMLINK;
186 break;
187 }
188 }
189 /* FALLTHROUGH */
190 case 0:
191 if (server->m.flags & NCP_MOUNT_EXTRAS)
192 inode->i_mode |= S_IRUGO;
193 break;
194 case aSYSTEM:
195 if (server->m.flags & NCP_MOUNT_EXTRAS)
196 inode->i_mode |= (inode->i_mode >> 2) & S_IXUGO;
197 break;
198 /* case aSYSTEM|aHIDDEN: */
199 default:
200 /* reserved combination */
201 break;
202 }
203 }
204#endif
205 }
206 if (nwi->attributes & aRONLY) inode->i_mode &= ~S_IWUGO;
207}
208
209void ncp_update_inode2(struct inode* inode, struct ncp_entry_info *nwinfo)
210{
211 NCP_FINFO(inode)->flags = 0;
212 if (!atomic_read(&NCP_FINFO(inode)->opened)) {
213 NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
214 ncp_update_attrs(inode, nwinfo);
215 }
216
217 ncp_update_dates(inode, &nwinfo->i);
218 ncp_update_dirent(inode, nwinfo);
219}
220
221/*
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200222 * Fill in the inode based on the ncp_entry_info structure. Used only for brand new inodes.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 */
224static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo)
225{
226 struct ncp_server *server = NCP_SERVER(inode);
227
228 NCP_FINFO(inode)->flags = 0;
229
230 ncp_update_attrs(inode, nwinfo);
231
232 DDPRINTK("ncp_read_inode: inode->i_mode = %u\n", inode->i_mode);
233
Miklos Szeredibfe86842011-10-28 14:13:29 +0200234 set_nlink(inode, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 inode->i_uid = server->m.uid;
236 inode->i_gid = server->m.gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237
238 ncp_update_dates(inode, &nwinfo->i);
239 ncp_update_inode(inode, nwinfo);
240}
241
242#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
Arjan van de Ven92e1d5b2007-02-12 00:55:39 -0800243static const struct inode_operations ncp_symlink_inode_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 .readlink = generic_readlink,
245 .follow_link = page_follow_link_light,
246 .put_link = page_put_link,
247 .setattr = ncp_notify_change,
248};
249#endif
250
251/*
252 * Get a new inode.
253 */
254struct inode *
255ncp_iget(struct super_block *sb, struct ncp_entry_info *info)
256{
257 struct inode *inode;
258
259 if (info == NULL) {
260 printk(KERN_ERR "ncp_iget: info is NULL\n");
261 return NULL;
262 }
263
264 inode = new_inode(sb);
265 if (inode) {
266 atomic_set(&NCP_FINFO(inode)->opened, info->opened);
267
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200268 inode->i_mapping->backing_dev_info = sb->s_bdi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 inode->i_ino = info->ino;
270 ncp_set_attr(inode, info);
271 if (S_ISREG(inode->i_mode)) {
272 inode->i_op = &ncp_file_inode_operations;
273 inode->i_fop = &ncp_file_operations;
274 } else if (S_ISDIR(inode->i_mode)) {
275 inode->i_op = &ncp_dir_inode_operations;
276 inode->i_fop = &ncp_dir_operations;
277#ifdef CONFIG_NCPFS_NFS_NS
278 } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
279 init_special_inode(inode, inode->i_mode,
280 new_decode_dev(info->i.nfs.rdev));
281#endif
282#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
283 } else if (S_ISLNK(inode->i_mode)) {
284 inode->i_op = &ncp_symlink_inode_operations;
285 inode->i_data.a_ops = &ncp_symlink_aops;
286#endif
287 } else {
288 make_bad_inode(inode);
289 }
290 insert_inode_hash(inode);
291 } else
292 printk(KERN_ERR "ncp_iget: iget failed!\n");
293 return inode;
294}
295
296static void
Al Viro94ee8492010-06-07 00:45:56 -0400297ncp_evict_inode(struct inode *inode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298{
Mark Fashehfef26652005-09-09 13:01:31 -0700299 truncate_inode_pages(&inode->i_data, 0);
Jan Karadbd57682012-05-03 14:48:02 +0200300 clear_inode(inode);
Mark Fashehfef26652005-09-09 13:01:31 -0700301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 if (S_ISDIR(inode->i_mode)) {
Al Viro94ee8492010-06-07 00:45:56 -0400303 DDPRINTK("ncp_evict_inode: put directory %ld\n", inode->i_ino);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 }
305
306 if (ncp_make_closed(inode) != 0) {
307 /* We can't do anything but complain. */
Al Viro94ee8492010-06-07 00:45:56 -0400308 printk(KERN_ERR "ncp_evict_inode: could not close\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310}
311
312static void ncp_stop_tasks(struct ncp_server *server) {
313 struct sock* sk = server->ncp_sock->sk;
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200314
315 lock_sock(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 sk->sk_error_report = server->error_report;
317 sk->sk_data_ready = server->data_ready;
318 sk->sk_write_space = server->write_space;
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200319 release_sock(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 del_timer_sync(&server->timeout_tm);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100321
Tejun Heo43829732012-08-20 14:51:24 -0700322 flush_work(&server->rcv.tq);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100323 if (sk->sk_socket->type == SOCK_STREAM)
Tejun Heo43829732012-08-20 14:51:24 -0700324 flush_work(&server->tx.tq);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100325 else
Tejun Heo43829732012-08-20 14:51:24 -0700326 flush_work(&server->timeout_tq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327}
328
Al Viro34c80b12011-12-08 21:32:45 -0500329static int ncp_show_options(struct seq_file *seq, struct dentry *root)
Miklos Szeredi564cd132008-02-08 04:21:46 -0800330{
Al Viro34c80b12011-12-08 21:32:45 -0500331 struct ncp_server *server = NCP_SBP(root->d_sb);
Miklos Szeredi564cd132008-02-08 04:21:46 -0800332 unsigned int tmp;
333
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800334 if (!uid_eq(server->m.uid, GLOBAL_ROOT_UID))
335 seq_printf(seq, ",uid=%u",
336 from_kuid_munged(&init_user_ns, server->m.uid));
337 if (!gid_eq(server->m.gid, GLOBAL_ROOT_GID))
338 seq_printf(seq, ",gid=%u",
339 from_kgid_munged(&init_user_ns, server->m.gid));
340 if (!uid_eq(server->m.mounted_uid, GLOBAL_ROOT_UID))
341 seq_printf(seq, ",owner=%u",
342 from_kuid_munged(&init_user_ns, server->m.mounted_uid));
Miklos Szeredi564cd132008-02-08 04:21:46 -0800343 tmp = server->m.file_mode & S_IALLUGO;
344 if (tmp != NCP_DEFAULT_FILE_MODE)
345 seq_printf(seq, ",mode=0%o", tmp);
346 tmp = server->m.dir_mode & S_IALLUGO;
347 if (tmp != NCP_DEFAULT_DIR_MODE)
348 seq_printf(seq, ",dirmode=0%o", tmp);
349 if (server->m.time_out != NCP_DEFAULT_TIME_OUT * HZ / 100) {
350 tmp = server->m.time_out * 100 / HZ;
351 seq_printf(seq, ",timeout=%u", tmp);
352 }
353 if (server->m.retry_count != NCP_DEFAULT_RETRY_COUNT)
354 seq_printf(seq, ",retry=%u", server->m.retry_count);
355 if (server->m.flags != 0)
356 seq_printf(seq, ",flags=%lu", server->m.flags);
357 if (server->m.wdog_pid != NULL)
358 seq_printf(seq, ",wdogpid=%u", pid_vnr(server->m.wdog_pid));
359
360 return 0;
361}
362
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363static const struct ncp_option ncp_opts[] = {
364 { "uid", OPT_INT, 'u' },
365 { "gid", OPT_INT, 'g' },
366 { "owner", OPT_INT, 'o' },
367 { "mode", OPT_INT, 'm' },
368 { "dirmode", OPT_INT, 'd' },
369 { "timeout", OPT_INT, 't' },
370 { "retry", OPT_INT, 'r' },
371 { "flags", OPT_INT, 'f' },
372 { "wdogpid", OPT_INT, 'w' },
373 { "ncpfd", OPT_INT, 'n' },
374 { "infofd", OPT_INT, 'i' }, /* v5 */
375 { "version", OPT_INT, 'v' },
376 { NULL, 0, 0 } };
377
378static int ncp_parse_options(struct ncp_mount_data_kernel *data, char *options) {
379 int optval;
380 char *optarg;
381 unsigned long optint;
382 int version = 0;
Eric W. Biederman1de24122006-12-13 00:35:13 -0800383 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384
385 data->flags = 0;
386 data->int_flags = 0;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800387 data->mounted_uid = GLOBAL_ROOT_UID;
Eric W. Biederman21542272006-12-13 00:35:11 -0800388 data->wdog_pid = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 data->ncp_fd = ~0;
Miklos Szeredi564cd132008-02-08 04:21:46 -0800390 data->time_out = NCP_DEFAULT_TIME_OUT;
391 data->retry_count = NCP_DEFAULT_RETRY_COUNT;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800392 data->uid = GLOBAL_ROOT_UID;
393 data->gid = GLOBAL_ROOT_GID;
Miklos Szeredi564cd132008-02-08 04:21:46 -0800394 data->file_mode = NCP_DEFAULT_FILE_MODE;
395 data->dir_mode = NCP_DEFAULT_DIR_MODE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 data->info_fd = -1;
397 data->mounted_vol[0] = 0;
398
399 while ((optval = ncp_getopt("ncpfs", &options, ncp_opts, NULL, &optarg, &optint)) != 0) {
Eric W. Biederman1de24122006-12-13 00:35:13 -0800400 ret = optval;
401 if (ret < 0)
402 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 switch (optval) {
404 case 'u':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800405 data->uid = make_kuid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800406 if (!uid_valid(data->uid)) {
407 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800408 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800409 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 break;
411 case 'g':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800412 data->gid = make_kgid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800413 if (!gid_valid(data->gid)) {
414 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800415 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800416 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 break;
418 case 'o':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800419 data->mounted_uid = make_kuid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800420 if (!uid_valid(data->mounted_uid)) {
421 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800422 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800423 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 break;
425 case 'm':
426 data->file_mode = optint;
427 break;
428 case 'd':
429 data->dir_mode = optint;
430 break;
431 case 't':
432 data->time_out = optint;
433 break;
434 case 'r':
435 data->retry_count = optint;
436 break;
437 case 'f':
438 data->flags = optint;
439 break;
440 case 'w':
Eric W. Biederman21542272006-12-13 00:35:11 -0800441 data->wdog_pid = find_get_pid(optint);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 break;
443 case 'n':
444 data->ncp_fd = optint;
445 break;
446 case 'i':
447 data->info_fd = optint;
448 break;
449 case 'v':
Eric W. Biederman1de24122006-12-13 00:35:13 -0800450 ret = -ECHRNG;
451 if (optint < NCP_MOUNT_VERSION_V4)
452 goto err;
453 if (optint > NCP_MOUNT_VERSION_V5)
454 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 version = optint;
456 break;
457
458 }
459 }
460 return 0;
Eric W. Biederman1de24122006-12-13 00:35:13 -0800461err:
462 put_pid(data->wdog_pid);
463 data->wdog_pid = NULL;
464 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465}
466
467static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
468{
469 struct ncp_mount_data_kernel data;
470 struct ncp_server *server;
471 struct file *ncp_filp;
472 struct inode *root_inode;
473 struct inode *sock_inode;
474 struct socket *sock;
475 int error;
476 int default_bufsize;
477#ifdef CONFIG_NCPFS_PACKET_SIGNING
478 int options;
479#endif
480 struct ncp_entry_info finfo;
481
Andrew Morton2a5cac12011-05-24 17:13:42 -0700482 memset(&data, 0, sizeof(data));
Panagiotis Issarisf8314dc2006-09-27 01:49:37 -0700483 server = kzalloc(sizeof(struct ncp_server), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 if (!server)
485 return -ENOMEM;
486 sb->s_fs_info = server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
488 error = -EFAULT;
489 if (raw_data == NULL)
490 goto out;
491 switch (*(int*)raw_data) {
492 case NCP_MOUNT_VERSION:
493 {
494 struct ncp_mount_data* md = (struct ncp_mount_data*)raw_data;
495
496 data.flags = md->flags;
497 data.int_flags = NCP_IMOUNT_LOGGEDIN_POSSIBLE;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800498 data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid);
Eric W. Biederman21542272006-12-13 00:35:11 -0800499 data.wdog_pid = find_get_pid(md->wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 data.ncp_fd = md->ncp_fd;
501 data.time_out = md->time_out;
502 data.retry_count = md->retry_count;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800503 data.uid = make_kuid(current_user_ns(), md->uid);
504 data.gid = make_kgid(current_user_ns(), md->gid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 data.file_mode = md->file_mode;
506 data.dir_mode = md->dir_mode;
507 data.info_fd = -1;
508 memcpy(data.mounted_vol, md->mounted_vol,
509 NCP_VOLNAME_LEN+1);
510 }
511 break;
512 case NCP_MOUNT_VERSION_V4:
513 {
514 struct ncp_mount_data_v4* md = (struct ncp_mount_data_v4*)raw_data;
515
516 data.flags = md->flags;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800517 data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid);
Eric W. Biederman21542272006-12-13 00:35:11 -0800518 data.wdog_pid = find_get_pid(md->wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 data.ncp_fd = md->ncp_fd;
520 data.time_out = md->time_out;
521 data.retry_count = md->retry_count;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800522 data.uid = make_kuid(current_user_ns(), md->uid);
523 data.gid = make_kgid(current_user_ns(), md->gid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 data.file_mode = md->file_mode;
525 data.dir_mode = md->dir_mode;
526 data.info_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 }
528 break;
529 default:
530 error = -ECHRNG;
531 if (memcmp(raw_data, "vers", 4) == 0) {
532 error = ncp_parse_options(&data, raw_data);
533 }
534 if (error)
535 goto out;
536 break;
537 }
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800538 error = -EINVAL;
539 if (!uid_valid(data.mounted_uid) || !uid_valid(data.uid) ||
540 !gid_valid(data.gid))
541 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 error = -EBADF;
543 ncp_filp = fget(data.ncp_fd);
544 if (!ncp_filp)
545 goto out;
546 error = -ENOTSOCK;
Al Viro496ad9a2013-01-23 17:07:38 -0500547 sock_inode = file_inode(ncp_filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 if (!S_ISSOCK(sock_inode->i_mode))
549 goto out_fput;
550 sock = SOCKET_I(sock_inode);
551 if (!sock)
552 goto out_fput;
553
554 if (sock->type == SOCK_STREAM)
555 default_bufsize = 0xF000;
556 else
557 default_bufsize = 1024;
558
559 sb->s_flags |= MS_NODIRATIME; /* probably even noatime */
560 sb->s_maxbytes = 0xFFFFFFFFU;
561 sb->s_blocksize = 1024; /* Eh... Is this correct? */
562 sb->s_blocksize_bits = 10;
563 sb->s_magic = NCP_SUPER_MAGIC;
564 sb->s_op = &ncp_sops;
Al Viro0378c402011-01-12 17:25:03 -0500565 sb->s_d_op = &ncp_dentry_operations;
Jens Axboef1970c72010-04-22 12:31:11 +0200566 sb->s_bdi = &server->bdi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
568 server = NCP_SBP(sb);
569 memset(server, 0, sizeof(*server));
570
Jens Axboef1970c72010-04-22 12:31:11 +0200571 error = bdi_setup_and_register(&server->bdi, "ncpfs", BDI_CAP_MAP_COPY);
572 if (error)
Djalal Harouni759c3612011-12-13 02:47:29 +0100573 goto out_fput;
Jens Axboef1970c72010-04-22 12:31:11 +0200574
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 server->ncp_filp = ncp_filp;
576 server->ncp_sock = sock;
577
578 if (data.info_fd != -1) {
579 struct socket *info_sock;
580
581 error = -EBADF;
582 server->info_filp = fget(data.info_fd);
583 if (!server->info_filp)
Djalal Harouni759c3612011-12-13 02:47:29 +0100584 goto out_bdi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 error = -ENOTSOCK;
Al Viro496ad9a2013-01-23 17:07:38 -0500586 sock_inode = file_inode(server->info_filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 if (!S_ISSOCK(sock_inode->i_mode))
588 goto out_fput2;
589 info_sock = SOCKET_I(sock_inode);
590 if (!info_sock)
591 goto out_fput2;
592 error = -EBADFD;
593 if (info_sock->type != SOCK_STREAM)
594 goto out_fput2;
595 server->info_sock = info_sock;
596 }
597
598/* server->lock = 0; */
Ingo Molnar8e3f9042006-03-23 03:00:43 -0800599 mutex_init(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 server->packet = NULL;
601/* server->buffer_size = 0; */
602/* server->conn_status = 0; */
603/* server->root_dentry = NULL; */
604/* server->root_setuped = 0; */
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200605 mutex_init(&server->root_setup_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606#ifdef CONFIG_NCPFS_PACKET_SIGNING
607/* server->sign_wanted = 0; */
608/* server->sign_active = 0; */
609#endif
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200610 init_rwsem(&server->auth_rwsem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 server->auth.auth_type = NCP_AUTH_NONE;
612/* server->auth.object_name_len = 0; */
613/* server->auth.object_name = NULL; */
614/* server->auth.object_type = 0; */
615/* server->priv.len = 0; */
616/* server->priv.data = NULL; */
617
618 server->m = data;
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300619 /* Although anything producing this is buggy, it happens
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 now because of PATH_MAX changes.. */
621 if (server->m.time_out < 1) {
622 server->m.time_out = 10;
623 printk(KERN_INFO "You need to recompile your ncpfs utils..\n");
624 }
625 server->m.time_out = server->m.time_out * HZ / 100;
626 server->m.file_mode = (server->m.file_mode & S_IRWXUGO) | S_IFREG;
627 server->m.dir_mode = (server->m.dir_mode & S_IRWXUGO) | S_IFDIR;
628
629#ifdef CONFIG_NCPFS_NLS
630 /* load the default NLS charsets */
631 server->nls_vol = load_nls_default();
632 server->nls_io = load_nls_default();
633#endif /* CONFIG_NCPFS_NLS */
634
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200635 atomic_set(&server->dentry_ttl, 0); /* no caching */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
637 INIT_LIST_HEAD(&server->tx.requests);
Ingo Molnar8e3f9042006-03-23 03:00:43 -0800638 mutex_init(&server->rcv.creq_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 server->tx.creq = NULL;
640 server->rcv.creq = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641
642 init_timer(&server->timeout_tm);
643#undef NCP_PACKET_SIZE
644#define NCP_PACKET_SIZE 131072
645 error = -ENOMEM;
646 server->packet_size = NCP_PACKET_SIZE;
647 server->packet = vmalloc(NCP_PACKET_SIZE);
648 if (server->packet == NULL)
649 goto out_nls;
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100650 server->txbuf = vmalloc(NCP_PACKET_SIZE);
651 if (server->txbuf == NULL)
652 goto out_packet;
653 server->rxbuf = vmalloc(NCP_PACKET_SIZE);
654 if (server->rxbuf == NULL)
655 goto out_txbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200657 lock_sock(sock->sk);
658 server->data_ready = sock->sk->sk_data_ready;
659 server->write_space = sock->sk->sk_write_space;
660 server->error_report = sock->sk->sk_error_report;
661 sock->sk->sk_user_data = server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 sock->sk->sk_data_ready = ncp_tcp_data_ready;
663 sock->sk->sk_error_report = ncp_tcp_error_report;
664 if (sock->type == SOCK_STREAM) {
665 server->rcv.ptr = (unsigned char*)&server->rcv.buf;
666 server->rcv.len = 10;
667 server->rcv.state = 0;
David Howellsc4028952006-11-22 14:57:56 +0000668 INIT_WORK(&server->rcv.tq, ncp_tcp_rcv_proc);
669 INIT_WORK(&server->tx.tq, ncp_tcp_tx_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 sock->sk->sk_write_space = ncp_tcp_write_space;
671 } else {
David Howellsc4028952006-11-22 14:57:56 +0000672 INIT_WORK(&server->rcv.tq, ncpdgram_rcv_proc);
673 INIT_WORK(&server->timeout_tq, ncpdgram_timeout_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 server->timeout_tm.data = (unsigned long)server;
675 server->timeout_tm.function = ncpdgram_timeout_call;
676 }
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200677 release_sock(sock->sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
679 ncp_lock_server(server);
680 error = ncp_connect(server);
681 ncp_unlock_server(server);
682 if (error < 0)
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100683 goto out_rxbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 DPRINTK("ncp_fill_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb));
685
686 error = -EMSGSIZE; /* -EREMOTESIDEINCOMPATIBLE */
687#ifdef CONFIG_NCPFS_PACKET_SIGNING
688 if (ncp_negotiate_size_and_options(server, default_bufsize,
689 NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0)
690 {
691 if (options != NCP_DEFAULT_OPTIONS)
692 {
693 if (ncp_negotiate_size_and_options(server,
694 default_bufsize,
695 options & 2,
696 &(server->buffer_size), &options) != 0)
697
698 {
699 goto out_disconnect;
700 }
701 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200702 ncp_lock_server(server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 if (options & 2)
704 server->sign_wanted = 1;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200705 ncp_unlock_server(server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 }
707 else
708#endif /* CONFIG_NCPFS_PACKET_SIGNING */
709 if (ncp_negotiate_buffersize(server, default_bufsize,
710 &(server->buffer_size)) != 0)
711 goto out_disconnect;
712 DPRINTK("ncpfs: bufsize = %d\n", server->buffer_size);
713
714 memset(&finfo, 0, sizeof(finfo));
715 finfo.i.attributes = aDIR;
716 finfo.i.dataStreamSize = 0; /* ignored */
717 finfo.i.dirEntNum = 0;
718 finfo.i.DosDirNum = 0;
719#ifdef CONFIG_NCPFS_SMALLDOS
720 finfo.i.NSCreator = NW_NS_DOS;
721#endif
722 finfo.volume = NCP_NUMBER_OF_VOLUMES;
723 /* set dates of mountpoint to Jan 1, 1986; 00:00 */
724 finfo.i.creationTime = finfo.i.modifyTime
725 = cpu_to_le16(0x0000);
726 finfo.i.creationDate = finfo.i.modifyDate
727 = finfo.i.lastAccessDate
728 = cpu_to_le16(0x0C21);
729 finfo.i.nameLen = 0;
730 finfo.i.entryName[0] = '\0';
731
732 finfo.opened = 0;
733 finfo.ino = 2; /* tradition */
734
735 server->name_space[finfo.volume] = NW_NS_DOS;
736
737 error = -ENOMEM;
738 root_inode = ncp_iget(sb, &finfo);
739 if (!root_inode)
740 goto out_disconnect;
741 DPRINTK("ncp_fill_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
Al Viro48fde702012-01-08 22:15:13 -0500742 sb->s_root = d_make_root(root_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 if (!sb->s_root)
Al Viro48fde702012-01-08 22:15:13 -0500744 goto out_disconnect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 return 0;
746
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747out_disconnect:
748 ncp_lock_server(server);
749 ncp_disconnect(server);
750 ncp_unlock_server(server);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100751out_rxbuf:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 ncp_stop_tasks(server);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100753 vfree(server->rxbuf);
754out_txbuf:
755 vfree(server->txbuf);
756out_packet:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 vfree(server->packet);
758out_nls:
759#ifdef CONFIG_NCPFS_NLS
760 unload_nls(server->nls_io);
761 unload_nls(server->nls_vol);
762#endif
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200763 mutex_destroy(&server->rcv.creq_mutex);
764 mutex_destroy(&server->root_setup_lock);
765 mutex_destroy(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766out_fput2:
767 if (server->info_filp)
768 fput(server->info_filp);
Jens Axboef1970c72010-04-22 12:31:11 +0200769out_bdi:
Djalal Harouni759c3612011-12-13 02:47:29 +0100770 bdi_destroy(&server->bdi);
771out_fput:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 /* 23/12/1998 Marcin Dalecki <dalecki@cs.net.pl>:
773 *
Matthew Whitworthe956b4b2010-06-27 14:34:30 +0100774 * The previously used put_filp(ncp_filp); was bogus, since
775 * it doesn't perform proper unlocking.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 */
777 fput(ncp_filp);
778out:
Eric W. Biederman1de24122006-12-13 00:35:13 -0800779 put_pid(data.wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 sb->s_fs_info = NULL;
781 kfree(server);
782 return error;
783}
784
Al Viro1dcddd42013-10-03 13:22:44 -0400785static void delayed_free(struct rcu_head *p)
786{
787 struct ncp_server *server = container_of(p, struct ncp_server, rcu);
788#ifdef CONFIG_NCPFS_NLS
789 /* unload the NLS charsets */
790 unload_nls(server->nls_vol);
791 unload_nls(server->nls_io);
792#endif /* CONFIG_NCPFS_NLS */
793 kfree(server);
794}
795
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796static void ncp_put_super(struct super_block *sb)
797{
798 struct ncp_server *server = NCP_SBP(sb);
799
800 ncp_lock_server(server);
801 ncp_disconnect(server);
802 ncp_unlock_server(server);
803
804 ncp_stop_tasks(server);
805
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200806 mutex_destroy(&server->rcv.creq_mutex);
807 mutex_destroy(&server->root_setup_lock);
808 mutex_destroy(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809
810 if (server->info_filp)
811 fput(server->info_filp);
812 fput(server->ncp_filp);
Eric W. Biederman21542272006-12-13 00:35:11 -0800813 kill_pid(server->m.wdog_pid, SIGTERM, 1);
814 put_pid(server->m.wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
Jens Axboef1970c72010-04-22 12:31:11 +0200816 bdi_destroy(&server->bdi);
Pekka Enberg44db77f2006-01-14 13:21:12 -0800817 kfree(server->priv.data);
818 kfree(server->auth.object_name);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100819 vfree(server->rxbuf);
820 vfree(server->txbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 vfree(server->packet);
Al Viro1dcddd42013-10-03 13:22:44 -0400822 call_rcu(&server->rcu, delayed_free);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823}
824
David Howells726c3342006-06-23 02:02:58 -0700825static int ncp_statfs(struct dentry *dentry, struct kstatfs *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826{
827 struct dentry* d;
828 struct inode* i;
829 struct ncp_inode_info* ni;
830 struct ncp_server* s;
831 struct ncp_volume_info vi;
David Howells726c3342006-06-23 02:02:58 -0700832 struct super_block *sb = dentry->d_sb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 int err;
834 __u8 dh;
835
836 d = sb->s_root;
837 if (!d) {
838 goto dflt;
839 }
840 i = d->d_inode;
841 if (!i) {
842 goto dflt;
843 }
844 ni = NCP_FINFO(i);
845 if (!ni) {
846 goto dflt;
847 }
848 s = NCP_SBP(sb);
849 if (!s) {
850 goto dflt;
851 }
852 if (!s->m.mounted_vol[0]) {
853 goto dflt;
854 }
855
856 err = ncp_dirhandle_alloc(s, ni->volNumber, ni->DosDirNum, &dh);
857 if (err) {
858 goto dflt;
859 }
860 err = ncp_get_directory_info(s, dh, &vi);
861 ncp_dirhandle_free(s, dh);
862 if (err) {
863 goto dflt;
864 }
865 buf->f_type = NCP_SUPER_MAGIC;
866 buf->f_bsize = vi.sectors_per_block * 512;
867 buf->f_blocks = vi.total_blocks;
868 buf->f_bfree = vi.free_blocks;
869 buf->f_bavail = vi.free_blocks;
870 buf->f_files = vi.total_dir_entries;
871 buf->f_ffree = vi.available_dir_entries;
872 buf->f_namelen = 12;
873 return 0;
874
875 /* We cannot say how much disk space is left on a mounted
876 NetWare Server, because free space is distributed over
877 volumes, and the current user might have disk quotas. So
878 free space is not that simple to determine. Our decision
879 here is to err conservatively. */
880
881dflt:;
882 buf->f_type = NCP_SUPER_MAGIC;
883 buf->f_bsize = NCP_BLOCK_SIZE;
884 buf->f_blocks = 0;
885 buf->f_bfree = 0;
886 buf->f_bavail = 0;
887 buf->f_namelen = 12;
888 return 0;
889}
890
891int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
892{
893 struct inode *inode = dentry->d_inode;
894 int result = 0;
895 __le32 info_mask;
896 struct nw_modify_dos_info info;
897 struct ncp_server *server;
898
899 result = -EIO;
900
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 server = NCP_SERVER(inode);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200902 if (!server) /* How this could happen? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 goto out;
904
Al Viro338b2f52013-06-15 05:53:23 +0400905 result = -EPERM;
906 if (IS_DEADDIR(dentry->d_inode))
907 goto out;
908
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 /* ageing the dentry to force validation */
910 ncp_age_dentry(server, dentry);
911
912 result = inode_change_ok(inode, attr);
913 if (result < 0)
914 goto out;
915
916 result = -EPERM;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800917 if ((attr->ia_valid & ATTR_UID) && !uid_eq(attr->ia_uid, server->m.uid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 goto out;
919
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800920 if ((attr->ia_valid & ATTR_GID) && !gid_eq(attr->ia_gid, server->m.gid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 goto out;
922
923 if (((attr->ia_valid & ATTR_MODE) &&
924 (attr->ia_mode &
925 ~(S_IFREG | S_IFDIR | S_IRWXUGO))))
926 goto out;
927
928 info_mask = 0;
929 memset(&info, 0, sizeof(info));
930
931#if 1
932 if ((attr->ia_valid & ATTR_MODE) != 0)
933 {
934 umode_t newmode = attr->ia_mode;
935
936 info_mask |= DM_ATTRIBUTES;
937
938 if (S_ISDIR(inode->i_mode)) {
939 newmode &= server->m.dir_mode;
940 } else {
941#ifdef CONFIG_NCPFS_EXTRAS
942 if (server->m.flags & NCP_MOUNT_EXTRAS) {
943 /* any non-default execute bit set */
944 if (newmode & ~server->m.file_mode & S_IXUGO)
945 info.attributes |= aSHARED | aSYSTEM;
946 /* read for group/world and not in default file_mode */
947 else if (newmode & ~server->m.file_mode & S_IRUGO)
948 info.attributes |= aSHARED;
949 } else
950#endif
951 newmode &= server->m.file_mode;
952 }
953 if (newmode & S_IWUGO)
954 info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
955 else
956 info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
957
958#ifdef CONFIG_NCPFS_NFS_NS
959 if (ncp_is_nfs_extras(server, NCP_FINFO(inode)->volNumber)) {
960 result = ncp_modify_nfs_info(server,
961 NCP_FINFO(inode)->volNumber,
962 NCP_FINFO(inode)->dirEntNum,
963 attr->ia_mode, 0);
964 if (result != 0)
965 goto out;
966 info.attributes &= ~(aSHARED | aSYSTEM);
967 {
968 /* mark partial success */
969 struct iattr tmpattr;
970
971 tmpattr.ia_valid = ATTR_MODE;
972 tmpattr.ia_mode = attr->ia_mode;
973
Christoph Hellwig10257742010-06-04 11:30:02 +0200974 setattr_copy(inode, &tmpattr);
975 mark_inode_dirty(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 }
977 }
978#endif
979 }
980#endif
981
982 /* Do SIZE before attributes, otherwise mtime together with size does not work...
983 */
984 if ((attr->ia_valid & ATTR_SIZE) != 0) {
985 int written;
986
987 DPRINTK("ncpfs: trying to change size to %ld\n",
988 attr->ia_size);
989
990 if ((result = ncp_make_open(inode, O_WRONLY)) < 0) {
991 result = -EACCES;
992 goto out;
993 }
994 ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
995 attr->ia_size, 0, "", &written);
996
997 /* According to ndir, the changes only take effect after
998 closing the file */
999 ncp_inode_close(inode);
1000 result = ncp_make_closed(inode);
1001 if (result)
1002 goto out;
Christoph Hellwig10257742010-06-04 11:30:02 +02001003
1004 if (attr->ia_size != i_size_read(inode)) {
Marco Stornelli3e7a8062012-12-15 11:57:03 +01001005 truncate_setsize(inode, attr->ia_size);
Christoph Hellwig10257742010-06-04 11:30:02 +02001006 mark_inode_dirty(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 }
1008 }
1009 if ((attr->ia_valid & ATTR_CTIME) != 0) {
1010 info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE);
1011 ncp_date_unix2dos(attr->ia_ctime.tv_sec,
1012 &info.creationTime, &info.creationDate);
1013 }
1014 if ((attr->ia_valid & ATTR_MTIME) != 0) {
1015 info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE);
1016 ncp_date_unix2dos(attr->ia_mtime.tv_sec,
1017 &info.modifyTime, &info.modifyDate);
1018 }
1019 if ((attr->ia_valid & ATTR_ATIME) != 0) {
1020 __le16 dummy;
1021 info_mask |= (DM_LAST_ACCESS_DATE);
1022 ncp_date_unix2dos(attr->ia_atime.tv_sec,
1023 &dummy, &info.lastAccessDate);
1024 }
1025 if (info_mask != 0) {
1026 result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode),
1027 inode, info_mask, &info);
1028 if (result != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) {
1030 /* NetWare seems not to allow this. I
1031 do not know why. So, just tell the
1032 user everything went fine. This is
1033 a terrible hack, but I do not know
1034 how to do this correctly. */
1035 result = 0;
1036 } else
1037 goto out;
1038 }
1039#ifdef CONFIG_NCPFS_STRONG
1040 if ((!result) && (info_mask & DM_ATTRIBUTES))
1041 NCP_FINFO(inode)->nwattr = info.attributes;
1042#endif
1043 }
Christoph Hellwig10257742010-06-04 11:30:02 +02001044 if (result)
1045 goto out;
1046
1047 setattr_copy(inode, attr);
1048 mark_inode_dirty(inode);
1049
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050out:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001051 if (result > 0)
1052 result = -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 return result;
1054}
1055
Al Viro3c26ff62010-07-25 11:46:36 +04001056static struct dentry *ncp_mount(struct file_system_type *fs_type,
1057 int flags, const char *dev_name, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058{
Al Viro3c26ff62010-07-25 11:46:36 +04001059 return mount_nodev(fs_type, flags, data, ncp_fill_super);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060}
1061
1062static struct file_system_type ncp_fs_type = {
1063 .owner = THIS_MODULE,
1064 .name = "ncpfs",
Al Viro3c26ff62010-07-25 11:46:36 +04001065 .mount = ncp_mount,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 .kill_sb = kill_anon_super,
Miklos Szeredi564cd132008-02-08 04:21:46 -08001067 .fs_flags = FS_BINARY_MOUNTDATA,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068};
Eric W. Biederman7f78e032013-03-02 19:39:14 -08001069MODULE_ALIAS_FS("ncpfs");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070
1071static int __init init_ncp_fs(void)
1072{
1073 int err;
Robert P. J. Day7c28cba2008-02-06 01:37:09 -08001074 DPRINTK("ncpfs: init_ncp_fs called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 err = init_inodecache();
1077 if (err)
1078 goto out1;
1079 err = register_filesystem(&ncp_fs_type);
1080 if (err)
1081 goto out;
1082 return 0;
1083out:
1084 destroy_inodecache();
1085out1:
1086 return err;
1087}
1088
1089static void __exit exit_ncp_fs(void)
1090{
Robert P. J. Day7c28cba2008-02-06 01:37:09 -08001091 DPRINTK("ncpfs: exit_ncp_fs called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 unregister_filesystem(&ncp_fs_type);
1093 destroy_inodecache();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094}
1095
1096module_init(init_ncp_fs)
1097module_exit(exit_ncp_fs)
1098MODULE_LICENSE("GPL");