blob: 119accd07dd58e84b085978c1d98bcce63e9df31 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * dir.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, 1999 Wolfram Pienkoss for NLS
8 * Modified 1999 Wolfram Pienkoss for directory caching
9 * Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info
10 *
11 */
12
Linus Torvalds1da177e2005-04-16 15:20:36 -070013
14#include <linux/time.h>
15#include <linux/errno.h>
16#include <linux/stat.h>
17#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/vmalloc.h>
19#include <linux/mm.h>
Nick Piggin34286d62011-01-07 17:49:57 +110020#include <linux/namei.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <asm/uaccess.h>
22#include <asm/byteorder.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023
24#include <linux/ncp_fs.h>
25
26#include "ncplib_kernel.h"
27
28static void ncp_read_volume_list(struct file *, void *, filldir_t,
29 struct ncp_cache_control *);
30static void ncp_do_readdir(struct file *, void *, filldir_t,
31 struct ncp_cache_control *);
32
33static int ncp_readdir(struct file *, void *, filldir_t);
34
35static int ncp_create(struct inode *, struct dentry *, int, struct nameidata *);
36static struct dentry *ncp_lookup(struct inode *, struct dentry *, struct nameidata *);
37static int ncp_unlink(struct inode *, struct dentry *);
38static int ncp_mkdir(struct inode *, struct dentry *, int);
39static int ncp_rmdir(struct inode *, struct dentry *);
40static int ncp_rename(struct inode *, struct dentry *,
41 struct inode *, struct dentry *);
42static int ncp_mknod(struct inode * dir, struct dentry *dentry,
43 int mode, dev_t rdev);
44#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
45extern int ncp_symlink(struct inode *, struct dentry *, const char *);
46#else
47#define ncp_symlink NULL
48#endif
49
Arjan van de Ven4b6f5d22006-03-28 01:56:42 -080050const struct file_operations ncp_dir_operations =
Linus Torvalds1da177e2005-04-16 15:20:36 -070051{
jan Blunckca572722010-05-26 14:44:53 -070052 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 .read = generic_read_dir,
54 .readdir = ncp_readdir,
John Kacur93d84b62010-05-05 15:15:37 +020055 .unlocked_ioctl = ncp_ioctl,
Petr Vandrovec54f67f62006-09-30 23:27:55 -070056#ifdef CONFIG_COMPAT
57 .compat_ioctl = ncp_compat_ioctl,
58#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070059};
60
Arjan van de Ven92e1d5b2007-02-12 00:55:39 -080061const struct inode_operations ncp_dir_inode_operations =
Linus Torvalds1da177e2005-04-16 15:20:36 -070062{
63 .create = ncp_create,
64 .lookup = ncp_lookup,
65 .unlink = ncp_unlink,
66 .symlink = ncp_symlink,
67 .mkdir = ncp_mkdir,
68 .rmdir = ncp_rmdir,
69 .mknod = ncp_mknod,
70 .rename = ncp_rename,
71 .setattr = ncp_notify_change,
72};
73
74/*
75 * Dentry operations routines
76 */
77static int ncp_lookup_validate(struct dentry *, struct nameidata *);
Nick Pigginb1e6a012011-01-07 17:49:28 +110078static int ncp_hash_dentry(const struct dentry *, const struct inode *,
79 struct qstr *);
Nick Piggin621e1552011-01-07 17:49:27 +110080static int ncp_compare_dentry(const struct dentry *, const struct inode *,
81 const struct dentry *, const struct inode *,
82 unsigned int, const char *, const struct qstr *);
Nick Pigginfe15ce42011-01-07 17:49:23 +110083static int ncp_delete_dentry(const struct dentry *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
Al Viro0378c402011-01-12 17:25:03 -050085const struct dentry_operations ncp_dentry_operations =
Linus Torvalds1da177e2005-04-16 15:20:36 -070086{
87 .d_revalidate = ncp_lookup_validate,
88 .d_hash = ncp_hash_dentry,
89 .d_compare = ncp_compare_dentry,
90 .d_delete = ncp_delete_dentry,
91};
92
Petr Vandrovec2e54eb92010-09-27 01:47:33 +020093#define ncp_namespace(i) (NCP_SERVER(i)->name_space[NCP_FINFO(i)->volNumber])
94
95static inline int ncp_preserve_entry_case(struct inode *i, __u32 nscreator)
96{
97#ifdef CONFIG_NCPFS_SMALLDOS
98 int ns = ncp_namespace(i);
99
100 if ((ns == NW_NS_DOS)
101#ifdef CONFIG_NCPFS_OS2_NS
102 || ((ns == NW_NS_OS2) && (nscreator == NW_NS_DOS))
103#endif /* CONFIG_NCPFS_OS2_NS */
104 )
105 return 0;
106#endif /* CONFIG_NCPFS_SMALLDOS */
107 return 1;
108}
109
110#define ncp_preserve_case(i) (ncp_namespace(i) != NW_NS_DOS)
111
Nick Piggin621e1552011-01-07 17:49:27 +1100112static inline int ncp_case_sensitive(const struct inode *i)
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200113{
114#ifdef CONFIG_NCPFS_NFS_NS
Nick Piggin621e1552011-01-07 17:49:27 +1100115 return ncp_namespace(i) == NW_NS_NFS;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200116#else
117 return 0;
118#endif /* CONFIG_NCPFS_NFS_NS */
119}
120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121/*
122 * Note: leave the hash unchanged if the directory
123 * is case-sensitive.
124 */
125static int
Nick Pigginb1e6a012011-01-07 17:49:28 +1100126ncp_hash_dentry(const struct dentry *dentry, const struct inode *inode,
127 struct qstr *this)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128{
Nick Pigginb1e6a012011-01-07 17:49:28 +1100129 if (!ncp_case_sensitive(inode)) {
Nick Piggin621e1552011-01-07 17:49:27 +1100130 struct super_block *sb = dentry->d_sb;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200131 struct nls_table *t;
132 unsigned long hash;
133 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134
Nick Piggin621e1552011-01-07 17:49:27 +1100135 t = NCP_IO_TABLE(sb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 hash = init_name_hash();
137 for (i=0; i<this->len ; i++)
138 hash = partial_name_hash(ncp_tolower(t, this->name[i]),
139 hash);
140 this->hash = end_name_hash(hash);
141 }
142 return 0;
143}
144
145static int
Nick Piggin621e1552011-01-07 17:49:27 +1100146ncp_compare_dentry(const struct dentry *parent, const struct inode *pinode,
147 const struct dentry *dentry, const struct inode *inode,
148 unsigned int len, const char *str, const struct qstr *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149{
Nick Piggin621e1552011-01-07 17:49:27 +1100150 if (len != name->len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 return 1;
152
Nick Piggin621e1552011-01-07 17:49:27 +1100153 if (ncp_case_sensitive(pinode))
154 return strncmp(str, name->name, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155
Nick Piggin621e1552011-01-07 17:49:27 +1100156 return ncp_strnicmp(NCP_IO_TABLE(pinode->i_sb), str, name->name, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157}
158
159/*
160 * This is the callback from dput() when d_count is going to 0.
161 * We use this to unhash dentries with bad inodes.
162 * Closing files can be safely postponed until iput() - it's done there anyway.
163 */
164static int
Nick Pigginfe15ce42011-01-07 17:49:23 +1100165ncp_delete_dentry(const struct dentry * dentry)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166{
167 struct inode *inode = dentry->d_inode;
168
169 if (inode) {
170 if (is_bad_inode(inode))
171 return 1;
172 } else
173 {
174 /* N.B. Unhash negative dentries? */
175 }
176 return 0;
177}
178
179static inline int
180ncp_single_volume(struct ncp_server *server)
181{
182 return (server->m.mounted_vol[0] != '\0');
183}
184
185static inline int ncp_is_server_root(struct inode *inode)
186{
187 return (!ncp_single_volume(NCP_SERVER(inode)) &&
188 inode == inode->i_sb->s_root->d_inode);
189}
190
191
192/*
193 * This is the callback when the dcache has a lookup hit.
194 */
195
196
197#ifdef CONFIG_NCPFS_STRONG
198/* try to delete a readonly file (NW R bit set) */
199
200static int
201ncp_force_unlink(struct inode *dir, struct dentry* dentry)
202{
203 int res=0x9c,res2;
204 struct nw_modify_dos_info info;
205 __le32 old_nwattr;
206 struct inode *inode;
207
208 memset(&info, 0, sizeof(info));
209
210 /* remove the Read-Only flag on the NW server */
211 inode = dentry->d_inode;
212
213 old_nwattr = NCP_FINFO(inode)->nwattr;
214 info.attributes = old_nwattr & ~(aRONLY|aDELETEINHIBIT|aRENAMEINHIBIT);
215 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
216 if (res2)
217 goto leave_me;
218
219 /* now try again the delete operation */
220 res = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry);
221
222 if (res) /* delete failed, set R bit again */
223 {
224 info.attributes = old_nwattr;
225 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
226 if (res2)
227 goto leave_me;
228 }
229leave_me:
230 return(res);
231}
232#endif /* CONFIG_NCPFS_STRONG */
233
234#ifdef CONFIG_NCPFS_STRONG
235static int
236ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name,
237 struct inode *new_dir, struct dentry* new_dentry, char *_new_name)
238{
239 struct nw_modify_dos_info info;
240 int res=0x90,res2;
241 struct inode *old_inode = old_dentry->d_inode;
242 __le32 old_nwattr = NCP_FINFO(old_inode)->nwattr;
243 __le32 new_nwattr = 0; /* shut compiler warning */
244 int old_nwattr_changed = 0;
245 int new_nwattr_changed = 0;
246
247 memset(&info, 0, sizeof(info));
248
249 /* remove the Read-Only flag on the NW server */
250
251 info.attributes = old_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
252 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
253 if (!res2)
254 old_nwattr_changed = 1;
255 if (new_dentry && new_dentry->d_inode) {
256 new_nwattr = NCP_FINFO(new_dentry->d_inode)->nwattr;
257 info.attributes = new_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
258 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
259 if (!res2)
260 new_nwattr_changed = 1;
261 }
262 /* now try again the rename operation */
263 /* but only if something really happened */
264 if (new_nwattr_changed || old_nwattr_changed) {
265 res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
266 old_dir, _old_name,
267 new_dir, _new_name);
268 }
269 if (res)
270 goto leave_me;
271 /* file was successfully renamed, so:
272 do not set attributes on old file - it no longer exists
273 copy attributes from old file to new */
274 new_nwattr_changed = old_nwattr_changed;
275 new_nwattr = old_nwattr;
276 old_nwattr_changed = 0;
277
278leave_me:;
279 if (old_nwattr_changed) {
280 info.attributes = old_nwattr;
281 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
282 /* ignore errors */
283 }
284 if (new_nwattr_changed) {
285 info.attributes = new_nwattr;
286 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
287 /* ignore errors */
288 }
289 return(res);
290}
291#endif /* CONFIG_NCPFS_STRONG */
292
293
294static int
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200295ncp_lookup_validate(struct dentry *dentry, struct nameidata *nd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
297 struct ncp_server *server;
298 struct dentry *parent;
299 struct inode *dir;
300 struct ncp_entry_info finfo;
301 int res, val = 0, len;
302 __u8 __name[NCP_MAXPATHLEN + 1];
303
Al Viro0378c402011-01-12 17:25:03 -0500304 if (dentry == dentry->d_sb->s_root)
305 return 1;
306
Nick Piggin34286d62011-01-07 17:49:57 +1100307 if (nd->flags & LOOKUP_RCU)
308 return -ECHILD;
309
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 parent = dget_parent(dentry);
311 dir = parent->d_inode;
312
313 if (!dentry->d_inode)
314 goto finished;
315
316 server = NCP_SERVER(dir);
317
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 /*
319 * Inspired by smbfs:
320 * The default validation is based on dentry age:
321 * We set the max age at mount time. (But each
322 * successful server lookup renews the timestamp.)
323 */
324 val = NCP_TEST_AGE(server, dentry);
325 if (val)
326 goto finished;
327
328 DDPRINTK("ncp_lookup_validate: %s/%s not valid, age=%ld, server lookup\n",
329 dentry->d_parent->d_name.name, dentry->d_name.name,
330 NCP_GET_AGE(dentry));
331
332 len = sizeof(__name);
333 if (ncp_is_server_root(dir)) {
334 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
335 dentry->d_name.len, 1);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200336 if (!res) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 res = ncp_lookup_volume(server, __name, &(finfo.i));
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200338 if (!res)
339 ncp_update_known_namespace(server, finfo.i.volNumber, NULL);
340 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 } else {
342 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
343 dentry->d_name.len, !ncp_preserve_case(dir));
344 if (!res)
345 res = ncp_obtain_info(server, dir, __name, &(finfo.i));
346 }
347 finfo.volume = finfo.i.volNumber;
348 DDPRINTK("ncp_lookup_validate: looked for %s/%s, res=%d\n",
349 dentry->d_parent->d_name.name, __name, res);
350 /*
351 * If we didn't find it, or if it has a different dirEntNum to
352 * what we remember, it's not valid any more.
353 */
354 if (!res) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200355 struct inode *inode = dentry->d_inode;
356
357 mutex_lock(&inode->i_mutex);
358 if (finfo.i.dirEntNum == NCP_FINFO(inode)->dirEntNum) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 ncp_new_dentry(dentry);
360 val=1;
361 } else
362 DDPRINTK("ncp_lookup_validate: found, but dirEntNum changed\n");
363
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200364 ncp_update_inode2(inode, &finfo);
365 mutex_unlock(&inode->i_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 }
367
368finished:
369 DDPRINTK("ncp_lookup_validate: result=%d\n", val);
370 dput(parent);
371 return val;
372}
373
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374static struct dentry *
375ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
376{
377 struct dentry *dent = dentry;
378 struct list_head *next;
379
380 if (d_validate(dent, parent)) {
381 if (dent->d_name.len <= NCP_MAXPATHLEN &&
382 (unsigned long)dent->d_fsdata == fpos) {
383 if (!dent->d_inode) {
384 dput(dent);
385 dent = NULL;
386 }
387 return dent;
388 }
389 dput(dent);
390 }
391
392 /* If a pointer is invalid, we search the dentry. */
Nick Piggin2fd6b7f2011-01-07 17:49:34 +1100393 spin_lock(&parent->d_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 next = parent->d_subdirs.next;
395 while (next != &parent->d_subdirs) {
Eric Dumazet5160ee62006-01-08 01:03:32 -0800396 dent = list_entry(next, struct dentry, d_u.d_child);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 if ((unsigned long)dent->d_fsdata == fpos) {
398 if (dent->d_inode)
Nick Piggindc0474b2011-01-07 17:49:43 +1100399 dget(dent);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 else
401 dent = NULL;
Nick Piggin2fd6b7f2011-01-07 17:49:34 +1100402 spin_unlock(&parent->d_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 goto out;
404 }
405 next = next->next;
406 }
Nick Piggin2fd6b7f2011-01-07 17:49:34 +1100407 spin_unlock(&parent->d_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 return NULL;
409
410out:
411 return dent;
412}
413
414static time_t ncp_obtain_mtime(struct dentry *dentry)
415{
416 struct inode *inode = dentry->d_inode;
417 struct ncp_server *server = NCP_SERVER(inode);
418 struct nw_info_struct i;
419
420 if (!ncp_conn_valid(server) || ncp_is_server_root(inode))
421 return 0;
422
423 if (ncp_obtain_info(server, inode, NULL, &i))
424 return 0;
425
426 return ncp_date_dos2unix(i.modifyTime, i.modifyDate);
427}
428
429static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
430{
Josef Sipek92e5bae2006-12-08 02:37:22 -0800431 struct dentry *dentry = filp->f_path.dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 struct inode *inode = dentry->d_inode;
433 struct page *page = NULL;
434 struct ncp_server *server = NCP_SERVER(inode);
435 union ncp_dir_cache *cache = NULL;
436 struct ncp_cache_control ctl;
437 int result, mtime_valid = 0;
438 time_t mtime = 0;
439
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 ctl.page = NULL;
441 ctl.cache = NULL;
442
443 DDPRINTK("ncp_readdir: reading %s/%s, pos=%d\n",
444 dentry->d_parent->d_name.name, dentry->d_name.name,
445 (int) filp->f_pos);
446
447 result = -EIO;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200448 /* Do not generate '.' and '..' when server is dead. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 if (!ncp_conn_valid(server))
450 goto out;
451
452 result = 0;
453 if (filp->f_pos == 0) {
454 if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
455 goto out;
456 filp->f_pos = 1;
457 }
458 if (filp->f_pos == 1) {
459 if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR))
460 goto out;
461 filp->f_pos = 2;
462 }
463
464 page = grab_cache_page(&inode->i_data, 0);
465 if (!page)
466 goto read_really;
467
468 ctl.cache = cache = kmap(page);
469 ctl.head = cache->head;
470
471 if (!PageUptodate(page) || !ctl.head.eof)
472 goto init_cache;
473
474 if (filp->f_pos == 2) {
475 if (jiffies - ctl.head.time >= NCP_MAX_AGE(server))
476 goto init_cache;
477
478 mtime = ncp_obtain_mtime(dentry);
479 mtime_valid = 1;
480 if ((!mtime) || (mtime != ctl.head.mtime))
481 goto init_cache;
482 }
483
484 if (filp->f_pos > ctl.head.end)
485 goto finished;
486
487 ctl.fpos = filp->f_pos + (NCP_DIRCACHE_START - 2);
488 ctl.ofs = ctl.fpos / NCP_DIRCACHE_SIZE;
489 ctl.idx = ctl.fpos % NCP_DIRCACHE_SIZE;
490
491 for (;;) {
492 if (ctl.ofs != 0) {
493 ctl.page = find_lock_page(&inode->i_data, ctl.ofs);
494 if (!ctl.page)
495 goto invalid_cache;
496 ctl.cache = kmap(ctl.page);
497 if (!PageUptodate(ctl.page))
498 goto invalid_cache;
499 }
500 while (ctl.idx < NCP_DIRCACHE_SIZE) {
501 struct dentry *dent;
502 int res;
503
504 dent = ncp_dget_fpos(ctl.cache->dentry[ctl.idx],
505 dentry, filp->f_pos);
506 if (!dent)
507 goto invalid_cache;
508 res = filldir(dirent, dent->d_name.name,
509 dent->d_name.len, filp->f_pos,
510 dent->d_inode->i_ino, DT_UNKNOWN);
511 dput(dent);
512 if (res)
513 goto finished;
514 filp->f_pos += 1;
515 ctl.idx += 1;
516 if (filp->f_pos > ctl.head.end)
517 goto finished;
518 }
519 if (ctl.page) {
520 kunmap(ctl.page);
521 SetPageUptodate(ctl.page);
522 unlock_page(ctl.page);
523 page_cache_release(ctl.page);
524 ctl.page = NULL;
525 }
526 ctl.idx = 0;
527 ctl.ofs += 1;
528 }
529invalid_cache:
530 if (ctl.page) {
531 kunmap(ctl.page);
532 unlock_page(ctl.page);
533 page_cache_release(ctl.page);
534 ctl.page = NULL;
535 }
536 ctl.cache = cache;
537init_cache:
538 ncp_invalidate_dircache_entries(dentry);
539 if (!mtime_valid) {
540 mtime = ncp_obtain_mtime(dentry);
541 mtime_valid = 1;
542 }
543 ctl.head.mtime = mtime;
544 ctl.head.time = jiffies;
545 ctl.head.eof = 0;
546 ctl.fpos = 2;
547 ctl.ofs = 0;
548 ctl.idx = NCP_DIRCACHE_START;
549 ctl.filled = 0;
550 ctl.valid = 1;
551read_really:
552 if (ncp_is_server_root(inode)) {
553 ncp_read_volume_list(filp, dirent, filldir, &ctl);
554 } else {
555 ncp_do_readdir(filp, dirent, filldir, &ctl);
556 }
557 ctl.head.end = ctl.fpos - 1;
558 ctl.head.eof = ctl.valid;
559finished:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200560 if (ctl.page) {
561 kunmap(ctl.page);
562 SetPageUptodate(ctl.page);
563 unlock_page(ctl.page);
564 page_cache_release(ctl.page);
565 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 if (page) {
567 cache->head = ctl.head;
568 kunmap(page);
569 SetPageUptodate(page);
570 unlock_page(page);
571 page_cache_release(page);
572 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 return result;
575}
576
577static int
578ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200579 struct ncp_cache_control *ctrl, struct ncp_entry_info *entry,
580 int inval_childs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581{
Josef Sipek92e5bae2006-12-08 02:37:22 -0800582 struct dentry *newdent, *dentry = filp->f_path.dentry;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200583 struct inode *dir = dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 struct ncp_cache_control ctl = *ctrl;
585 struct qstr qname;
586 int valid = 0;
587 int hashed = 0;
588 ino_t ino = 0;
589 __u8 __name[NCP_MAXPATHLEN + 1];
590
591 qname.len = sizeof(__name);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200592 if (ncp_vol2io(NCP_SERVER(dir), __name, &qname.len,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 entry->i.entryName, entry->i.nameLen,
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200594 !ncp_preserve_entry_case(dir, entry->i.NSCreator)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 return 1; /* I'm not sure */
596
597 qname.name = __name;
598 qname.hash = full_name_hash(qname.name, qname.len);
599
600 if (dentry->d_op && dentry->d_op->d_hash)
Nick Pigginb1e6a012011-01-07 17:49:28 +1100601 if (dentry->d_op->d_hash(dentry, dentry->d_inode, &qname) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 goto end_advance;
603
604 newdent = d_lookup(dentry, &qname);
605
606 if (!newdent) {
607 newdent = d_alloc(dentry, &qname);
608 if (!newdent)
609 goto end_advance;
610 } else {
611 hashed = 1;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200612
613 /* If case sensitivity changed for this volume, all entries below this one
614 should be thrown away. This entry itself is not affected, as its case
615 sensitivity is controlled by its own parent. */
616 if (inval_childs)
617 shrink_dcache_parent(newdent);
618
619 /*
Nick Pigginfb2d5b82011-01-07 17:49:26 +1100620 * NetWare's OS2 namespace is case preserving yet case
621 * insensitive. So we update dentry's name as received from
622 * server. Parent dir's i_mutex is locked because we're in
623 * readdir.
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200624 */
Nick Pigginfb2d5b82011-01-07 17:49:26 +1100625 dentry_update_name_case(newdent, &qname);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 }
627
628 if (!newdent->d_inode) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200629 struct inode *inode;
630
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 entry->opened = 0;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200632 entry->ino = iunique(dir->i_sb, 2);
633 inode = ncp_iget(dir->i_sb, entry);
634 if (inode) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200635 d_instantiate(newdent, inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 if (!hashed)
637 d_rehash(newdent);
638 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200639 } else {
640 struct inode *inode = newdent->d_inode;
641
Nick Pigginfb2d5b82011-01-07 17:49:26 +1100642 mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200643 ncp_update_inode2(inode, entry);
644 mutex_unlock(&inode->i_mutex);
645 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646
647 if (newdent->d_inode) {
648 ino = newdent->d_inode->i_ino;
649 newdent->d_fsdata = (void *) ctl.fpos;
650 ncp_new_dentry(newdent);
651 }
652
653 if (ctl.idx >= NCP_DIRCACHE_SIZE) {
654 if (ctl.page) {
655 kunmap(ctl.page);
656 SetPageUptodate(ctl.page);
657 unlock_page(ctl.page);
658 page_cache_release(ctl.page);
659 }
660 ctl.cache = NULL;
661 ctl.idx -= NCP_DIRCACHE_SIZE;
662 ctl.ofs += 1;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200663 ctl.page = grab_cache_page(&dir->i_data, ctl.ofs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 if (ctl.page)
665 ctl.cache = kmap(ctl.page);
666 }
667 if (ctl.cache) {
668 ctl.cache->dentry[ctl.idx] = newdent;
669 valid = 1;
670 }
671 dput(newdent);
672end_advance:
673 if (!valid)
674 ctl.valid = 0;
675 if (!ctl.filled && (ctl.fpos == filp->f_pos)) {
676 if (!ino)
677 ino = find_inode_number(dentry, &qname);
678 if (!ino)
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200679 ino = iunique(dir->i_sb, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 ctl.filled = filldir(dirent, qname.name, qname.len,
681 filp->f_pos, ino, DT_UNKNOWN);
682 if (!ctl.filled)
683 filp->f_pos += 1;
684 }
685 ctl.fpos += 1;
686 ctl.idx += 1;
687 *ctrl = ctl;
688 return (ctl.valid || !ctl.filled);
689}
690
691static void
692ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir,
693 struct ncp_cache_control *ctl)
694{
Josef Sipek92e5bae2006-12-08 02:37:22 -0800695 struct dentry *dentry = filp->f_path.dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 struct inode *inode = dentry->d_inode;
697 struct ncp_server *server = NCP_SERVER(inode);
698 struct ncp_volume_info info;
699 struct ncp_entry_info entry;
700 int i;
701
702 DPRINTK("ncp_read_volume_list: pos=%ld\n",
703 (unsigned long) filp->f_pos);
704
705 for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200706 int inval_dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707
708 if (ncp_get_volume_info_with_number(server, i, &info) != 0)
709 return;
710 if (!strlen(info.volume_name))
711 continue;
712
713 DPRINTK("ncp_read_volume_list: found vol: %s\n",
714 info.volume_name);
715
716 if (ncp_lookup_volume(server, info.volume_name,
717 &entry.i)) {
718 DPRINTK("ncpfs: could not lookup vol %s\n",
719 info.volume_name);
720 continue;
721 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200722 inval_dentry = ncp_update_known_namespace(server, entry.i.volNumber, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 entry.volume = entry.i.volNumber;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200724 if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, inval_dentry))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 return;
726 }
727}
728
729static void
730ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir,
731 struct ncp_cache_control *ctl)
732{
Josef Sipek92e5bae2006-12-08 02:37:22 -0800733 struct dentry *dentry = filp->f_path.dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 struct inode *dir = dentry->d_inode;
735 struct ncp_server *server = NCP_SERVER(dir);
736 struct nw_search_sequence seq;
737 struct ncp_entry_info entry;
738 int err;
739 void* buf;
740 int more;
741 size_t bufsize;
742
743 DPRINTK("ncp_do_readdir: %s/%s, fpos=%ld\n",
744 dentry->d_parent->d_name.name, dentry->d_name.name,
745 (unsigned long) filp->f_pos);
746 PPRINTK("ncp_do_readdir: init %s, volnum=%d, dirent=%u\n",
747 dentry->d_name.name, NCP_FINFO(dir)->volNumber,
748 NCP_FINFO(dir)->dirEntNum);
749
750 err = ncp_initialize_search(server, dir, &seq);
751 if (err) {
752 DPRINTK("ncp_do_readdir: init failed, err=%d\n", err);
753 return;
754 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 /* We MUST NOT use server->buffer_size handshaked with server if we are
756 using UDP, as for UDP server uses max. buffer size determined by
757 MTU, and for TCP server uses hardwired value 65KB (== 66560 bytes).
758 So we use 128KB, just to be sure, as there is no way how to know
759 this value in advance. */
760 bufsize = 131072;
761 buf = vmalloc(bufsize);
762 if (!buf)
763 return;
764 do {
765 int cnt;
766 char* rpl;
767 size_t rpls;
768
769 err = ncp_search_for_fileset(server, &seq, &more, &cnt, buf, bufsize, &rpl, &rpls);
770 if (err) /* Error */
771 break;
772 if (!cnt) /* prevent endless loop */
773 break;
774 while (cnt--) {
775 size_t onerpl;
776
777 if (rpls < offsetof(struct nw_info_struct, entryName))
778 break; /* short packet */
779 ncp_extract_file_info(rpl, &entry.i);
780 onerpl = offsetof(struct nw_info_struct, entryName) + entry.i.nameLen;
781 if (rpls < onerpl)
782 break; /* short packet */
783 (void)ncp_obtain_nfs_info(server, &entry.i);
784 rpl += onerpl;
785 rpls -= onerpl;
786 entry.volume = entry.i.volNumber;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200787 if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 break;
789 }
790 } while (more);
791 vfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 return;
793}
794
795int ncp_conn_logged_in(struct super_block *sb)
796{
797 struct ncp_server* server = NCP_SBP(sb);
798 int result;
799
800 if (ncp_single_volume(server)) {
801 int len;
802 struct dentry* dent;
803 __u32 volNumber;
804 __le32 dirEntNum;
805 __le32 DosDirNum;
806 __u8 __name[NCP_MAXPATHLEN + 1];
807
808 len = sizeof(__name);
809 result = ncp_io2vol(server, __name, &len, server->m.mounted_vol,
810 strlen(server->m.mounted_vol), 1);
811 if (result)
812 goto out;
813 result = -ENOENT;
814 if (ncp_get_volume_root(server, __name, &volNumber, &dirEntNum, &DosDirNum)) {
815 PPRINTK("ncp_conn_logged_in: %s not found\n",
816 server->m.mounted_vol);
817 goto out;
818 }
819 dent = sb->s_root;
820 if (dent) {
821 struct inode* ino = dent->d_inode;
822 if (ino) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200823 ncp_update_known_namespace(server, volNumber, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 NCP_FINFO(ino)->volNumber = volNumber;
825 NCP_FINFO(ino)->dirEntNum = dirEntNum;
826 NCP_FINFO(ino)->DosDirNum = DosDirNum;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200827 result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 } else {
829 DPRINTK("ncpfs: sb->s_root->d_inode == NULL!\n");
830 }
831 } else {
832 DPRINTK("ncpfs: sb->s_root == NULL!\n");
833 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200834 } else
835 result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
837out:
838 return result;
839}
840
841static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
842{
843 struct ncp_server *server = NCP_SERVER(dir);
844 struct inode *inode = NULL;
845 struct ncp_entry_info finfo;
846 int error, res, len;
847 __u8 __name[NCP_MAXPATHLEN + 1];
848
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 error = -EIO;
850 if (!ncp_conn_valid(server))
851 goto finished;
852
853 PPRINTK("ncp_lookup: server lookup for %s/%s\n",
854 dentry->d_parent->d_name.name, dentry->d_name.name);
855
856 len = sizeof(__name);
857 if (ncp_is_server_root(dir)) {
858 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
859 dentry->d_name.len, 1);
860 if (!res)
861 res = ncp_lookup_volume(server, __name, &(finfo.i));
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200862 if (!res)
863 ncp_update_known_namespace(server, finfo.i.volNumber, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 } else {
865 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
866 dentry->d_name.len, !ncp_preserve_case(dir));
867 if (!res)
868 res = ncp_obtain_info(server, dir, __name, &(finfo.i));
869 }
870 PPRINTK("ncp_lookup: looked for %s/%s, res=%d\n",
871 dentry->d_parent->d_name.name, __name, res);
872 /*
873 * If we didn't find an entry, make a negative dentry.
874 */
875 if (res)
876 goto add_entry;
877
878 /*
879 * Create an inode for the entry.
880 */
881 finfo.opened = 0;
882 finfo.ino = iunique(dir->i_sb, 2);
883 finfo.volume = finfo.i.volNumber;
884 error = -EACCES;
885 inode = ncp_iget(dir->i_sb, &finfo);
886
887 if (inode) {
888 ncp_new_dentry(dentry);
889add_entry:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 d_add(dentry, inode);
891 error = 0;
892 }
893
894finished:
895 PPRINTK("ncp_lookup: result=%d\n", error);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 return ERR_PTR(error);
897}
898
899/*
900 * This code is common to create, mkdir, and mknod.
901 */
902static int ncp_instantiate(struct inode *dir, struct dentry *dentry,
903 struct ncp_entry_info *finfo)
904{
905 struct inode *inode;
906 int error = -EINVAL;
907
908 finfo->ino = iunique(dir->i_sb, 2);
909 inode = ncp_iget(dir->i_sb, finfo);
910 if (!inode)
911 goto out_close;
912 d_instantiate(dentry,inode);
913 error = 0;
914out:
915 return error;
916
917out_close:
918 PPRINTK("ncp_instantiate: %s/%s failed, closing file\n",
919 dentry->d_parent->d_name.name, dentry->d_name.name);
920 ncp_close_file(NCP_SERVER(dir), finfo->file_handle);
921 goto out;
922}
923
924int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode,
925 dev_t rdev, __le32 attributes)
926{
927 struct ncp_server *server = NCP_SERVER(dir);
928 struct ncp_entry_info finfo;
929 int error, result, len;
930 int opmode;
931 __u8 __name[NCP_MAXPATHLEN + 1];
932
933 PPRINTK("ncp_create_new: creating %s/%s, mode=%x\n",
934 dentry->d_parent->d_name.name, dentry->d_name.name, mode);
935
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 ncp_age_dentry(server, dentry);
937 len = sizeof(__name);
938 error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
939 dentry->d_name.len, !ncp_preserve_case(dir));
940 if (error)
941 goto out;
942
943 error = -EACCES;
944
945 if (S_ISREG(mode) &&
946 (server->m.flags & NCP_MOUNT_EXTRAS) &&
947 (mode & S_IXUGO))
948 attributes |= aSYSTEM | aSHARED;
949
950 result = ncp_open_create_file_or_subdir(server, dir, __name,
951 OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
952 attributes, AR_READ | AR_WRITE, &finfo);
953 opmode = O_RDWR;
954 if (result) {
955 result = ncp_open_create_file_or_subdir(server, dir, __name,
956 OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
957 attributes, AR_WRITE, &finfo);
958 if (result) {
959 if (result == 0x87)
960 error = -ENAMETOOLONG;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200961 else if (result < 0)
962 error = result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 DPRINTK("ncp_create: %s/%s failed\n",
964 dentry->d_parent->d_name.name, dentry->d_name.name);
965 goto out;
966 }
967 opmode = O_WRONLY;
968 }
969 finfo.access = opmode;
970 if (ncp_is_nfs_extras(server, finfo.volume)) {
971 finfo.i.nfs.mode = mode;
972 finfo.i.nfs.rdev = new_encode_dev(rdev);
973 if (ncp_modify_nfs_info(server, finfo.volume,
974 finfo.i.dirEntNum,
975 mode, new_encode_dev(rdev)) != 0)
976 goto out;
977 }
978
979 error = ncp_instantiate(dir, dentry, &finfo);
980out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 return error;
982}
983
984static int ncp_create(struct inode *dir, struct dentry *dentry, int mode,
985 struct nameidata *nd)
986{
987 return ncp_create_new(dir, dentry, mode, 0, 0);
988}
989
990static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode)
991{
992 struct ncp_entry_info finfo;
993 struct ncp_server *server = NCP_SERVER(dir);
994 int error, len;
995 __u8 __name[NCP_MAXPATHLEN + 1];
996
997 DPRINTK("ncp_mkdir: making %s/%s\n",
998 dentry->d_parent->d_name.name, dentry->d_name.name);
999
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 ncp_age_dentry(server, dentry);
1001 len = sizeof(__name);
1002 error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
1003 dentry->d_name.len, !ncp_preserve_case(dir));
1004 if (error)
1005 goto out;
1006
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001007 error = ncp_open_create_file_or_subdir(server, dir, __name,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 OC_MODE_CREATE, aDIR,
1009 cpu_to_le16(0xffff),
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001010 &finfo);
1011 if (error == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 if (ncp_is_nfs_extras(server, finfo.volume)) {
1013 mode |= S_IFDIR;
1014 finfo.i.nfs.mode = mode;
1015 if (ncp_modify_nfs_info(server,
1016 finfo.volume,
1017 finfo.i.dirEntNum,
1018 mode, 0) != 0)
1019 goto out;
1020 }
1021 error = ncp_instantiate(dir, dentry, &finfo);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001022 } else if (error > 0) {
1023 error = -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 }
1025out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 return error;
1027}
1028
1029static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
1030{
1031 struct ncp_server *server = NCP_SERVER(dir);
1032 int error, result, len;
1033 __u8 __name[NCP_MAXPATHLEN + 1];
1034
1035 DPRINTK("ncp_rmdir: removing %s/%s\n",
1036 dentry->d_parent->d_name.name, dentry->d_name.name);
1037
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 error = -EBUSY;
1039 if (!d_unhashed(dentry))
1040 goto out;
1041
1042 len = sizeof(__name);
1043 error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
1044 dentry->d_name.len, !ncp_preserve_case(dir));
1045 if (error)
1046 goto out;
1047
1048 result = ncp_del_file_or_subdir(server, dir, __name);
1049 switch (result) {
1050 case 0x00:
1051 error = 0;
1052 break;
1053 case 0x85: /* unauthorized to delete file */
1054 case 0x8A: /* unauthorized to delete file */
1055 error = -EACCES;
1056 break;
1057 case 0x8F:
1058 case 0x90: /* read only */
1059 error = -EPERM;
1060 break;
1061 case 0x9F: /* in use by another client */
1062 error = -EBUSY;
1063 break;
1064 case 0xA0: /* directory not empty */
1065 error = -ENOTEMPTY;
1066 break;
1067 case 0xFF: /* someone deleted file */
1068 error = -ENOENT;
1069 break;
1070 default:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001071 error = result < 0 ? result : -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 break;
1073 }
1074out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 return error;
1076}
1077
1078static int ncp_unlink(struct inode *dir, struct dentry *dentry)
1079{
1080 struct inode *inode = dentry->d_inode;
1081 struct ncp_server *server;
1082 int error;
1083
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 server = NCP_SERVER(dir);
1085 DPRINTK("ncp_unlink: unlinking %s/%s\n",
1086 dentry->d_parent->d_name.name, dentry->d_name.name);
1087
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 /*
1089 * Check whether to close the file ...
1090 */
1091 if (inode) {
1092 PPRINTK("ncp_unlink: closing file\n");
1093 ncp_make_closed(inode);
1094 }
1095
1096 error = ncp_del_file_or_subdir2(server, dentry);
1097#ifdef CONFIG_NCPFS_STRONG
1098 /* 9C is Invalid path.. It should be 8F, 90 - read only, but
1099 it is not :-( */
1100 if ((error == 0x9C || error == 0x90) && server->m.flags & NCP_MOUNT_STRONG) { /* R/O */
1101 error = ncp_force_unlink(dir, dentry);
1102 }
1103#endif
1104 switch (error) {
1105 case 0x00:
1106 DPRINTK("ncp: removed %s/%s\n",
1107 dentry->d_parent->d_name.name, dentry->d_name.name);
1108 break;
1109 case 0x85:
1110 case 0x8A:
1111 error = -EACCES;
1112 break;
1113 case 0x8D: /* some files in use */
1114 case 0x8E: /* all files in use */
1115 error = -EBUSY;
1116 break;
1117 case 0x8F: /* some read only */
1118 case 0x90: /* all read only */
1119 case 0x9C: /* !!! returned when in-use or read-only by NW4 */
1120 error = -EPERM;
1121 break;
1122 case 0xFF:
1123 error = -ENOENT;
1124 break;
1125 default:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001126 error = error < 0 ? error : -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 break;
1128 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 return error;
1130}
1131
1132static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
1133 struct inode *new_dir, struct dentry *new_dentry)
1134{
1135 struct ncp_server *server = NCP_SERVER(old_dir);
1136 int error;
1137 int old_len, new_len;
1138 __u8 __old_name[NCP_MAXPATHLEN + 1], __new_name[NCP_MAXPATHLEN + 1];
1139
1140 DPRINTK("ncp_rename: %s/%s to %s/%s\n",
1141 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
1142 new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
1143
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 ncp_age_dentry(server, old_dentry);
1145 ncp_age_dentry(server, new_dentry);
1146
1147 old_len = sizeof(__old_name);
1148 error = ncp_io2vol(server, __old_name, &old_len,
1149 old_dentry->d_name.name, old_dentry->d_name.len,
1150 !ncp_preserve_case(old_dir));
1151 if (error)
1152 goto out;
1153
1154 new_len = sizeof(__new_name);
1155 error = ncp_io2vol(server, __new_name, &new_len,
1156 new_dentry->d_name.name, new_dentry->d_name.len,
1157 !ncp_preserve_case(new_dir));
1158 if (error)
1159 goto out;
1160
1161 error = ncp_ren_or_mov_file_or_subdir(server, old_dir, __old_name,
1162 new_dir, __new_name);
1163#ifdef CONFIG_NCPFS_STRONG
1164 if ((error == 0x90 || error == 0x8B || error == -EACCES) &&
1165 server->m.flags & NCP_MOUNT_STRONG) { /* RO */
1166 error = ncp_force_rename(old_dir, old_dentry, __old_name,
1167 new_dir, new_dentry, __new_name);
1168 }
1169#endif
1170 switch (error) {
1171 case 0x00:
1172 DPRINTK("ncp renamed %s -> %s.\n",
1173 old_dentry->d_name.name,new_dentry->d_name.name);
1174 break;
1175 case 0x9E:
1176 error = -ENAMETOOLONG;
1177 break;
1178 case 0xFF:
1179 error = -ENOENT;
1180 break;
1181 default:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001182 error = error < 0 ? error : -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 break;
1184 }
1185out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 return error;
1187}
1188
1189static int ncp_mknod(struct inode * dir, struct dentry *dentry,
1190 int mode, dev_t rdev)
1191{
1192 if (!new_valid_dev(rdev))
1193 return -EINVAL;
1194 if (ncp_is_nfs_extras(NCP_SERVER(dir), NCP_FINFO(dir)->volNumber)) {
1195 DPRINTK(KERN_DEBUG "ncp_mknod: mode = 0%o\n", mode);
1196 return ncp_create_new(dir, dentry, mode, rdev, 0);
1197 }
1198 return -EPERM; /* Strange, but true */
1199}
1200
1201/* The following routines are taken directly from msdos-fs */
1202
1203/* Linear day numbers of the respective 1sts in non-leap years. */
1204
1205static int day_n[] =
1206{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0};
1207/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
1208
1209
1210extern struct timezone sys_tz;
1211
1212static int utc2local(int time)
1213{
1214 return time - sys_tz.tz_minuteswest * 60;
1215}
1216
1217static int local2utc(int time)
1218{
1219 return time + sys_tz.tz_minuteswest * 60;
1220}
1221
1222/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
1223int
1224ncp_date_dos2unix(__le16 t, __le16 d)
1225{
1226 unsigned short time = le16_to_cpu(t), date = le16_to_cpu(d);
1227 int month, year, secs;
1228
1229 /* first subtract and mask after that... Otherwise, if
1230 date == 0, bad things happen */
1231 month = ((date >> 5) - 1) & 15;
1232 year = date >> 9;
1233 secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 +
1234 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) +
1235 year * 365 - ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653);
1236 /* days since 1.1.70 plus 80's leap day */
1237 return local2utc(secs);
1238}
1239
1240
1241/* Convert linear UNIX date to a MS-DOS time/date pair. */
1242void
1243ncp_date_unix2dos(int unix_date, __le16 *time, __le16 *date)
1244{
1245 int day, year, nl_day, month;
1246
1247 unix_date = utc2local(unix_date);
1248 *time = cpu_to_le16(
1249 (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) +
1250 (((unix_date / 3600) % 24) << 11));
1251 day = unix_date / 86400 - 3652;
1252 year = day / 365;
1253 if ((year + 3) / 4 + 365 * year > day)
1254 year--;
1255 day -= (year + 3) / 4 + 365 * year;
1256 if (day == 59 && !(year & 3)) {
1257 nl_day = day;
1258 month = 2;
1259 } else {
1260 nl_day = (year & 3) || day <= 59 ? day : day - 1;
Roel Kluinc5df5912009-09-22 16:45:54 -07001261 for (month = 1; month < 12; month++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 if (day_n[month] > nl_day)
1263 break;
1264 }
1265 *date = cpu_to_le16(nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9));
1266}