blob: a699a3fc62c07ac248b1988b3ed3228fc938ec0b [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
Joe Perchesb41f8b82014-04-08 16:04:14 -070012#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/module.h>
15
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <asm/uaccess.h>
17#include <asm/byteorder.h>
18
19#include <linux/time.h>
20#include <linux/kernel.h>
21#include <linux/mm.h>
22#include <linux/string.h>
23#include <linux/stat.h>
24#include <linux/errno.h>
25#include <linux/file.h>
26#include <linux/fcntl.h>
27#include <linux/slab.h>
28#include <linux/vmalloc.h>
29#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/vfs.h>
Miklos Szeredi564cd132008-02-08 04:21:46 -080031#include <linux/mount.h>
32#include <linux/seq_file.h>
Nick Piggin34286d62011-01-07 17:49:57 +110033#include <linux/namei.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <net/sock.h>
36
Al Viro32c419d2011-01-12 17:37:47 -050037#include "ncp_fs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include "getopt.h"
39
Miklos Szeredi564cd132008-02-08 04:21:46 -080040#define NCP_DEFAULT_FILE_MODE 0600
41#define NCP_DEFAULT_DIR_MODE 0700
42#define NCP_DEFAULT_TIME_OUT 10
43#define NCP_DEFAULT_RETRY_COUNT 20
44
Al Viro94ee8492010-06-07 00:45:56 -040045static void ncp_evict_inode(struct inode *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070046static void ncp_put_super(struct super_block *);
David Howells726c3342006-06-23 02:02:58 -070047static int ncp_statfs(struct dentry *, struct kstatfs *);
Al Viro34c80b12011-12-08 21:32:45 -050048static int ncp_show_options(struct seq_file *, struct dentry *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
Christoph Lametere18b8902006-12-06 20:33:20 -080050static struct kmem_cache * ncp_inode_cachep;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52static struct inode *ncp_alloc_inode(struct super_block *sb)
53{
54 struct ncp_inode_info *ei;
Christoph Lametere94b1762006-12-06 20:33:17 -080055 ei = (struct ncp_inode_info *)kmem_cache_alloc(ncp_inode_cachep, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 if (!ei)
57 return NULL;
58 return &ei->vfs_inode;
59}
60
Nick Pigginfa0d7e3d2011-01-07 17:49:49 +110061static void ncp_i_callback(struct rcu_head *head)
62{
63 struct inode *inode = container_of(head, struct inode, i_rcu);
Nick Pigginfa0d7e3d2011-01-07 17:49:49 +110064 kmem_cache_free(ncp_inode_cachep, NCP_FINFO(inode));
65}
66
Linus Torvalds1da177e2005-04-16 15:20:36 -070067static void ncp_destroy_inode(struct inode *inode)
68{
Nick Pigginfa0d7e3d2011-01-07 17:49:49 +110069 call_rcu(&inode->i_rcu, ncp_i_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070}
71
Alexey Dobriyan51cc5062008-07-25 19:45:34 -070072static void init_once(void *foo)
Linus Torvalds1da177e2005-04-16 15:20:36 -070073{
74 struct ncp_inode_info *ei = (struct ncp_inode_info *) foo;
75
Christoph Lametera35afb82007-05-16 22:10:57 -070076 mutex_init(&ei->open_mutex);
77 inode_init_once(&ei->vfs_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078}
Paul Mundt20c2df82007-07-20 10:11:58 +090079
Linus Torvalds1da177e2005-04-16 15:20:36 -070080static int init_inodecache(void)
81{
82 ncp_inode_cachep = kmem_cache_create("ncp_inode_cache",
83 sizeof(struct ncp_inode_info),
Paul Jacksonfffb60f2006-03-24 03:16:06 -080084 0, (SLAB_RECLAIM_ACCOUNT|
85 SLAB_MEM_SPREAD),
Paul Mundt20c2df82007-07-20 10:11:58 +090086 init_once);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 if (ncp_inode_cachep == NULL)
88 return -ENOMEM;
89 return 0;
90}
91
92static void destroy_inodecache(void)
93{
Kirill A. Shutemov8c0a8532012-09-26 11:33:07 +100094 /*
95 * Make sure all delayed rcu free inodes are flushed before we
96 * destroy cache.
97 */
98 rcu_barrier();
Alexey Dobriyan1a1d92c2006-09-27 01:49:40 -070099 kmem_cache_destroy(ncp_inode_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100}
101
102static int ncp_remount(struct super_block *sb, int *flags, char* data)
103{
Theodore Ts'o02b99842014-03-13 10:14:33 -0400104 sync_filesystem(sb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 *flags |= MS_NODIRATIME;
106 return 0;
107}
108
Josef 'Jeff' Sipekee9b6d62007-02-12 00:55:41 -0800109static const struct super_operations ncp_sops =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110{
111 .alloc_inode = ncp_alloc_inode,
112 .destroy_inode = ncp_destroy_inode,
113 .drop_inode = generic_delete_inode,
Al Viro94ee8492010-06-07 00:45:56 -0400114 .evict_inode = ncp_evict_inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 .put_super = ncp_put_super,
116 .statfs = ncp_statfs,
117 .remount_fs = ncp_remount,
Miklos Szeredi564cd132008-02-08 04:21:46 -0800118 .show_options = ncp_show_options,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119};
120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121/*
122 * Fill in the ncpfs-specific information in the inode.
123 */
124static void ncp_update_dirent(struct inode *inode, struct ncp_entry_info *nwinfo)
125{
126 NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum;
127 NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum;
128 NCP_FINFO(inode)->volNumber = nwinfo->volume;
129}
130
131void ncp_update_inode(struct inode *inode, struct ncp_entry_info *nwinfo)
132{
133 ncp_update_dirent(inode, nwinfo);
134 NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
135 NCP_FINFO(inode)->access = nwinfo->access;
136 memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle,
137 sizeof(nwinfo->file_handle));
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700138 ncp_dbg(1, "updated %s, volnum=%d, dirent=%u\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 nwinfo->i.entryName, NCP_FINFO(inode)->volNumber,
140 NCP_FINFO(inode)->dirEntNum);
141}
142
143static void ncp_update_dates(struct inode *inode, struct nw_info_struct *nwi)
144{
145 /* NFS namespace mode overrides others if it's set. */
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700146 ncp_dbg(1, "(%s) nfs.mode=0%o\n", nwi->entryName, nwi->nfs.mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 if (nwi->nfs.mode) {
148 /* XXX Security? */
149 inode->i_mode = nwi->nfs.mode;
150 }
151
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200152 inode->i_blocks = (i_size_read(inode) + NCP_BLOCK_SIZE - 1) >> NCP_BLOCK_SHIFT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
154 inode->i_mtime.tv_sec = ncp_date_dos2unix(nwi->modifyTime, nwi->modifyDate);
155 inode->i_ctime.tv_sec = ncp_date_dos2unix(nwi->creationTime, nwi->creationDate);
156 inode->i_atime.tv_sec = ncp_date_dos2unix(0, nwi->lastAccessDate);
157 inode->i_atime.tv_nsec = 0;
158 inode->i_mtime.tv_nsec = 0;
159 inode->i_ctime.tv_nsec = 0;
160}
161
162static void ncp_update_attrs(struct inode *inode, struct ncp_entry_info *nwinfo)
163{
164 struct nw_info_struct *nwi = &nwinfo->i;
165 struct ncp_server *server = NCP_SERVER(inode);
166
167 if (nwi->attributes & aDIR) {
168 inode->i_mode = server->m.dir_mode;
169 /* for directories dataStreamSize seems to be some
170 Object ID ??? */
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200171 i_size_write(inode, NCP_BLOCK_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 } else {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200173 u32 size;
174
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 inode->i_mode = server->m.file_mode;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200176 size = le32_to_cpu(nwi->dataStreamSize);
177 i_size_write(inode, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178#ifdef CONFIG_NCPFS_EXTRAS
179 if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS))
180 && (nwi->attributes & aSHARED)) {
181 switch (nwi->attributes & (aHIDDEN|aSYSTEM)) {
182 case aHIDDEN:
183 if (server->m.flags & NCP_MOUNT_SYMLINKS) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200184 if (/* (size >= NCP_MIN_SYMLINK_SIZE)
185 && */ (size <= NCP_MAX_SYMLINK_SIZE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK;
187 NCP_FINFO(inode)->flags |= NCPI_KLUDGE_SYMLINK;
188 break;
189 }
190 }
191 /* FALLTHROUGH */
192 case 0:
193 if (server->m.flags & NCP_MOUNT_EXTRAS)
194 inode->i_mode |= S_IRUGO;
195 break;
196 case aSYSTEM:
197 if (server->m.flags & NCP_MOUNT_EXTRAS)
198 inode->i_mode |= (inode->i_mode >> 2) & S_IXUGO;
199 break;
200 /* case aSYSTEM|aHIDDEN: */
201 default:
202 /* reserved combination */
203 break;
204 }
205 }
206#endif
207 }
208 if (nwi->attributes & aRONLY) inode->i_mode &= ~S_IWUGO;
209}
210
211void ncp_update_inode2(struct inode* inode, struct ncp_entry_info *nwinfo)
212{
213 NCP_FINFO(inode)->flags = 0;
214 if (!atomic_read(&NCP_FINFO(inode)->opened)) {
215 NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
216 ncp_update_attrs(inode, nwinfo);
217 }
218
219 ncp_update_dates(inode, &nwinfo->i);
220 ncp_update_dirent(inode, nwinfo);
221}
222
223/*
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200224 * Fill in the inode based on the ncp_entry_info structure. Used only for brand new inodes.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 */
226static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo)
227{
228 struct ncp_server *server = NCP_SERVER(inode);
229
230 NCP_FINFO(inode)->flags = 0;
231
232 ncp_update_attrs(inode, nwinfo);
233
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700234 ncp_dbg(2, "inode->i_mode = %u\n", inode->i_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
Miklos Szeredibfe86842011-10-28 14:13:29 +0200236 set_nlink(inode, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 inode->i_uid = server->m.uid;
238 inode->i_gid = server->m.gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
240 ncp_update_dates(inode, &nwinfo->i);
241 ncp_update_inode(inode, nwinfo);
242}
243
244#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
Arjan van de Ven92e1d5b2007-02-12 00:55:39 -0800245static const struct inode_operations ncp_symlink_inode_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 .readlink = generic_readlink,
247 .follow_link = page_follow_link_light,
248 .put_link = page_put_link,
249 .setattr = ncp_notify_change,
250};
251#endif
252
253/*
254 * Get a new inode.
255 */
256struct inode *
257ncp_iget(struct super_block *sb, struct ncp_entry_info *info)
258{
259 struct inode *inode;
260
261 if (info == NULL) {
Joe Perchesb41f8b82014-04-08 16:04:14 -0700262 pr_err("%s: info is NULL\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 return NULL;
264 }
265
266 inode = new_inode(sb);
267 if (inode) {
268 atomic_set(&NCP_FINFO(inode)->opened, info->opened);
269
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200270 inode->i_mapping->backing_dev_info = sb->s_bdi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 inode->i_ino = info->ino;
272 ncp_set_attr(inode, info);
273 if (S_ISREG(inode->i_mode)) {
274 inode->i_op = &ncp_file_inode_operations;
275 inode->i_fop = &ncp_file_operations;
276 } else if (S_ISDIR(inode->i_mode)) {
277 inode->i_op = &ncp_dir_inode_operations;
278 inode->i_fop = &ncp_dir_operations;
279#ifdef CONFIG_NCPFS_NFS_NS
280 } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
281 init_special_inode(inode, inode->i_mode,
282 new_decode_dev(info->i.nfs.rdev));
283#endif
284#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
285 } else if (S_ISLNK(inode->i_mode)) {
286 inode->i_op = &ncp_symlink_inode_operations;
287 inode->i_data.a_ops = &ncp_symlink_aops;
288#endif
289 } else {
290 make_bad_inode(inode);
291 }
292 insert_inode_hash(inode);
293 } else
Joe Perchesb41f8b82014-04-08 16:04:14 -0700294 pr_err("%s: iget failed!\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 return inode;
296}
297
298static void
Al Viro94ee8492010-06-07 00:45:56 -0400299ncp_evict_inode(struct inode *inode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300{
Johannes Weiner91b0abe2014-04-03 14:47:49 -0700301 truncate_inode_pages_final(&inode->i_data);
Jan Karadbd57682012-05-03 14:48:02 +0200302 clear_inode(inode);
Mark Fashehfef26652005-09-09 13:01:31 -0700303
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 if (S_ISDIR(inode->i_mode)) {
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700305 ncp_dbg(2, "put directory %ld\n", inode->i_ino);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 }
307
308 if (ncp_make_closed(inode) != 0) {
309 /* We can't do anything but complain. */
Joe Perchesb41f8b82014-04-08 16:04:14 -0700310 pr_err("%s: could not close\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312}
313
314static void ncp_stop_tasks(struct ncp_server *server) {
315 struct sock* sk = server->ncp_sock->sk;
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200316
317 lock_sock(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 sk->sk_error_report = server->error_report;
319 sk->sk_data_ready = server->data_ready;
320 sk->sk_write_space = server->write_space;
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200321 release_sock(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 del_timer_sync(&server->timeout_tm);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100323
Tejun Heo43829732012-08-20 14:51:24 -0700324 flush_work(&server->rcv.tq);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100325 if (sk->sk_socket->type == SOCK_STREAM)
Tejun Heo43829732012-08-20 14:51:24 -0700326 flush_work(&server->tx.tq);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100327 else
Tejun Heo43829732012-08-20 14:51:24 -0700328 flush_work(&server->timeout_tq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329}
330
Al Viro34c80b12011-12-08 21:32:45 -0500331static int ncp_show_options(struct seq_file *seq, struct dentry *root)
Miklos Szeredi564cd132008-02-08 04:21:46 -0800332{
Al Viro34c80b12011-12-08 21:32:45 -0500333 struct ncp_server *server = NCP_SBP(root->d_sb);
Miklos Szeredi564cd132008-02-08 04:21:46 -0800334 unsigned int tmp;
335
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800336 if (!uid_eq(server->m.uid, GLOBAL_ROOT_UID))
337 seq_printf(seq, ",uid=%u",
338 from_kuid_munged(&init_user_ns, server->m.uid));
339 if (!gid_eq(server->m.gid, GLOBAL_ROOT_GID))
340 seq_printf(seq, ",gid=%u",
341 from_kgid_munged(&init_user_ns, server->m.gid));
342 if (!uid_eq(server->m.mounted_uid, GLOBAL_ROOT_UID))
343 seq_printf(seq, ",owner=%u",
344 from_kuid_munged(&init_user_ns, server->m.mounted_uid));
Miklos Szeredi564cd132008-02-08 04:21:46 -0800345 tmp = server->m.file_mode & S_IALLUGO;
346 if (tmp != NCP_DEFAULT_FILE_MODE)
347 seq_printf(seq, ",mode=0%o", tmp);
348 tmp = server->m.dir_mode & S_IALLUGO;
349 if (tmp != NCP_DEFAULT_DIR_MODE)
350 seq_printf(seq, ",dirmode=0%o", tmp);
351 if (server->m.time_out != NCP_DEFAULT_TIME_OUT * HZ / 100) {
352 tmp = server->m.time_out * 100 / HZ;
353 seq_printf(seq, ",timeout=%u", tmp);
354 }
355 if (server->m.retry_count != NCP_DEFAULT_RETRY_COUNT)
356 seq_printf(seq, ",retry=%u", server->m.retry_count);
357 if (server->m.flags != 0)
358 seq_printf(seq, ",flags=%lu", server->m.flags);
359 if (server->m.wdog_pid != NULL)
360 seq_printf(seq, ",wdogpid=%u", pid_vnr(server->m.wdog_pid));
361
362 return 0;
363}
364
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365static const struct ncp_option ncp_opts[] = {
366 { "uid", OPT_INT, 'u' },
367 { "gid", OPT_INT, 'g' },
368 { "owner", OPT_INT, 'o' },
369 { "mode", OPT_INT, 'm' },
370 { "dirmode", OPT_INT, 'd' },
371 { "timeout", OPT_INT, 't' },
372 { "retry", OPT_INT, 'r' },
373 { "flags", OPT_INT, 'f' },
374 { "wdogpid", OPT_INT, 'w' },
375 { "ncpfd", OPT_INT, 'n' },
376 { "infofd", OPT_INT, 'i' }, /* v5 */
377 { "version", OPT_INT, 'v' },
378 { NULL, 0, 0 } };
379
380static int ncp_parse_options(struct ncp_mount_data_kernel *data, char *options) {
381 int optval;
382 char *optarg;
383 unsigned long optint;
384 int version = 0;
Eric W. Biederman1de24122006-12-13 00:35:13 -0800385 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
387 data->flags = 0;
388 data->int_flags = 0;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800389 data->mounted_uid = GLOBAL_ROOT_UID;
Eric W. Biederman21542272006-12-13 00:35:11 -0800390 data->wdog_pid = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 data->ncp_fd = ~0;
Miklos Szeredi564cd132008-02-08 04:21:46 -0800392 data->time_out = NCP_DEFAULT_TIME_OUT;
393 data->retry_count = NCP_DEFAULT_RETRY_COUNT;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800394 data->uid = GLOBAL_ROOT_UID;
395 data->gid = GLOBAL_ROOT_GID;
Miklos Szeredi564cd132008-02-08 04:21:46 -0800396 data->file_mode = NCP_DEFAULT_FILE_MODE;
397 data->dir_mode = NCP_DEFAULT_DIR_MODE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 data->info_fd = -1;
399 data->mounted_vol[0] = 0;
400
401 while ((optval = ncp_getopt("ncpfs", &options, ncp_opts, NULL, &optarg, &optint)) != 0) {
Eric W. Biederman1de24122006-12-13 00:35:13 -0800402 ret = optval;
403 if (ret < 0)
404 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 switch (optval) {
406 case 'u':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800407 data->uid = make_kuid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800408 if (!uid_valid(data->uid)) {
409 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800410 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800411 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 break;
413 case 'g':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800414 data->gid = make_kgid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800415 if (!gid_valid(data->gid)) {
416 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800417 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800418 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 break;
420 case 'o':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800421 data->mounted_uid = make_kuid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800422 if (!uid_valid(data->mounted_uid)) {
423 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800424 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800425 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 break;
427 case 'm':
428 data->file_mode = optint;
429 break;
430 case 'd':
431 data->dir_mode = optint;
432 break;
433 case 't':
434 data->time_out = optint;
435 break;
436 case 'r':
437 data->retry_count = optint;
438 break;
439 case 'f':
440 data->flags = optint;
441 break;
442 case 'w':
Eric W. Biederman21542272006-12-13 00:35:11 -0800443 data->wdog_pid = find_get_pid(optint);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 break;
445 case 'n':
446 data->ncp_fd = optint;
447 break;
448 case 'i':
449 data->info_fd = optint;
450 break;
451 case 'v':
Eric W. Biederman1de24122006-12-13 00:35:13 -0800452 ret = -ECHRNG;
453 if (optint < NCP_MOUNT_VERSION_V4)
454 goto err;
455 if (optint > NCP_MOUNT_VERSION_V5)
456 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 version = optint;
458 break;
459
460 }
461 }
462 return 0;
Eric W. Biederman1de24122006-12-13 00:35:13 -0800463err:
464 put_pid(data->wdog_pid);
465 data->wdog_pid = NULL;
466 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467}
468
469static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
470{
471 struct ncp_mount_data_kernel data;
472 struct ncp_server *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 struct inode *root_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 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;
Al Viro44ba84062014-03-06 17:41:01 -0500542 sock = sockfd_lookup(data.ncp_fd, &error);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 if (!sock)
Al Viro44ba84062014-03-06 17:41:01 -0500544 goto out;
545
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 if (sock->type == SOCK_STREAM)
547 default_bufsize = 0xF000;
548 else
549 default_bufsize = 1024;
550
551 sb->s_flags |= MS_NODIRATIME; /* probably even noatime */
552 sb->s_maxbytes = 0xFFFFFFFFU;
553 sb->s_blocksize = 1024; /* Eh... Is this correct? */
554 sb->s_blocksize_bits = 10;
555 sb->s_magic = NCP_SUPER_MAGIC;
556 sb->s_op = &ncp_sops;
Al Viro0378c402011-01-12 17:25:03 -0500557 sb->s_d_op = &ncp_dentry_operations;
Jens Axboef1970c72010-04-22 12:31:11 +0200558 sb->s_bdi = &server->bdi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
560 server = NCP_SBP(sb);
561 memset(server, 0, sizeof(*server));
562
Christoph Hellwigb4caecd2015-01-14 10:42:32 +0100563 error = bdi_setup_and_register(&server->bdi, "ncpfs");
Jens Axboef1970c72010-04-22 12:31:11 +0200564 if (error)
Djalal Harouni759c3612011-12-13 02:47:29 +0100565 goto out_fput;
Jens Axboef1970c72010-04-22 12:31:11 +0200566
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 server->ncp_sock = sock;
568
569 if (data.info_fd != -1) {
Al Viro44ba84062014-03-06 17:41:01 -0500570 struct socket *info_sock = sockfd_lookup(data.info_fd, &error);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 if (!info_sock)
Al Viro44ba84062014-03-06 17:41:01 -0500572 goto out_bdi;
573 server->info_sock = info_sock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 error = -EBADFD;
575 if (info_sock->type != SOCK_STREAM)
576 goto out_fput2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 }
578
579/* server->lock = 0; */
Ingo Molnar8e3f9042006-03-23 03:00:43 -0800580 mutex_init(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 server->packet = NULL;
582/* server->buffer_size = 0; */
583/* server->conn_status = 0; */
584/* server->root_dentry = NULL; */
585/* server->root_setuped = 0; */
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200586 mutex_init(&server->root_setup_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587#ifdef CONFIG_NCPFS_PACKET_SIGNING
588/* server->sign_wanted = 0; */
589/* server->sign_active = 0; */
590#endif
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200591 init_rwsem(&server->auth_rwsem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 server->auth.auth_type = NCP_AUTH_NONE;
593/* server->auth.object_name_len = 0; */
594/* server->auth.object_name = NULL; */
595/* server->auth.object_type = 0; */
596/* server->priv.len = 0; */
597/* server->priv.data = NULL; */
598
599 server->m = data;
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300600 /* Although anything producing this is buggy, it happens
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 now because of PATH_MAX changes.. */
602 if (server->m.time_out < 1) {
603 server->m.time_out = 10;
Joe Perchesb41f8b82014-04-08 16:04:14 -0700604 pr_info("You need to recompile your ncpfs utils..\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 }
606 server->m.time_out = server->m.time_out * HZ / 100;
607 server->m.file_mode = (server->m.file_mode & S_IRWXUGO) | S_IFREG;
608 server->m.dir_mode = (server->m.dir_mode & S_IRWXUGO) | S_IFDIR;
609
610#ifdef CONFIG_NCPFS_NLS
611 /* load the default NLS charsets */
612 server->nls_vol = load_nls_default();
613 server->nls_io = load_nls_default();
614#endif /* CONFIG_NCPFS_NLS */
615
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200616 atomic_set(&server->dentry_ttl, 0); /* no caching */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617
618 INIT_LIST_HEAD(&server->tx.requests);
Ingo Molnar8e3f9042006-03-23 03:00:43 -0800619 mutex_init(&server->rcv.creq_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 server->tx.creq = NULL;
621 server->rcv.creq = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
623 init_timer(&server->timeout_tm);
624#undef NCP_PACKET_SIZE
625#define NCP_PACKET_SIZE 131072
626 error = -ENOMEM;
627 server->packet_size = NCP_PACKET_SIZE;
628 server->packet = vmalloc(NCP_PACKET_SIZE);
629 if (server->packet == NULL)
630 goto out_nls;
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100631 server->txbuf = vmalloc(NCP_PACKET_SIZE);
632 if (server->txbuf == NULL)
633 goto out_packet;
634 server->rxbuf = vmalloc(NCP_PACKET_SIZE);
635 if (server->rxbuf == NULL)
636 goto out_txbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200638 lock_sock(sock->sk);
639 server->data_ready = sock->sk->sk_data_ready;
640 server->write_space = sock->sk->sk_write_space;
641 server->error_report = sock->sk->sk_error_report;
642 sock->sk->sk_user_data = server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 sock->sk->sk_data_ready = ncp_tcp_data_ready;
644 sock->sk->sk_error_report = ncp_tcp_error_report;
645 if (sock->type == SOCK_STREAM) {
646 server->rcv.ptr = (unsigned char*)&server->rcv.buf;
647 server->rcv.len = 10;
648 server->rcv.state = 0;
David Howellsc4028952006-11-22 14:57:56 +0000649 INIT_WORK(&server->rcv.tq, ncp_tcp_rcv_proc);
650 INIT_WORK(&server->tx.tq, ncp_tcp_tx_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 sock->sk->sk_write_space = ncp_tcp_write_space;
652 } else {
David Howellsc4028952006-11-22 14:57:56 +0000653 INIT_WORK(&server->rcv.tq, ncpdgram_rcv_proc);
654 INIT_WORK(&server->timeout_tq, ncpdgram_timeout_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 server->timeout_tm.data = (unsigned long)server;
656 server->timeout_tm.function = ncpdgram_timeout_call;
657 }
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200658 release_sock(sock->sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659
660 ncp_lock_server(server);
661 error = ncp_connect(server);
662 ncp_unlock_server(server);
663 if (error < 0)
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100664 goto out_rxbuf;
Joe Perches15a03ac2014-04-08 16:04:18 -0700665 ncp_dbg(1, "NCP_SBP(sb) = %p\n", NCP_SBP(sb));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666
667 error = -EMSGSIZE; /* -EREMOTESIDEINCOMPATIBLE */
668#ifdef CONFIG_NCPFS_PACKET_SIGNING
669 if (ncp_negotiate_size_and_options(server, default_bufsize,
670 NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0)
671 {
672 if (options != NCP_DEFAULT_OPTIONS)
673 {
674 if (ncp_negotiate_size_and_options(server,
675 default_bufsize,
676 options & 2,
677 &(server->buffer_size), &options) != 0)
678
679 {
680 goto out_disconnect;
681 }
682 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200683 ncp_lock_server(server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 if (options & 2)
685 server->sign_wanted = 1;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200686 ncp_unlock_server(server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 }
688 else
689#endif /* CONFIG_NCPFS_PACKET_SIGNING */
690 if (ncp_negotiate_buffersize(server, default_bufsize,
691 &(server->buffer_size)) != 0)
692 goto out_disconnect;
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700693 ncp_dbg(1, "bufsize = %d\n", server->buffer_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
695 memset(&finfo, 0, sizeof(finfo));
696 finfo.i.attributes = aDIR;
697 finfo.i.dataStreamSize = 0; /* ignored */
698 finfo.i.dirEntNum = 0;
699 finfo.i.DosDirNum = 0;
700#ifdef CONFIG_NCPFS_SMALLDOS
701 finfo.i.NSCreator = NW_NS_DOS;
702#endif
703 finfo.volume = NCP_NUMBER_OF_VOLUMES;
704 /* set dates of mountpoint to Jan 1, 1986; 00:00 */
705 finfo.i.creationTime = finfo.i.modifyTime
706 = cpu_to_le16(0x0000);
707 finfo.i.creationDate = finfo.i.modifyDate
708 = finfo.i.lastAccessDate
709 = cpu_to_le16(0x0C21);
710 finfo.i.nameLen = 0;
711 finfo.i.entryName[0] = '\0';
712
713 finfo.opened = 0;
714 finfo.ino = 2; /* tradition */
715
716 server->name_space[finfo.volume] = NW_NS_DOS;
717
718 error = -ENOMEM;
719 root_inode = ncp_iget(sb, &finfo);
720 if (!root_inode)
721 goto out_disconnect;
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700722 ncp_dbg(1, "root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
Al Viro48fde702012-01-08 22:15:13 -0500723 sb->s_root = d_make_root(root_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 if (!sb->s_root)
Al Viro48fde702012-01-08 22:15:13 -0500725 goto out_disconnect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 return 0;
727
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728out_disconnect:
729 ncp_lock_server(server);
730 ncp_disconnect(server);
731 ncp_unlock_server(server);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100732out_rxbuf:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 ncp_stop_tasks(server);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100734 vfree(server->rxbuf);
735out_txbuf:
736 vfree(server->txbuf);
737out_packet:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 vfree(server->packet);
739out_nls:
740#ifdef CONFIG_NCPFS_NLS
741 unload_nls(server->nls_io);
742 unload_nls(server->nls_vol);
743#endif
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200744 mutex_destroy(&server->rcv.creq_mutex);
745 mutex_destroy(&server->root_setup_lock);
746 mutex_destroy(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747out_fput2:
Al Viro44ba84062014-03-06 17:41:01 -0500748 if (server->info_sock)
749 sockfd_put(server->info_sock);
Jens Axboef1970c72010-04-22 12:31:11 +0200750out_bdi:
Djalal Harouni759c3612011-12-13 02:47:29 +0100751 bdi_destroy(&server->bdi);
752out_fput:
Al Viro44ba84062014-03-06 17:41:01 -0500753 sockfd_put(sock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754out:
Eric W. Biederman1de24122006-12-13 00:35:13 -0800755 put_pid(data.wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 sb->s_fs_info = NULL;
757 kfree(server);
758 return error;
759}
760
Al Viro1dcddd42013-10-03 13:22:44 -0400761static void delayed_free(struct rcu_head *p)
762{
763 struct ncp_server *server = container_of(p, struct ncp_server, rcu);
764#ifdef CONFIG_NCPFS_NLS
765 /* unload the NLS charsets */
766 unload_nls(server->nls_vol);
767 unload_nls(server->nls_io);
768#endif /* CONFIG_NCPFS_NLS */
769 kfree(server);
770}
771
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772static void ncp_put_super(struct super_block *sb)
773{
774 struct ncp_server *server = NCP_SBP(sb);
775
776 ncp_lock_server(server);
777 ncp_disconnect(server);
778 ncp_unlock_server(server);
779
780 ncp_stop_tasks(server);
781
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200782 mutex_destroy(&server->rcv.creq_mutex);
783 mutex_destroy(&server->root_setup_lock);
784 mutex_destroy(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785
Al Viro44ba84062014-03-06 17:41:01 -0500786 if (server->info_sock)
787 sockfd_put(server->info_sock);
788 sockfd_put(server->ncp_sock);
Eric W. Biederman21542272006-12-13 00:35:11 -0800789 kill_pid(server->m.wdog_pid, SIGTERM, 1);
790 put_pid(server->m.wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
Jens Axboef1970c72010-04-22 12:31:11 +0200792 bdi_destroy(&server->bdi);
Pekka Enberg44db77f2006-01-14 13:21:12 -0800793 kfree(server->priv.data);
794 kfree(server->auth.object_name);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100795 vfree(server->rxbuf);
796 vfree(server->txbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 vfree(server->packet);
Al Viro1dcddd42013-10-03 13:22:44 -0400798 call_rcu(&server->rcu, delayed_free);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799}
800
David Howells726c3342006-06-23 02:02:58 -0700801static int ncp_statfs(struct dentry *dentry, struct kstatfs *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802{
803 struct dentry* d;
804 struct inode* i;
805 struct ncp_inode_info* ni;
806 struct ncp_server* s;
807 struct ncp_volume_info vi;
David Howells726c3342006-06-23 02:02:58 -0700808 struct super_block *sb = dentry->d_sb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 int err;
810 __u8 dh;
811
812 d = sb->s_root;
813 if (!d) {
814 goto dflt;
815 }
816 i = d->d_inode;
817 if (!i) {
818 goto dflt;
819 }
820 ni = NCP_FINFO(i);
821 if (!ni) {
822 goto dflt;
823 }
824 s = NCP_SBP(sb);
825 if (!s) {
826 goto dflt;
827 }
828 if (!s->m.mounted_vol[0]) {
829 goto dflt;
830 }
831
832 err = ncp_dirhandle_alloc(s, ni->volNumber, ni->DosDirNum, &dh);
833 if (err) {
834 goto dflt;
835 }
836 err = ncp_get_directory_info(s, dh, &vi);
837 ncp_dirhandle_free(s, dh);
838 if (err) {
839 goto dflt;
840 }
841 buf->f_type = NCP_SUPER_MAGIC;
842 buf->f_bsize = vi.sectors_per_block * 512;
843 buf->f_blocks = vi.total_blocks;
844 buf->f_bfree = vi.free_blocks;
845 buf->f_bavail = vi.free_blocks;
846 buf->f_files = vi.total_dir_entries;
847 buf->f_ffree = vi.available_dir_entries;
848 buf->f_namelen = 12;
849 return 0;
850
851 /* We cannot say how much disk space is left on a mounted
852 NetWare Server, because free space is distributed over
853 volumes, and the current user might have disk quotas. So
854 free space is not that simple to determine. Our decision
855 here is to err conservatively. */
856
857dflt:;
858 buf->f_type = NCP_SUPER_MAGIC;
859 buf->f_bsize = NCP_BLOCK_SIZE;
860 buf->f_blocks = 0;
861 buf->f_bfree = 0;
862 buf->f_bavail = 0;
863 buf->f_namelen = 12;
864 return 0;
865}
866
867int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
868{
869 struct inode *inode = dentry->d_inode;
870 int result = 0;
871 __le32 info_mask;
872 struct nw_modify_dos_info info;
873 struct ncp_server *server;
874
875 result = -EIO;
876
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 server = NCP_SERVER(inode);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200878 if (!server) /* How this could happen? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 goto out;
880
Al Viro338b2f52013-06-15 05:53:23 +0400881 result = -EPERM;
882 if (IS_DEADDIR(dentry->d_inode))
883 goto out;
884
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 /* ageing the dentry to force validation */
886 ncp_age_dentry(server, dentry);
887
888 result = inode_change_ok(inode, attr);
889 if (result < 0)
890 goto out;
891
892 result = -EPERM;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800893 if ((attr->ia_valid & ATTR_UID) && !uid_eq(attr->ia_uid, server->m.uid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 goto out;
895
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800896 if ((attr->ia_valid & ATTR_GID) && !gid_eq(attr->ia_gid, server->m.gid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 goto out;
898
899 if (((attr->ia_valid & ATTR_MODE) &&
900 (attr->ia_mode &
901 ~(S_IFREG | S_IFDIR | S_IRWXUGO))))
902 goto out;
903
904 info_mask = 0;
905 memset(&info, 0, sizeof(info));
906
907#if 1
908 if ((attr->ia_valid & ATTR_MODE) != 0)
909 {
910 umode_t newmode = attr->ia_mode;
911
912 info_mask |= DM_ATTRIBUTES;
913
914 if (S_ISDIR(inode->i_mode)) {
915 newmode &= server->m.dir_mode;
916 } else {
917#ifdef CONFIG_NCPFS_EXTRAS
918 if (server->m.flags & NCP_MOUNT_EXTRAS) {
919 /* any non-default execute bit set */
920 if (newmode & ~server->m.file_mode & S_IXUGO)
921 info.attributes |= aSHARED | aSYSTEM;
922 /* read for group/world and not in default file_mode */
923 else if (newmode & ~server->m.file_mode & S_IRUGO)
924 info.attributes |= aSHARED;
925 } else
926#endif
927 newmode &= server->m.file_mode;
928 }
929 if (newmode & S_IWUGO)
930 info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
931 else
932 info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
933
934#ifdef CONFIG_NCPFS_NFS_NS
935 if (ncp_is_nfs_extras(server, NCP_FINFO(inode)->volNumber)) {
936 result = ncp_modify_nfs_info(server,
937 NCP_FINFO(inode)->volNumber,
938 NCP_FINFO(inode)->dirEntNum,
939 attr->ia_mode, 0);
940 if (result != 0)
941 goto out;
942 info.attributes &= ~(aSHARED | aSYSTEM);
943 {
944 /* mark partial success */
945 struct iattr tmpattr;
946
947 tmpattr.ia_valid = ATTR_MODE;
948 tmpattr.ia_mode = attr->ia_mode;
949
Christoph Hellwig10257742010-06-04 11:30:02 +0200950 setattr_copy(inode, &tmpattr);
951 mark_inode_dirty(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 }
953 }
954#endif
955 }
956#endif
957
958 /* Do SIZE before attributes, otherwise mtime together with size does not work...
959 */
960 if ((attr->ia_valid & ATTR_SIZE) != 0) {
961 int written;
962
Joe Perches15a03ac2014-04-08 16:04:18 -0700963 ncp_dbg(1, "trying to change size to %llu\n", attr->ia_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964
965 if ((result = ncp_make_open(inode, O_WRONLY)) < 0) {
966 result = -EACCES;
967 goto out;
968 }
969 ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
970 attr->ia_size, 0, "", &written);
971
972 /* According to ndir, the changes only take effect after
973 closing the file */
974 ncp_inode_close(inode);
975 result = ncp_make_closed(inode);
976 if (result)
977 goto out;
Christoph Hellwig10257742010-06-04 11:30:02 +0200978
979 if (attr->ia_size != i_size_read(inode)) {
Marco Stornelli3e7a8062012-12-15 11:57:03 +0100980 truncate_setsize(inode, attr->ia_size);
Christoph Hellwig10257742010-06-04 11:30:02 +0200981 mark_inode_dirty(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 }
983 }
984 if ((attr->ia_valid & ATTR_CTIME) != 0) {
985 info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE);
986 ncp_date_unix2dos(attr->ia_ctime.tv_sec,
987 &info.creationTime, &info.creationDate);
988 }
989 if ((attr->ia_valid & ATTR_MTIME) != 0) {
990 info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE);
991 ncp_date_unix2dos(attr->ia_mtime.tv_sec,
992 &info.modifyTime, &info.modifyDate);
993 }
994 if ((attr->ia_valid & ATTR_ATIME) != 0) {
995 __le16 dummy;
996 info_mask |= (DM_LAST_ACCESS_DATE);
997 ncp_date_unix2dos(attr->ia_atime.tv_sec,
998 &dummy, &info.lastAccessDate);
999 }
1000 if (info_mask != 0) {
1001 result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode),
1002 inode, info_mask, &info);
1003 if (result != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) {
1005 /* NetWare seems not to allow this. I
1006 do not know why. So, just tell the
1007 user everything went fine. This is
1008 a terrible hack, but I do not know
1009 how to do this correctly. */
1010 result = 0;
1011 } else
1012 goto out;
1013 }
1014#ifdef CONFIG_NCPFS_STRONG
1015 if ((!result) && (info_mask & DM_ATTRIBUTES))
1016 NCP_FINFO(inode)->nwattr = info.attributes;
1017#endif
1018 }
Christoph Hellwig10257742010-06-04 11:30:02 +02001019 if (result)
1020 goto out;
1021
1022 setattr_copy(inode, attr);
1023 mark_inode_dirty(inode);
1024
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025out:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001026 if (result > 0)
1027 result = -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 return result;
1029}
1030
Al Viro3c26ff62010-07-25 11:46:36 +04001031static struct dentry *ncp_mount(struct file_system_type *fs_type,
1032 int flags, const char *dev_name, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033{
Al Viro3c26ff62010-07-25 11:46:36 +04001034 return mount_nodev(fs_type, flags, data, ncp_fill_super);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035}
1036
1037static struct file_system_type ncp_fs_type = {
1038 .owner = THIS_MODULE,
1039 .name = "ncpfs",
Al Viro3c26ff62010-07-25 11:46:36 +04001040 .mount = ncp_mount,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 .kill_sb = kill_anon_super,
Miklos Szeredi564cd132008-02-08 04:21:46 -08001042 .fs_flags = FS_BINARY_MOUNTDATA,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043};
Eric W. Biederman7f78e032013-03-02 19:39:14 -08001044MODULE_ALIAS_FS("ncpfs");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045
1046static int __init init_ncp_fs(void)
1047{
1048 int err;
Joe Perchesd3b73ca2014-04-08 16:04:15 -07001049 ncp_dbg(1, "called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 err = init_inodecache();
1052 if (err)
1053 goto out1;
1054 err = register_filesystem(&ncp_fs_type);
1055 if (err)
1056 goto out;
1057 return 0;
1058out:
1059 destroy_inodecache();
1060out1:
1061 return err;
1062}
1063
1064static void __exit exit_ncp_fs(void)
1065{
Joe Perchesd3b73ca2014-04-08 16:04:15 -07001066 ncp_dbg(1, "called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 unregister_filesystem(&ncp_fs_type);
1068 destroy_inodecache();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069}
1070
1071module_init(init_ncp_fs)
1072module_exit(exit_ncp_fs)
1073MODULE_LICENSE("GPL");