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