blob: 28f136d4aaec63321b895bcce5138b93bae5f4eb [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 Viroe16404e2009-02-20 05:55:13 +000085static const 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
Al Viroe16404e2009-02-20 05:55:13 +000093const struct dentry_operations ncp_root_dentry_operations =
Linus Torvalds1da177e2005-04-16 15:20:36 -070094{
95 .d_hash = ncp_hash_dentry,
96 .d_compare = ncp_compare_dentry,
97 .d_delete = ncp_delete_dentry,
98};
99
100
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200101#define ncp_namespace(i) (NCP_SERVER(i)->name_space[NCP_FINFO(i)->volNumber])
102
103static inline int ncp_preserve_entry_case(struct inode *i, __u32 nscreator)
104{
105#ifdef CONFIG_NCPFS_SMALLDOS
106 int ns = ncp_namespace(i);
107
108 if ((ns == NW_NS_DOS)
109#ifdef CONFIG_NCPFS_OS2_NS
110 || ((ns == NW_NS_OS2) && (nscreator == NW_NS_DOS))
111#endif /* CONFIG_NCPFS_OS2_NS */
112 )
113 return 0;
114#endif /* CONFIG_NCPFS_SMALLDOS */
115 return 1;
116}
117
118#define ncp_preserve_case(i) (ncp_namespace(i) != NW_NS_DOS)
119
Nick Piggin621e1552011-01-07 17:49:27 +1100120static inline int ncp_case_sensitive(const struct inode *i)
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200121{
122#ifdef CONFIG_NCPFS_NFS_NS
Nick Piggin621e1552011-01-07 17:49:27 +1100123 return ncp_namespace(i) == NW_NS_NFS;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200124#else
125 return 0;
126#endif /* CONFIG_NCPFS_NFS_NS */
127}
128
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129/*
130 * Note: leave the hash unchanged if the directory
131 * is case-sensitive.
132 */
133static int
Nick Pigginb1e6a012011-01-07 17:49:28 +1100134ncp_hash_dentry(const struct dentry *dentry, const struct inode *inode,
135 struct qstr *this)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136{
Nick Pigginb1e6a012011-01-07 17:49:28 +1100137 if (!ncp_case_sensitive(inode)) {
Nick Piggin621e1552011-01-07 17:49:27 +1100138 struct super_block *sb = dentry->d_sb;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200139 struct nls_table *t;
140 unsigned long hash;
141 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142
Nick Piggin621e1552011-01-07 17:49:27 +1100143 t = NCP_IO_TABLE(sb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 hash = init_name_hash();
145 for (i=0; i<this->len ; i++)
146 hash = partial_name_hash(ncp_tolower(t, this->name[i]),
147 hash);
148 this->hash = end_name_hash(hash);
149 }
150 return 0;
151}
152
153static int
Nick Piggin621e1552011-01-07 17:49:27 +1100154ncp_compare_dentry(const struct dentry *parent, const struct inode *pinode,
155 const struct dentry *dentry, const struct inode *inode,
156 unsigned int len, const char *str, const struct qstr *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157{
Nick Piggin621e1552011-01-07 17:49:27 +1100158 if (len != name->len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 return 1;
160
Nick Piggin621e1552011-01-07 17:49:27 +1100161 if (ncp_case_sensitive(pinode))
162 return strncmp(str, name->name, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
Nick Piggin621e1552011-01-07 17:49:27 +1100164 return ncp_strnicmp(NCP_IO_TABLE(pinode->i_sb), str, name->name, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165}
166
167/*
168 * This is the callback from dput() when d_count is going to 0.
169 * We use this to unhash dentries with bad inodes.
170 * Closing files can be safely postponed until iput() - it's done there anyway.
171 */
172static int
Nick Pigginfe15ce42011-01-07 17:49:23 +1100173ncp_delete_dentry(const struct dentry * dentry)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174{
175 struct inode *inode = dentry->d_inode;
176
177 if (inode) {
178 if (is_bad_inode(inode))
179 return 1;
180 } else
181 {
182 /* N.B. Unhash negative dentries? */
183 }
184 return 0;
185}
186
187static inline int
188ncp_single_volume(struct ncp_server *server)
189{
190 return (server->m.mounted_vol[0] != '\0');
191}
192
193static inline int ncp_is_server_root(struct inode *inode)
194{
195 return (!ncp_single_volume(NCP_SERVER(inode)) &&
196 inode == inode->i_sb->s_root->d_inode);
197}
198
199
200/*
201 * This is the callback when the dcache has a lookup hit.
202 */
203
204
205#ifdef CONFIG_NCPFS_STRONG
206/* try to delete a readonly file (NW R bit set) */
207
208static int
209ncp_force_unlink(struct inode *dir, struct dentry* dentry)
210{
211 int res=0x9c,res2;
212 struct nw_modify_dos_info info;
213 __le32 old_nwattr;
214 struct inode *inode;
215
216 memset(&info, 0, sizeof(info));
217
218 /* remove the Read-Only flag on the NW server */
219 inode = dentry->d_inode;
220
221 old_nwattr = NCP_FINFO(inode)->nwattr;
222 info.attributes = old_nwattr & ~(aRONLY|aDELETEINHIBIT|aRENAMEINHIBIT);
223 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
224 if (res2)
225 goto leave_me;
226
227 /* now try again the delete operation */
228 res = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry);
229
230 if (res) /* delete failed, set R bit again */
231 {
232 info.attributes = old_nwattr;
233 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
234 if (res2)
235 goto leave_me;
236 }
237leave_me:
238 return(res);
239}
240#endif /* CONFIG_NCPFS_STRONG */
241
242#ifdef CONFIG_NCPFS_STRONG
243static int
244ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name,
245 struct inode *new_dir, struct dentry* new_dentry, char *_new_name)
246{
247 struct nw_modify_dos_info info;
248 int res=0x90,res2;
249 struct inode *old_inode = old_dentry->d_inode;
250 __le32 old_nwattr = NCP_FINFO(old_inode)->nwattr;
251 __le32 new_nwattr = 0; /* shut compiler warning */
252 int old_nwattr_changed = 0;
253 int new_nwattr_changed = 0;
254
255 memset(&info, 0, sizeof(info));
256
257 /* remove the Read-Only flag on the NW server */
258
259 info.attributes = old_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
260 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
261 if (!res2)
262 old_nwattr_changed = 1;
263 if (new_dentry && new_dentry->d_inode) {
264 new_nwattr = NCP_FINFO(new_dentry->d_inode)->nwattr;
265 info.attributes = new_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
266 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
267 if (!res2)
268 new_nwattr_changed = 1;
269 }
270 /* now try again the rename operation */
271 /* but only if something really happened */
272 if (new_nwattr_changed || old_nwattr_changed) {
273 res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
274 old_dir, _old_name,
275 new_dir, _new_name);
276 }
277 if (res)
278 goto leave_me;
279 /* file was successfully renamed, so:
280 do not set attributes on old file - it no longer exists
281 copy attributes from old file to new */
282 new_nwattr_changed = old_nwattr_changed;
283 new_nwattr = old_nwattr;
284 old_nwattr_changed = 0;
285
286leave_me:;
287 if (old_nwattr_changed) {
288 info.attributes = old_nwattr;
289 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
290 /* ignore errors */
291 }
292 if (new_nwattr_changed) {
293 info.attributes = new_nwattr;
294 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
295 /* ignore errors */
296 }
297 return(res);
298}
299#endif /* CONFIG_NCPFS_STRONG */
300
301
302static int
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200303ncp_lookup_validate(struct dentry *dentry, struct nameidata *nd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304{
305 struct ncp_server *server;
306 struct dentry *parent;
307 struct inode *dir;
308 struct ncp_entry_info finfo;
309 int res, val = 0, len;
310 __u8 __name[NCP_MAXPATHLEN + 1];
311
Nick Piggin34286d62011-01-07 17:49:57 +1100312 if (nd->flags & LOOKUP_RCU)
313 return -ECHILD;
314
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 parent = dget_parent(dentry);
316 dir = parent->d_inode;
317
318 if (!dentry->d_inode)
319 goto finished;
320
321 server = NCP_SERVER(dir);
322
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 /*
324 * Inspired by smbfs:
325 * The default validation is based on dentry age:
326 * We set the max age at mount time. (But each
327 * successful server lookup renews the timestamp.)
328 */
329 val = NCP_TEST_AGE(server, dentry);
330 if (val)
331 goto finished;
332
333 DDPRINTK("ncp_lookup_validate: %s/%s not valid, age=%ld, server lookup\n",
334 dentry->d_parent->d_name.name, dentry->d_name.name,
335 NCP_GET_AGE(dentry));
336
337 len = sizeof(__name);
338 if (ncp_is_server_root(dir)) {
339 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
340 dentry->d_name.len, 1);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200341 if (!res) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 res = ncp_lookup_volume(server, __name, &(finfo.i));
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200343 if (!res)
344 ncp_update_known_namespace(server, finfo.i.volNumber, NULL);
345 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 } else {
347 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
348 dentry->d_name.len, !ncp_preserve_case(dir));
349 if (!res)
350 res = ncp_obtain_info(server, dir, __name, &(finfo.i));
351 }
352 finfo.volume = finfo.i.volNumber;
353 DDPRINTK("ncp_lookup_validate: looked for %s/%s, res=%d\n",
354 dentry->d_parent->d_name.name, __name, res);
355 /*
356 * If we didn't find it, or if it has a different dirEntNum to
357 * what we remember, it's not valid any more.
358 */
359 if (!res) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200360 struct inode *inode = dentry->d_inode;
361
362 mutex_lock(&inode->i_mutex);
363 if (finfo.i.dirEntNum == NCP_FINFO(inode)->dirEntNum) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 ncp_new_dentry(dentry);
365 val=1;
366 } else
367 DDPRINTK("ncp_lookup_validate: found, but dirEntNum changed\n");
368
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200369 ncp_update_inode2(inode, &finfo);
370 mutex_unlock(&inode->i_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 }
372
373finished:
374 DDPRINTK("ncp_lookup_validate: result=%d\n", val);
375 dput(parent);
376 return val;
377}
378
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379static struct dentry *
380ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
381{
382 struct dentry *dent = dentry;
383 struct list_head *next;
384
385 if (d_validate(dent, parent)) {
386 if (dent->d_name.len <= NCP_MAXPATHLEN &&
387 (unsigned long)dent->d_fsdata == fpos) {
388 if (!dent->d_inode) {
389 dput(dent);
390 dent = NULL;
391 }
392 return dent;
393 }
394 dput(dent);
395 }
396
397 /* If a pointer is invalid, we search the dentry. */
Nick Piggin2fd6b7f2011-01-07 17:49:34 +1100398 spin_lock(&parent->d_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 next = parent->d_subdirs.next;
400 while (next != &parent->d_subdirs) {
Eric Dumazet5160ee62006-01-08 01:03:32 -0800401 dent = list_entry(next, struct dentry, d_u.d_child);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 if ((unsigned long)dent->d_fsdata == fpos) {
403 if (dent->d_inode)
Nick Piggindc0474b2011-01-07 17:49:43 +1100404 dget(dent);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 else
406 dent = NULL;
Nick Piggin2fd6b7f2011-01-07 17:49:34 +1100407 spin_unlock(&parent->d_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 goto out;
409 }
410 next = next->next;
411 }
Nick Piggin2fd6b7f2011-01-07 17:49:34 +1100412 spin_unlock(&parent->d_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 return NULL;
414
415out:
416 return dent;
417}
418
419static time_t ncp_obtain_mtime(struct dentry *dentry)
420{
421 struct inode *inode = dentry->d_inode;
422 struct ncp_server *server = NCP_SERVER(inode);
423 struct nw_info_struct i;
424
425 if (!ncp_conn_valid(server) || ncp_is_server_root(inode))
426 return 0;
427
428 if (ncp_obtain_info(server, inode, NULL, &i))
429 return 0;
430
431 return ncp_date_dos2unix(i.modifyTime, i.modifyDate);
432}
433
434static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
435{
Josef Sipek92e5bae2006-12-08 02:37:22 -0800436 struct dentry *dentry = filp->f_path.dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 struct inode *inode = dentry->d_inode;
438 struct page *page = NULL;
439 struct ncp_server *server = NCP_SERVER(inode);
440 union ncp_dir_cache *cache = NULL;
441 struct ncp_cache_control ctl;
442 int result, mtime_valid = 0;
443 time_t mtime = 0;
444
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 ctl.page = NULL;
446 ctl.cache = NULL;
447
448 DDPRINTK("ncp_readdir: reading %s/%s, pos=%d\n",
449 dentry->d_parent->d_name.name, dentry->d_name.name,
450 (int) filp->f_pos);
451
452 result = -EIO;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200453 /* Do not generate '.' and '..' when server is dead. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 if (!ncp_conn_valid(server))
455 goto out;
456
457 result = 0;
458 if (filp->f_pos == 0) {
459 if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
460 goto out;
461 filp->f_pos = 1;
462 }
463 if (filp->f_pos == 1) {
464 if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR))
465 goto out;
466 filp->f_pos = 2;
467 }
468
469 page = grab_cache_page(&inode->i_data, 0);
470 if (!page)
471 goto read_really;
472
473 ctl.cache = cache = kmap(page);
474 ctl.head = cache->head;
475
476 if (!PageUptodate(page) || !ctl.head.eof)
477 goto init_cache;
478
479 if (filp->f_pos == 2) {
480 if (jiffies - ctl.head.time >= NCP_MAX_AGE(server))
481 goto init_cache;
482
483 mtime = ncp_obtain_mtime(dentry);
484 mtime_valid = 1;
485 if ((!mtime) || (mtime != ctl.head.mtime))
486 goto init_cache;
487 }
488
489 if (filp->f_pos > ctl.head.end)
490 goto finished;
491
492 ctl.fpos = filp->f_pos + (NCP_DIRCACHE_START - 2);
493 ctl.ofs = ctl.fpos / NCP_DIRCACHE_SIZE;
494 ctl.idx = ctl.fpos % NCP_DIRCACHE_SIZE;
495
496 for (;;) {
497 if (ctl.ofs != 0) {
498 ctl.page = find_lock_page(&inode->i_data, ctl.ofs);
499 if (!ctl.page)
500 goto invalid_cache;
501 ctl.cache = kmap(ctl.page);
502 if (!PageUptodate(ctl.page))
503 goto invalid_cache;
504 }
505 while (ctl.idx < NCP_DIRCACHE_SIZE) {
506 struct dentry *dent;
507 int res;
508
509 dent = ncp_dget_fpos(ctl.cache->dentry[ctl.idx],
510 dentry, filp->f_pos);
511 if (!dent)
512 goto invalid_cache;
513 res = filldir(dirent, dent->d_name.name,
514 dent->d_name.len, filp->f_pos,
515 dent->d_inode->i_ino, DT_UNKNOWN);
516 dput(dent);
517 if (res)
518 goto finished;
519 filp->f_pos += 1;
520 ctl.idx += 1;
521 if (filp->f_pos > ctl.head.end)
522 goto finished;
523 }
524 if (ctl.page) {
525 kunmap(ctl.page);
526 SetPageUptodate(ctl.page);
527 unlock_page(ctl.page);
528 page_cache_release(ctl.page);
529 ctl.page = NULL;
530 }
531 ctl.idx = 0;
532 ctl.ofs += 1;
533 }
534invalid_cache:
535 if (ctl.page) {
536 kunmap(ctl.page);
537 unlock_page(ctl.page);
538 page_cache_release(ctl.page);
539 ctl.page = NULL;
540 }
541 ctl.cache = cache;
542init_cache:
543 ncp_invalidate_dircache_entries(dentry);
544 if (!mtime_valid) {
545 mtime = ncp_obtain_mtime(dentry);
546 mtime_valid = 1;
547 }
548 ctl.head.mtime = mtime;
549 ctl.head.time = jiffies;
550 ctl.head.eof = 0;
551 ctl.fpos = 2;
552 ctl.ofs = 0;
553 ctl.idx = NCP_DIRCACHE_START;
554 ctl.filled = 0;
555 ctl.valid = 1;
556read_really:
557 if (ncp_is_server_root(inode)) {
558 ncp_read_volume_list(filp, dirent, filldir, &ctl);
559 } else {
560 ncp_do_readdir(filp, dirent, filldir, &ctl);
561 }
562 ctl.head.end = ctl.fpos - 1;
563 ctl.head.eof = ctl.valid;
564finished:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200565 if (ctl.page) {
566 kunmap(ctl.page);
567 SetPageUptodate(ctl.page);
568 unlock_page(ctl.page);
569 page_cache_release(ctl.page);
570 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 if (page) {
572 cache->head = ctl.head;
573 kunmap(page);
574 SetPageUptodate(page);
575 unlock_page(page);
576 page_cache_release(page);
577 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 return result;
580}
581
582static int
583ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200584 struct ncp_cache_control *ctrl, struct ncp_entry_info *entry,
585 int inval_childs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586{
Josef Sipek92e5bae2006-12-08 02:37:22 -0800587 struct dentry *newdent, *dentry = filp->f_path.dentry;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200588 struct inode *dir = dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 struct ncp_cache_control ctl = *ctrl;
590 struct qstr qname;
591 int valid = 0;
592 int hashed = 0;
593 ino_t ino = 0;
594 __u8 __name[NCP_MAXPATHLEN + 1];
595
596 qname.len = sizeof(__name);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200597 if (ncp_vol2io(NCP_SERVER(dir), __name, &qname.len,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 entry->i.entryName, entry->i.nameLen,
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200599 !ncp_preserve_entry_case(dir, entry->i.NSCreator)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 return 1; /* I'm not sure */
601
602 qname.name = __name;
603 qname.hash = full_name_hash(qname.name, qname.len);
604
605 if (dentry->d_op && dentry->d_op->d_hash)
Nick Pigginb1e6a012011-01-07 17:49:28 +1100606 if (dentry->d_op->d_hash(dentry, dentry->d_inode, &qname) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 goto end_advance;
608
609 newdent = d_lookup(dentry, &qname);
610
611 if (!newdent) {
612 newdent = d_alloc(dentry, &qname);
613 if (!newdent)
614 goto end_advance;
615 } else {
616 hashed = 1;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200617
618 /* If case sensitivity changed for this volume, all entries below this one
619 should be thrown away. This entry itself is not affected, as its case
620 sensitivity is controlled by its own parent. */
621 if (inval_childs)
622 shrink_dcache_parent(newdent);
623
624 /*
Nick Pigginfb2d5b82011-01-07 17:49:26 +1100625 * NetWare's OS2 namespace is case preserving yet case
626 * insensitive. So we update dentry's name as received from
627 * server. Parent dir's i_mutex is locked because we're in
628 * readdir.
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200629 */
Nick Pigginfb2d5b82011-01-07 17:49:26 +1100630 dentry_update_name_case(newdent, &qname);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 }
632
633 if (!newdent->d_inode) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200634 struct inode *inode;
635
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 entry->opened = 0;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200637 entry->ino = iunique(dir->i_sb, 2);
638 inode = ncp_iget(dir->i_sb, entry);
639 if (inode) {
Nick Pigginfb045ad2011-01-07 17:49:55 +1100640 d_set_d_op(newdent, &ncp_dentry_operations);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200641 d_instantiate(newdent, inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 if (!hashed)
643 d_rehash(newdent);
644 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200645 } else {
646 struct inode *inode = newdent->d_inode;
647
Nick Pigginfb2d5b82011-01-07 17:49:26 +1100648 mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200649 ncp_update_inode2(inode, entry);
650 mutex_unlock(&inode->i_mutex);
651 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
653 if (newdent->d_inode) {
654 ino = newdent->d_inode->i_ino;
655 newdent->d_fsdata = (void *) ctl.fpos;
656 ncp_new_dentry(newdent);
657 }
658
659 if (ctl.idx >= NCP_DIRCACHE_SIZE) {
660 if (ctl.page) {
661 kunmap(ctl.page);
662 SetPageUptodate(ctl.page);
663 unlock_page(ctl.page);
664 page_cache_release(ctl.page);
665 }
666 ctl.cache = NULL;
667 ctl.idx -= NCP_DIRCACHE_SIZE;
668 ctl.ofs += 1;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200669 ctl.page = grab_cache_page(&dir->i_data, ctl.ofs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 if (ctl.page)
671 ctl.cache = kmap(ctl.page);
672 }
673 if (ctl.cache) {
674 ctl.cache->dentry[ctl.idx] = newdent;
675 valid = 1;
676 }
677 dput(newdent);
678end_advance:
679 if (!valid)
680 ctl.valid = 0;
681 if (!ctl.filled && (ctl.fpos == filp->f_pos)) {
682 if (!ino)
683 ino = find_inode_number(dentry, &qname);
684 if (!ino)
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200685 ino = iunique(dir->i_sb, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 ctl.filled = filldir(dirent, qname.name, qname.len,
687 filp->f_pos, ino, DT_UNKNOWN);
688 if (!ctl.filled)
689 filp->f_pos += 1;
690 }
691 ctl.fpos += 1;
692 ctl.idx += 1;
693 *ctrl = ctl;
694 return (ctl.valid || !ctl.filled);
695}
696
697static void
698ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir,
699 struct ncp_cache_control *ctl)
700{
Josef Sipek92e5bae2006-12-08 02:37:22 -0800701 struct dentry *dentry = filp->f_path.dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 struct inode *inode = dentry->d_inode;
703 struct ncp_server *server = NCP_SERVER(inode);
704 struct ncp_volume_info info;
705 struct ncp_entry_info entry;
706 int i;
707
708 DPRINTK("ncp_read_volume_list: pos=%ld\n",
709 (unsigned long) filp->f_pos);
710
711 for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200712 int inval_dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713
714 if (ncp_get_volume_info_with_number(server, i, &info) != 0)
715 return;
716 if (!strlen(info.volume_name))
717 continue;
718
719 DPRINTK("ncp_read_volume_list: found vol: %s\n",
720 info.volume_name);
721
722 if (ncp_lookup_volume(server, info.volume_name,
723 &entry.i)) {
724 DPRINTK("ncpfs: could not lookup vol %s\n",
725 info.volume_name);
726 continue;
727 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200728 inval_dentry = ncp_update_known_namespace(server, entry.i.volNumber, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 entry.volume = entry.i.volNumber;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200730 if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, inval_dentry))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 return;
732 }
733}
734
735static void
736ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir,
737 struct ncp_cache_control *ctl)
738{
Josef Sipek92e5bae2006-12-08 02:37:22 -0800739 struct dentry *dentry = filp->f_path.dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 struct inode *dir = dentry->d_inode;
741 struct ncp_server *server = NCP_SERVER(dir);
742 struct nw_search_sequence seq;
743 struct ncp_entry_info entry;
744 int err;
745 void* buf;
746 int more;
747 size_t bufsize;
748
749 DPRINTK("ncp_do_readdir: %s/%s, fpos=%ld\n",
750 dentry->d_parent->d_name.name, dentry->d_name.name,
751 (unsigned long) filp->f_pos);
752 PPRINTK("ncp_do_readdir: init %s, volnum=%d, dirent=%u\n",
753 dentry->d_name.name, NCP_FINFO(dir)->volNumber,
754 NCP_FINFO(dir)->dirEntNum);
755
756 err = ncp_initialize_search(server, dir, &seq);
757 if (err) {
758 DPRINTK("ncp_do_readdir: init failed, err=%d\n", err);
759 return;
760 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 /* We MUST NOT use server->buffer_size handshaked with server if we are
762 using UDP, as for UDP server uses max. buffer size determined by
763 MTU, and for TCP server uses hardwired value 65KB (== 66560 bytes).
764 So we use 128KB, just to be sure, as there is no way how to know
765 this value in advance. */
766 bufsize = 131072;
767 buf = vmalloc(bufsize);
768 if (!buf)
769 return;
770 do {
771 int cnt;
772 char* rpl;
773 size_t rpls;
774
775 err = ncp_search_for_fileset(server, &seq, &more, &cnt, buf, bufsize, &rpl, &rpls);
776 if (err) /* Error */
777 break;
778 if (!cnt) /* prevent endless loop */
779 break;
780 while (cnt--) {
781 size_t onerpl;
782
783 if (rpls < offsetof(struct nw_info_struct, entryName))
784 break; /* short packet */
785 ncp_extract_file_info(rpl, &entry.i);
786 onerpl = offsetof(struct nw_info_struct, entryName) + entry.i.nameLen;
787 if (rpls < onerpl)
788 break; /* short packet */
789 (void)ncp_obtain_nfs_info(server, &entry.i);
790 rpl += onerpl;
791 rpls -= onerpl;
792 entry.volume = entry.i.volNumber;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200793 if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 break;
795 }
796 } while (more);
797 vfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 return;
799}
800
801int ncp_conn_logged_in(struct super_block *sb)
802{
803 struct ncp_server* server = NCP_SBP(sb);
804 int result;
805
806 if (ncp_single_volume(server)) {
807 int len;
808 struct dentry* dent;
809 __u32 volNumber;
810 __le32 dirEntNum;
811 __le32 DosDirNum;
812 __u8 __name[NCP_MAXPATHLEN + 1];
813
814 len = sizeof(__name);
815 result = ncp_io2vol(server, __name, &len, server->m.mounted_vol,
816 strlen(server->m.mounted_vol), 1);
817 if (result)
818 goto out;
819 result = -ENOENT;
820 if (ncp_get_volume_root(server, __name, &volNumber, &dirEntNum, &DosDirNum)) {
821 PPRINTK("ncp_conn_logged_in: %s not found\n",
822 server->m.mounted_vol);
823 goto out;
824 }
825 dent = sb->s_root;
826 if (dent) {
827 struct inode* ino = dent->d_inode;
828 if (ino) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200829 ncp_update_known_namespace(server, volNumber, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 NCP_FINFO(ino)->volNumber = volNumber;
831 NCP_FINFO(ino)->dirEntNum = dirEntNum;
832 NCP_FINFO(ino)->DosDirNum = DosDirNum;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200833 result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 } else {
835 DPRINTK("ncpfs: sb->s_root->d_inode == NULL!\n");
836 }
837 } else {
838 DPRINTK("ncpfs: sb->s_root == NULL!\n");
839 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200840 } else
841 result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
843out:
844 return result;
845}
846
847static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
848{
849 struct ncp_server *server = NCP_SERVER(dir);
850 struct inode *inode = NULL;
851 struct ncp_entry_info finfo;
852 int error, res, len;
853 __u8 __name[NCP_MAXPATHLEN + 1];
854
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 error = -EIO;
856 if (!ncp_conn_valid(server))
857 goto finished;
858
859 PPRINTK("ncp_lookup: server lookup for %s/%s\n",
860 dentry->d_parent->d_name.name, dentry->d_name.name);
861
862 len = sizeof(__name);
863 if (ncp_is_server_root(dir)) {
864 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
865 dentry->d_name.len, 1);
866 if (!res)
867 res = ncp_lookup_volume(server, __name, &(finfo.i));
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200868 if (!res)
869 ncp_update_known_namespace(server, finfo.i.volNumber, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 } else {
871 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
872 dentry->d_name.len, !ncp_preserve_case(dir));
873 if (!res)
874 res = ncp_obtain_info(server, dir, __name, &(finfo.i));
875 }
876 PPRINTK("ncp_lookup: looked for %s/%s, res=%d\n",
877 dentry->d_parent->d_name.name, __name, res);
878 /*
879 * If we didn't find an entry, make a negative dentry.
880 */
881 if (res)
882 goto add_entry;
883
884 /*
885 * Create an inode for the entry.
886 */
887 finfo.opened = 0;
888 finfo.ino = iunique(dir->i_sb, 2);
889 finfo.volume = finfo.i.volNumber;
890 error = -EACCES;
891 inode = ncp_iget(dir->i_sb, &finfo);
892
893 if (inode) {
894 ncp_new_dentry(dentry);
895add_entry:
Nick Pigginfb045ad2011-01-07 17:49:55 +1100896 d_set_d_op(dentry, &ncp_dentry_operations);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 d_add(dentry, inode);
898 error = 0;
899 }
900
901finished:
902 PPRINTK("ncp_lookup: result=%d\n", error);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 return ERR_PTR(error);
904}
905
906/*
907 * This code is common to create, mkdir, and mknod.
908 */
909static int ncp_instantiate(struct inode *dir, struct dentry *dentry,
910 struct ncp_entry_info *finfo)
911{
912 struct inode *inode;
913 int error = -EINVAL;
914
915 finfo->ino = iunique(dir->i_sb, 2);
916 inode = ncp_iget(dir->i_sb, finfo);
917 if (!inode)
918 goto out_close;
919 d_instantiate(dentry,inode);
920 error = 0;
921out:
922 return error;
923
924out_close:
925 PPRINTK("ncp_instantiate: %s/%s failed, closing file\n",
926 dentry->d_parent->d_name.name, dentry->d_name.name);
927 ncp_close_file(NCP_SERVER(dir), finfo->file_handle);
928 goto out;
929}
930
931int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode,
932 dev_t rdev, __le32 attributes)
933{
934 struct ncp_server *server = NCP_SERVER(dir);
935 struct ncp_entry_info finfo;
936 int error, result, len;
937 int opmode;
938 __u8 __name[NCP_MAXPATHLEN + 1];
939
940 PPRINTK("ncp_create_new: creating %s/%s, mode=%x\n",
941 dentry->d_parent->d_name.name, dentry->d_name.name, mode);
942
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 ncp_age_dentry(server, dentry);
944 len = sizeof(__name);
945 error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
946 dentry->d_name.len, !ncp_preserve_case(dir));
947 if (error)
948 goto out;
949
950 error = -EACCES;
951
952 if (S_ISREG(mode) &&
953 (server->m.flags & NCP_MOUNT_EXTRAS) &&
954 (mode & S_IXUGO))
955 attributes |= aSYSTEM | aSHARED;
956
957 result = ncp_open_create_file_or_subdir(server, dir, __name,
958 OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
959 attributes, AR_READ | AR_WRITE, &finfo);
960 opmode = O_RDWR;
961 if (result) {
962 result = ncp_open_create_file_or_subdir(server, dir, __name,
963 OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
964 attributes, AR_WRITE, &finfo);
965 if (result) {
966 if (result == 0x87)
967 error = -ENAMETOOLONG;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200968 else if (result < 0)
969 error = result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 DPRINTK("ncp_create: %s/%s failed\n",
971 dentry->d_parent->d_name.name, dentry->d_name.name);
972 goto out;
973 }
974 opmode = O_WRONLY;
975 }
976 finfo.access = opmode;
977 if (ncp_is_nfs_extras(server, finfo.volume)) {
978 finfo.i.nfs.mode = mode;
979 finfo.i.nfs.rdev = new_encode_dev(rdev);
980 if (ncp_modify_nfs_info(server, finfo.volume,
981 finfo.i.dirEntNum,
982 mode, new_encode_dev(rdev)) != 0)
983 goto out;
984 }
985
986 error = ncp_instantiate(dir, dentry, &finfo);
987out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 return error;
989}
990
991static int ncp_create(struct inode *dir, struct dentry *dentry, int mode,
992 struct nameidata *nd)
993{
994 return ncp_create_new(dir, dentry, mode, 0, 0);
995}
996
997static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode)
998{
999 struct ncp_entry_info finfo;
1000 struct ncp_server *server = NCP_SERVER(dir);
1001 int error, len;
1002 __u8 __name[NCP_MAXPATHLEN + 1];
1003
1004 DPRINTK("ncp_mkdir: making %s/%s\n",
1005 dentry->d_parent->d_name.name, dentry->d_name.name);
1006
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 ncp_age_dentry(server, dentry);
1008 len = sizeof(__name);
1009 error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
1010 dentry->d_name.len, !ncp_preserve_case(dir));
1011 if (error)
1012 goto out;
1013
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001014 error = ncp_open_create_file_or_subdir(server, dir, __name,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 OC_MODE_CREATE, aDIR,
1016 cpu_to_le16(0xffff),
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001017 &finfo);
1018 if (error == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 if (ncp_is_nfs_extras(server, finfo.volume)) {
1020 mode |= S_IFDIR;
1021 finfo.i.nfs.mode = mode;
1022 if (ncp_modify_nfs_info(server,
1023 finfo.volume,
1024 finfo.i.dirEntNum,
1025 mode, 0) != 0)
1026 goto out;
1027 }
1028 error = ncp_instantiate(dir, dentry, &finfo);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001029 } else if (error > 0) {
1030 error = -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 }
1032out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 return error;
1034}
1035
1036static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
1037{
1038 struct ncp_server *server = NCP_SERVER(dir);
1039 int error, result, len;
1040 __u8 __name[NCP_MAXPATHLEN + 1];
1041
1042 DPRINTK("ncp_rmdir: removing %s/%s\n",
1043 dentry->d_parent->d_name.name, dentry->d_name.name);
1044
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 error = -EBUSY;
1046 if (!d_unhashed(dentry))
1047 goto out;
1048
1049 len = sizeof(__name);
1050 error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
1051 dentry->d_name.len, !ncp_preserve_case(dir));
1052 if (error)
1053 goto out;
1054
1055 result = ncp_del_file_or_subdir(server, dir, __name);
1056 switch (result) {
1057 case 0x00:
1058 error = 0;
1059 break;
1060 case 0x85: /* unauthorized to delete file */
1061 case 0x8A: /* unauthorized to delete file */
1062 error = -EACCES;
1063 break;
1064 case 0x8F:
1065 case 0x90: /* read only */
1066 error = -EPERM;
1067 break;
1068 case 0x9F: /* in use by another client */
1069 error = -EBUSY;
1070 break;
1071 case 0xA0: /* directory not empty */
1072 error = -ENOTEMPTY;
1073 break;
1074 case 0xFF: /* someone deleted file */
1075 error = -ENOENT;
1076 break;
1077 default:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001078 error = result < 0 ? result : -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 break;
1080 }
1081out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 return error;
1083}
1084
1085static int ncp_unlink(struct inode *dir, struct dentry *dentry)
1086{
1087 struct inode *inode = dentry->d_inode;
1088 struct ncp_server *server;
1089 int error;
1090
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 server = NCP_SERVER(dir);
1092 DPRINTK("ncp_unlink: unlinking %s/%s\n",
1093 dentry->d_parent->d_name.name, dentry->d_name.name);
1094
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 /*
1096 * Check whether to close the file ...
1097 */
1098 if (inode) {
1099 PPRINTK("ncp_unlink: closing file\n");
1100 ncp_make_closed(inode);
1101 }
1102
1103 error = ncp_del_file_or_subdir2(server, dentry);
1104#ifdef CONFIG_NCPFS_STRONG
1105 /* 9C is Invalid path.. It should be 8F, 90 - read only, but
1106 it is not :-( */
1107 if ((error == 0x9C || error == 0x90) && server->m.flags & NCP_MOUNT_STRONG) { /* R/O */
1108 error = ncp_force_unlink(dir, dentry);
1109 }
1110#endif
1111 switch (error) {
1112 case 0x00:
1113 DPRINTK("ncp: removed %s/%s\n",
1114 dentry->d_parent->d_name.name, dentry->d_name.name);
1115 break;
1116 case 0x85:
1117 case 0x8A:
1118 error = -EACCES;
1119 break;
1120 case 0x8D: /* some files in use */
1121 case 0x8E: /* all files in use */
1122 error = -EBUSY;
1123 break;
1124 case 0x8F: /* some read only */
1125 case 0x90: /* all read only */
1126 case 0x9C: /* !!! returned when in-use or read-only by NW4 */
1127 error = -EPERM;
1128 break;
1129 case 0xFF:
1130 error = -ENOENT;
1131 break;
1132 default:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001133 error = error < 0 ? error : -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 break;
1135 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 return error;
1137}
1138
1139static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
1140 struct inode *new_dir, struct dentry *new_dentry)
1141{
1142 struct ncp_server *server = NCP_SERVER(old_dir);
1143 int error;
1144 int old_len, new_len;
1145 __u8 __old_name[NCP_MAXPATHLEN + 1], __new_name[NCP_MAXPATHLEN + 1];
1146
1147 DPRINTK("ncp_rename: %s/%s to %s/%s\n",
1148 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
1149 new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
1150
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 ncp_age_dentry(server, old_dentry);
1152 ncp_age_dentry(server, new_dentry);
1153
1154 old_len = sizeof(__old_name);
1155 error = ncp_io2vol(server, __old_name, &old_len,
1156 old_dentry->d_name.name, old_dentry->d_name.len,
1157 !ncp_preserve_case(old_dir));
1158 if (error)
1159 goto out;
1160
1161 new_len = sizeof(__new_name);
1162 error = ncp_io2vol(server, __new_name, &new_len,
1163 new_dentry->d_name.name, new_dentry->d_name.len,
1164 !ncp_preserve_case(new_dir));
1165 if (error)
1166 goto out;
1167
1168 error = ncp_ren_or_mov_file_or_subdir(server, old_dir, __old_name,
1169 new_dir, __new_name);
1170#ifdef CONFIG_NCPFS_STRONG
1171 if ((error == 0x90 || error == 0x8B || error == -EACCES) &&
1172 server->m.flags & NCP_MOUNT_STRONG) { /* RO */
1173 error = ncp_force_rename(old_dir, old_dentry, __old_name,
1174 new_dir, new_dentry, __new_name);
1175 }
1176#endif
1177 switch (error) {
1178 case 0x00:
1179 DPRINTK("ncp renamed %s -> %s.\n",
1180 old_dentry->d_name.name,new_dentry->d_name.name);
1181 break;
1182 case 0x9E:
1183 error = -ENAMETOOLONG;
1184 break;
1185 case 0xFF:
1186 error = -ENOENT;
1187 break;
1188 default:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001189 error = error < 0 ? error : -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 break;
1191 }
1192out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 return error;
1194}
1195
1196static int ncp_mknod(struct inode * dir, struct dentry *dentry,
1197 int mode, dev_t rdev)
1198{
1199 if (!new_valid_dev(rdev))
1200 return -EINVAL;
1201 if (ncp_is_nfs_extras(NCP_SERVER(dir), NCP_FINFO(dir)->volNumber)) {
1202 DPRINTK(KERN_DEBUG "ncp_mknod: mode = 0%o\n", mode);
1203 return ncp_create_new(dir, dentry, mode, rdev, 0);
1204 }
1205 return -EPERM; /* Strange, but true */
1206}
1207
1208/* The following routines are taken directly from msdos-fs */
1209
1210/* Linear day numbers of the respective 1sts in non-leap years. */
1211
1212static int day_n[] =
1213{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0};
1214/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
1215
1216
1217extern struct timezone sys_tz;
1218
1219static int utc2local(int time)
1220{
1221 return time - sys_tz.tz_minuteswest * 60;
1222}
1223
1224static int local2utc(int time)
1225{
1226 return time + sys_tz.tz_minuteswest * 60;
1227}
1228
1229/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
1230int
1231ncp_date_dos2unix(__le16 t, __le16 d)
1232{
1233 unsigned short time = le16_to_cpu(t), date = le16_to_cpu(d);
1234 int month, year, secs;
1235
1236 /* first subtract and mask after that... Otherwise, if
1237 date == 0, bad things happen */
1238 month = ((date >> 5) - 1) & 15;
1239 year = date >> 9;
1240 secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 +
1241 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) +
1242 year * 365 - ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653);
1243 /* days since 1.1.70 plus 80's leap day */
1244 return local2utc(secs);
1245}
1246
1247
1248/* Convert linear UNIX date to a MS-DOS time/date pair. */
1249void
1250ncp_date_unix2dos(int unix_date, __le16 *time, __le16 *date)
1251{
1252 int day, year, nl_day, month;
1253
1254 unix_date = utc2local(unix_date);
1255 *time = cpu_to_le16(
1256 (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) +
1257 (((unix_date / 3600) % 24) << 11));
1258 day = unix_date / 86400 - 3652;
1259 year = day / 365;
1260 if ((year + 3) / 4 + 365 * year > day)
1261 year--;
1262 day -= (year + 3) / 4 + 365 * year;
1263 if (day == 59 && !(year & 3)) {
1264 nl_day = day;
1265 month = 2;
1266 } else {
1267 nl_day = (year & 3) || day <= 59 ? day : day - 1;
Roel Kluinc5df5912009-09-22 16:45:54 -07001268 for (month = 1; month < 12; month++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 if (day_n[month] > nl_day)
1270 break;
1271 }
1272 *date = cpu_to_le16(nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9));
1273}