[PATCH] (1/2) inode trimming
[opensuse:kernel.git] / fs / ncpfs / inode.c
1 /*
2  *  inode.c
3  *
4  *  Copyright (C) 1995, 1996 by Volker Lendecke
5  *  Modified for big endian by J.F. Chadima and David S. Miller
6  *  Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
7  *  Modified 1998 Wolfram Pienkoss for NLS
8  *
9  */
10
11 #include <linux/config.h>
12 #include <linux/module.h>
13
14 #include <asm/system.h>
15 #include <asm/uaccess.h>
16 #include <asm/byteorder.h>
17
18 #include <linux/sched.h>
19 #include <linux/kernel.h>
20 #include <linux/mm.h>
21 #include <linux/string.h>
22 #include <linux/stat.h>
23 #include <linux/errno.h>
24 #include <linux/locks.h>
25 #include <linux/file.h>
26 #include <linux/fcntl.h>
27 #include <linux/slab.h>
28 #include <linux/vmalloc.h>
29 #include <linux/init.h>
30
31 #include <linux/ncp_fs.h>
32
33 #include "ncplib_kernel.h"
34
35 static void ncp_delete_inode(struct inode *);
36 static void ncp_put_super(struct super_block *);
37 static int  ncp_statfs(struct super_block *, struct statfs *);
38
39 static kmem_cache_t * ncp_inode_cachep;
40
41 static struct inode *ncp_alloc_inode(struct super_block *sb)
42 {
43         struct ncp_inode_info *ei;
44         ei = (struct ncp_inode_info *)kmem_cache_alloc(ncp_inode_cachep, SLAB_KERNEL);
45         if (!ei)
46                 return NULL;
47         return &ei->vfs_inode;
48 }
49
50 static void ncp_destroy_inode(struct inode *inode)
51 {
52         kmem_cache_free(ncp_inode_cachep, NCP_FINFO(inode));
53 }
54
55 static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
56 {
57         struct ncp_inode_info *ei = (struct ncp_inode_info *) foo;
58
59         if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
60             SLAB_CTOR_CONSTRUCTOR) {
61                 init_MUTEX(&ei->open_sem);
62                 inode_init_once(&ei->vfs_inode);
63         }
64 }
65  
66 static int init_inodecache(void)
67 {
68         ncp_inode_cachep = kmem_cache_create("ncp_inode_cache",
69                                              sizeof(struct ncp_inode_info),
70                                              0, SLAB_HWCACHE_ALIGN,
71                                              init_once, NULL);
72         if (ncp_inode_cachep == NULL)
73                 return -ENOMEM;
74         return 0;
75 }
76
77 static void destroy_inodecache(void)
78 {
79         if (kmem_cache_destroy(ncp_inode_cachep))
80                 printk(KERN_INFO "ncp_inode_cache: not all structures were freed\n");
81 }
82
83 static struct super_operations ncp_sops =
84 {
85         alloc_inode:    ncp_alloc_inode,
86         destroy_inode:  ncp_destroy_inode,
87         put_inode:      force_delete,
88         delete_inode:   ncp_delete_inode,
89         put_super:      ncp_put_super,
90         statfs:         ncp_statfs,
91 };
92
93 extern struct dentry_operations ncp_dentry_operations;
94 #ifdef CONFIG_NCPFS_EXTRAS
95 extern struct address_space_operations ncp_symlink_aops;
96 extern int ncp_symlink(struct inode*, struct dentry*, const char*);
97 #endif
98
99 /*
100  * Fill in the ncpfs-specific information in the inode.
101  */
102 void ncp_update_inode(struct inode *inode, struct ncp_entry_info *nwinfo)
103 {
104         NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum;
105         NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum;
106         NCP_FINFO(inode)->volNumber = nwinfo->i.volNumber;
107
108 #ifdef CONFIG_NCPFS_STRONG
109         NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
110 #else
111         NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
112 #endif
113         NCP_FINFO(inode)->access = nwinfo->access;
114         NCP_FINFO(inode)->server_file_handle = nwinfo->server_file_handle;
115         memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle,
116                         sizeof(nwinfo->file_handle));
117         DPRINTK("ncp_update_inode: updated %s, volnum=%d, dirent=%u\n",
118                 nwinfo->i.entryName, NCP_FINFO(inode)->volNumber,
119                 NCP_FINFO(inode)->dirEntNum);
120 }
121
122 void ncp_update_inode2(struct inode* inode, struct ncp_entry_info *nwinfo)
123 {
124         struct nw_info_struct *nwi = &nwinfo->i;
125         struct ncp_server *server = NCP_SERVER(inode);
126
127         if (!atomic_read(&NCP_FINFO(inode)->opened)) {
128 #ifdef CONFIG_NCPFS_STRONG
129                 NCP_FINFO(inode)->nwattr = nwi->attributes;
130 #endif
131                 if (nwi->attributes & aDIR) {
132                         inode->i_mode = server->m.dir_mode;
133                         inode->i_size = NCP_BLOCK_SIZE;
134                 } else {
135                         inode->i_mode = server->m.file_mode;
136                         inode->i_size = le32_to_cpu(nwi->dataStreamSize);
137 #ifdef CONFIG_NCPFS_EXTRAS
138                         if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS)) && (nwi->attributes & aSHARED)) {
139                                 switch (nwi->attributes & (aHIDDEN|aSYSTEM)) {
140                                         case aHIDDEN:
141                                                 if (server->m.flags & NCP_MOUNT_SYMLINKS) {
142                                                         if ( /* (inode->i_size >= NCP_MIN_SYMLINK_SIZE)
143                                                          && */ (inode->i_size <= NCP_MAX_SYMLINK_SIZE)) {
144                                                                 inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK;
145                                                                 break;
146                                                         }
147                                                 }
148                                                 /* FALLTHROUGH */
149                                         case 0:
150                                                 if (server->m.flags & NCP_MOUNT_EXTRAS)
151                                                         inode->i_mode |= 0444;
152                                                 break;
153                                         case aSYSTEM:
154                                                 if (server->m.flags & NCP_MOUNT_EXTRAS)
155                                                         inode->i_mode |= (inode->i_mode >> 2) & 0111;
156                                                 break;
157                                         /* case aSYSTEM|aHIDDEN: */
158                                         default:
159                                                 /* reserved combination */
160                                                 break;
161                                 }
162                         }
163 #endif
164                 }
165                 if (nwi->attributes & aRONLY) inode->i_mode &= ~0222;
166         }
167         inode->i_blocks = (inode->i_size + NCP_BLOCK_SIZE - 1) >> NCP_BLOCK_SHIFT;
168
169         inode->i_mtime = ncp_date_dos2unix(le16_to_cpu(nwi->modifyTime),
170                                            le16_to_cpu(nwi->modifyDate));
171         inode->i_ctime = ncp_date_dos2unix(le16_to_cpu(nwi->creationTime),
172                                            le16_to_cpu(nwi->creationDate));
173         inode->i_atime = ncp_date_dos2unix(0, le16_to_cpu(nwi->lastAccessDate));
174
175         NCP_FINFO(inode)->DosDirNum = nwi->DosDirNum;
176         NCP_FINFO(inode)->dirEntNum = nwi->dirEntNum;
177         NCP_FINFO(inode)->volNumber = nwi->volNumber;
178 }
179
180 /*
181  * Fill in the inode based on the ncp_entry_info structure.
182  */
183 static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo)
184 {
185         struct nw_info_struct *nwi = &nwinfo->i;
186         struct ncp_server *server = NCP_SERVER(inode);
187
188         if (nwi->attributes & aDIR) {
189                 inode->i_mode = server->m.dir_mode;
190                 /* for directories dataStreamSize seems to be some
191                    Object ID ??? */
192                 inode->i_size = NCP_BLOCK_SIZE;
193         } else {
194                 inode->i_mode = server->m.file_mode;
195                 inode->i_size = le32_to_cpu(nwi->dataStreamSize);
196 #ifdef CONFIG_NCPFS_EXTRAS
197                 if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS)) 
198                  && (nwi->attributes & aSHARED)) {
199                         switch (nwi->attributes & (aHIDDEN|aSYSTEM)) {
200                                 case aHIDDEN:
201                                         if (server->m.flags & NCP_MOUNT_SYMLINKS) {
202                                                 if (/* (inode->i_size >= NCP_MIN_SYMLINK_SIZE)
203                                                  && */ (inode->i_size <= NCP_MAX_SYMLINK_SIZE)) {
204                                                         inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK;
205                                                         break;
206                                                 }
207                                         }
208                                         /* FALLTHROUGH */
209                                 case 0:
210                                         if (server->m.flags & NCP_MOUNT_EXTRAS)
211                                                 inode->i_mode |= 0444;
212                                         break;
213                                 case aSYSTEM:
214                                         if (server->m.flags & NCP_MOUNT_EXTRAS)
215                                                 inode->i_mode |= (inode->i_mode >> 2) & 0111;
216                                         break;
217                                 /* case aSYSTEM|aHIDDEN: */
218                                 default:
219                                         /* reserved combination */
220                                         break;
221                         }
222                 }
223 #endif
224         }
225         if (nwi->attributes & aRONLY) inode->i_mode &= ~0222;
226
227         DDPRINTK("ncp_read_inode: inode->i_mode = %u\n", inode->i_mode);
228
229         inode->i_nlink = 1;
230         inode->i_uid = server->m.uid;
231         inode->i_gid = server->m.gid;
232         inode->i_rdev = NODEV;
233         inode->i_blksize = NCP_BLOCK_SIZE;
234
235         inode->i_blocks = (inode->i_size + NCP_BLOCK_SIZE - 1) >> NCP_BLOCK_SHIFT;
236
237         inode->i_mtime = ncp_date_dos2unix(le16_to_cpu(nwi->modifyTime),
238                                            le16_to_cpu(nwi->modifyDate));
239         inode->i_ctime = ncp_date_dos2unix(le16_to_cpu(nwi->creationTime),
240                                            le16_to_cpu(nwi->creationDate));
241         inode->i_atime = ncp_date_dos2unix(0,
242                                            le16_to_cpu(nwi->lastAccessDate));
243         ncp_update_inode(inode, nwinfo);
244 }
245
246 static struct inode_operations ncp_symlink_inode_operations = {
247         readlink:       page_readlink,
248         follow_link:    page_follow_link,
249         setattr:        ncp_notify_change,
250 };
251
252 /*
253  * Get a new inode.
254  */
255 struct inode * 
256 ncp_iget(struct super_block *sb, struct ncp_entry_info *info)
257 {
258         struct inode *inode;
259
260         if (info == NULL) {
261                 printk(KERN_ERR "ncp_iget: info is NULL\n");
262                 return NULL;
263         }
264
265         inode = new_inode(sb);
266         if (inode) {
267                 atomic_set(&NCP_FINFO(inode)->opened, info->opened);
268
269                 inode->i_ino = info->ino;
270                 ncp_set_attr(inode, info);
271                 if (S_ISREG(inode->i_mode)) {
272                         inode->i_op = &ncp_file_inode_operations;
273                         inode->i_fop = &ncp_file_operations;
274                 } else if (S_ISDIR(inode->i_mode)) {
275                         inode->i_op = &ncp_dir_inode_operations;
276                         inode->i_fop = &ncp_dir_operations;
277 #ifdef CONFIG_NCPFS_EXTRAS
278                 } else if (S_ISLNK(inode->i_mode)) {
279                         inode->i_op = &ncp_symlink_inode_operations;
280                         inode->i_data.a_ops = &ncp_symlink_aops;
281 #endif
282                 }
283                 insert_inode_hash(inode);
284         } else
285                 printk(KERN_ERR "ncp_iget: iget failed!\n");
286         return inode;
287 }
288
289 static void
290 ncp_delete_inode(struct inode *inode)
291 {
292         if (S_ISDIR(inode->i_mode)) {
293                 DDPRINTK("ncp_delete_inode: put directory %ld\n", inode->i_ino);
294         }
295
296         if (ncp_make_closed(inode) != 0) {
297                 /* We can't do anything but complain. */
298                 printk(KERN_ERR "ncp_delete_inode: could not close\n");
299         }
300         clear_inode(inode);
301 }
302
303 struct super_block *
304 ncp_read_super(struct super_block *sb, void *raw_data, int silent)
305 {
306         struct ncp_mount_data_kernel data;
307         struct ncp_server *server;
308         struct file *ncp_filp;
309         struct inode *root_inode;
310         struct inode *sock_inode;
311         struct socket *sock;
312         int error;
313         int default_bufsize;
314 #ifdef CONFIG_NCPFS_PACKET_SIGNING
315         int options;
316 #endif
317         struct ncp_entry_info finfo;
318
319         if (raw_data == NULL)
320                 goto out_no_data;
321         switch (*(int*)raw_data) {
322                 case NCP_MOUNT_VERSION:
323                         {
324                                 struct ncp_mount_data* md = (struct ncp_mount_data*)raw_data;
325
326                                 data.flags = md->flags;
327                                 data.int_flags = NCP_IMOUNT_LOGGEDIN_POSSIBLE;
328                                 data.mounted_uid = md->mounted_uid;
329                                 data.wdog_pid = md->wdog_pid;
330                                 data.ncp_fd = md->ncp_fd;
331                                 data.time_out = md->time_out;
332                                 data.retry_count = md->retry_count;
333                                 data.uid = md->uid;
334                                 data.gid = md->gid;
335                                 data.file_mode = md->file_mode;
336                                 data.dir_mode = md->dir_mode;
337                                 memcpy(data.mounted_vol, md->mounted_vol,
338                                         NCP_VOLNAME_LEN+1);
339                         }
340                         break;
341                 case NCP_MOUNT_VERSION_V4:
342                         {
343                                 struct ncp_mount_data_v4* md = (struct ncp_mount_data_v4*)raw_data;
344
345                                 data.flags = md->flags;
346                                 data.int_flags = 0;
347                                 data.mounted_uid = md->mounted_uid;
348                                 data.wdog_pid = md->wdog_pid;
349                                 data.ncp_fd = md->ncp_fd;
350                                 data.time_out = md->time_out;
351                                 data.retry_count = md->retry_count;
352                                 data.uid = md->uid;
353                                 data.gid = md->gid;
354                                 data.file_mode = md->file_mode;
355                                 data.dir_mode = md->dir_mode;
356                                 data.mounted_vol[0] = 0;
357                         }
358                         break;
359                 default:
360                         goto out_bad_mount;
361         }
362         ncp_filp = fget(data.ncp_fd);
363         if (!ncp_filp)
364                 goto out_bad_file;
365         sock_inode = ncp_filp->f_dentry->d_inode;
366         if (!S_ISSOCK(sock_inode->i_mode))
367                 goto out_bad_file2;
368         sock = SOCKET_I(sock_inode);
369         if (!sock)
370                 goto out_bad_file2;
371                 
372         if (sock->type == SOCK_STREAM)
373                 default_bufsize = 61440;
374         else
375                 default_bufsize = 1024;
376
377         sb->s_blocksize = 1024; /* Eh...  Is this correct? */
378         sb->s_blocksize_bits = 10;
379         sb->s_magic = NCP_SUPER_MAGIC;
380         sb->s_op = &ncp_sops;
381
382         server = NCP_SBP(sb);
383         memset(server, 0, sizeof(*server));
384
385         server->ncp_filp = ncp_filp;
386 /*      server->lock = 0;       */
387         init_MUTEX(&server->sem);
388         server->packet = NULL;
389 /*      server->buffer_size = 0;        */
390 /*      server->conn_status = 0;        */
391 /*      server->root_dentry = NULL;     */
392 /*      server->root_setuped = 0;       */
393 #ifdef CONFIG_NCPFS_PACKET_SIGNING
394 /*      server->sign_wanted = 0;        */
395 /*      server->sign_active = 0;        */
396 #endif
397         server->auth.auth_type = NCP_AUTH_NONE;
398 /*      server->auth.object_name_len = 0;       */
399 /*      server->auth.object_name = NULL;        */
400 /*      server->auth.object_type = 0;           */
401 /*      server->priv.len = 0;                   */
402 /*      server->priv.data = NULL;               */
403
404         server->m = data;
405         /* Althought anything producing this is buggy, it happens
406            now because of PATH_MAX changes.. */
407         if (server->m.time_out < 1) {
408                 server->m.time_out = 10;
409                 printk(KERN_INFO "You need to recompile your ncpfs utils..\n");
410         }
411         server->m.time_out = server->m.time_out * HZ / 100;
412         server->m.file_mode = (server->m.file_mode &
413                                (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG;
414         server->m.dir_mode = (server->m.dir_mode &
415                               (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR;
416
417 #ifdef CONFIG_NCPFS_NLS
418         /* load the default NLS charsets */
419         server->nls_vol = load_nls_default();
420         server->nls_io = load_nls_default();
421 #endif /* CONFIG_NCPFS_NLS */
422
423         server->dentry_ttl = 0; /* no caching */
424
425 #undef NCP_PACKET_SIZE
426 #define NCP_PACKET_SIZE 65536
427         server->packet_size = NCP_PACKET_SIZE;
428         server->packet = vmalloc(NCP_PACKET_SIZE);
429         if (server->packet == NULL)
430                 goto out_no_packet;
431
432         ncp_lock_server(server);
433         error = ncp_connect(server);
434         ncp_unlock_server(server);
435         if (error < 0)
436                 goto out_no_connect;
437         DPRINTK("ncp_read_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb));
438
439 #ifdef CONFIG_NCPFS_PACKET_SIGNING
440         if (ncp_negotiate_size_and_options(server, default_bufsize,
441                 NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0)
442         {
443                 if (options != NCP_DEFAULT_OPTIONS)
444                 {
445                         if (ncp_negotiate_size_and_options(server, 
446                                 default_bufsize,
447                                 options & 2, 
448                                 &(server->buffer_size), &options) != 0)
449                                 
450                         {
451                                 goto out_no_bufsize;
452                         }
453                 }
454                 if (options & 2)
455                         server->sign_wanted = 1;
456         }
457         else 
458 #endif  /* CONFIG_NCPFS_PACKET_SIGNING */
459         if (ncp_negotiate_buffersize(server, default_bufsize,
460                                      &(server->buffer_size)) != 0)
461                 goto out_no_bufsize;
462         DPRINTK("ncpfs: bufsize = %d\n", server->buffer_size);
463
464         memset(&finfo, 0, sizeof(finfo));
465         finfo.i.attributes      = aDIR;
466         finfo.i.dataStreamSize  = NCP_BLOCK_SIZE;
467         finfo.i.dirEntNum       = 0;
468         finfo.i.DosDirNum       = 0;
469 #ifdef CONFIG_NCPFS_SMALLDOS
470         finfo.i.NSCreator       = NW_NS_DOS;
471 #endif
472         finfo.i.volNumber       = NCP_NUMBER_OF_VOLUMES + 1;    /* illegal volnum */
473         /* set dates of mountpoint to Jan 1, 1986; 00:00 */
474         finfo.i.creationTime    = finfo.i.modifyTime
475                                 = cpu_to_le16(0x0000);
476         finfo.i.creationDate    = finfo.i.modifyDate
477                                 = finfo.i.lastAccessDate
478                                 = cpu_to_le16(0x0C21);
479         finfo.i.nameLen         = 0;
480         finfo.i.entryName[0]    = '\0';
481
482         finfo.opened            = 0;
483         finfo.ino               = 2;    /* tradition */
484
485         server->name_space[finfo.i.volNumber] = NW_NS_DOS;
486         root_inode = ncp_iget(sb, &finfo);
487         if (!root_inode)
488                 goto out_no_root;
489         DPRINTK("ncp_read_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
490         sb->s_root = d_alloc_root(root_inode);
491         if (!sb->s_root)
492                 goto out_no_root;
493         sb->s_root->d_op = &ncp_dentry_operations;
494         return sb;
495
496 out_no_root:
497         printk(KERN_ERR "ncp_read_super: get root inode failed\n");
498         iput(root_inode);
499         goto out_disconnect;
500 out_no_bufsize:
501         printk(KERN_ERR "ncp_read_super: could not get bufsize\n");
502 out_disconnect:
503         ncp_lock_server(server);
504         ncp_disconnect(server);
505         ncp_unlock_server(server);
506         goto out_free_packet;
507 out_no_connect:
508         printk(KERN_ERR "ncp_read_super: Failed connection, error=%d\n", error);
509 out_free_packet:
510         vfree(server->packet);
511         goto out_free_server;
512 out_no_packet:
513         printk(KERN_ERR "ncp_read_super: could not alloc packet\n");
514 out_free_server:
515 #ifdef CONFIG_NCPFS_NLS
516         unload_nls(server->nls_io);
517         unload_nls(server->nls_vol);
518 #endif
519         /* 23/12/1998 Marcin Dalecki <dalecki@cs.net.pl>:
520          * 
521          * The previously used put_filp(ncp_filp); was bogous, since
522          * it doesn't proper unlocking.
523          */
524         fput(ncp_filp);
525         goto out;
526
527 out_bad_file2:
528         fput(ncp_filp);
529 out_bad_file:
530         printk(KERN_ERR "ncp_read_super: invalid ncp socket\n");
531         goto out;
532 out_bad_mount:
533         printk(KERN_INFO "ncp_read_super: kernel requires mount version %d\n",
534                 NCP_MOUNT_VERSION);
535         goto out;
536 out_no_data:
537         printk(KERN_ERR "ncp_read_super: missing data argument\n");
538 out:
539         return NULL;
540 }
541
542 static void ncp_put_super(struct super_block *sb)
543 {
544         struct ncp_server *server = NCP_SBP(sb);
545
546         ncp_lock_server(server);
547         ncp_disconnect(server);
548         ncp_unlock_server(server);
549
550 #ifdef CONFIG_NCPFS_NLS
551         /* unload the NLS charsets */
552         if (server->nls_vol)
553         {
554                 unload_nls(server->nls_vol);
555                 server->nls_vol = NULL;
556         }
557         if (server->nls_io)
558         {
559                 unload_nls(server->nls_io);
560                 server->nls_io = NULL;
561         }
562 #endif /* CONFIG_NCPFS_NLS */
563
564         fput(server->ncp_filp);
565         kill_proc(server->m.wdog_pid, SIGTERM, 1);
566
567         if (server->priv.data) 
568                 ncp_kfree_s(server->priv.data, server->priv.len);
569         if (server->auth.object_name)
570                 ncp_kfree_s(server->auth.object_name, server->auth.object_name_len);
571         vfree(server->packet);
572
573 }
574
575 static int ncp_statfs(struct super_block *sb, struct statfs *buf)
576 {
577         /* We cannot say how much disk space is left on a mounted
578            NetWare Server, because free space is distributed over
579            volumes, and the current user might have disk quotas. So
580            free space is not that simple to determine. Our decision
581            here is to err conservatively. */
582
583         buf->f_type = NCP_SUPER_MAGIC;
584         buf->f_bsize = NCP_BLOCK_SIZE;
585         buf->f_blocks = 0;
586         buf->f_bfree = 0;
587         buf->f_bavail = 0;
588         buf->f_namelen = 12;
589         return 0;
590 }
591
592 int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
593 {
594         struct inode *inode = dentry->d_inode;
595         int result = 0;
596         int info_mask;
597         struct nw_modify_dos_info info;
598         struct ncp_server *server;
599
600         result = -EIO;
601
602         server = NCP_SERVER(inode);
603         if ((!server) || !ncp_conn_valid(server))
604                 goto out;
605
606         /* ageing the dentry to force validation */
607         ncp_age_dentry(server, dentry);
608
609         result = inode_change_ok(inode, attr);
610         if (result < 0)
611                 goto out;
612
613         result = -EPERM;
614         if (((attr->ia_valid & ATTR_UID) &&
615              (attr->ia_uid != server->m.uid)))
616                 goto out;
617
618         if (((attr->ia_valid & ATTR_GID) &&
619              (attr->ia_gid != server->m.gid)))
620                 goto out;
621
622         if (((attr->ia_valid & ATTR_MODE) &&
623              (attr->ia_mode &
624               ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO))))
625                 goto out;
626
627         info_mask = 0;
628         memset(&info, 0, sizeof(info));
629
630 #if 1 
631         if ((attr->ia_valid & ATTR_MODE) != 0)
632         {
633                 if (S_ISDIR(inode->i_mode)) {
634                         umode_t newmode;
635
636                         info_mask |= DM_ATTRIBUTES;
637                         newmode = attr->ia_mode;
638                         newmode &= NCP_SERVER(inode)->m.dir_mode;
639
640                         if (newmode & 0222)
641                                 info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
642                         else
643                                 info.attributes |=  (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
644                 } else if (!S_ISREG(inode->i_mode))
645                 {
646                         return -EPERM;
647                 }
648                 else
649                 {
650                         umode_t newmode;
651 #ifdef CONFIG_NCPFS_EXTRAS                      
652                         int extras;
653                         
654                         extras = server->m.flags & NCP_MOUNT_EXTRAS;
655 #endif
656                         info_mask |= DM_ATTRIBUTES;
657                         newmode=attr->ia_mode;
658 #ifdef CONFIG_NCPFS_EXTRAS
659                         if (!extras)
660 #endif
661                                 newmode &= server->m.file_mode;
662
663                         if (newmode & 0222) /* any write bit set */
664                         {
665                                 info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
666                         }
667                         else
668                         {
669                                 info.attributes |=  (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
670                         }
671 #ifdef CONFIG_NCPFS_EXTRAS
672                         if (extras) {
673                                 if (newmode & 0111) /* any execute bit set */
674                                         info.attributes |= aSHARED | aSYSTEM;
675                                 /* read for group/world and not in default file_mode */
676                                 else if (newmode & ~server->m.file_mode & 0444)
677                                         info.attributes |= aSHARED;
678                         }
679 #endif
680                 }
681         }
682 #endif
683
684         if ((attr->ia_valid & ATTR_CTIME) != 0) {
685                 info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE);
686                 ncp_date_unix2dos(attr->ia_ctime,
687                              &(info.creationTime), &(info.creationDate));
688                 info.creationTime = le16_to_cpu(info.creationTime);
689                 info.creationDate = le16_to_cpu(info.creationDate);
690         }
691         if ((attr->ia_valid & ATTR_MTIME) != 0) {
692                 info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE);
693                 ncp_date_unix2dos(attr->ia_mtime,
694                                   &(info.modifyTime), &(info.modifyDate));
695                 info.modifyTime = le16_to_cpu(info.modifyTime);
696                 info.modifyDate = le16_to_cpu(info.modifyDate);
697         }
698         if ((attr->ia_valid & ATTR_ATIME) != 0) {
699                 __u16 dummy;
700                 info_mask |= (DM_LAST_ACCESS_DATE);
701                 ncp_date_unix2dos(attr->ia_ctime,
702                                   &(dummy), &(info.lastAccessDate));
703                 info.lastAccessDate = le16_to_cpu(info.lastAccessDate);
704         }
705         if (info_mask != 0) {
706                 result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode),
707                                       inode, info_mask, &info);
708                 if (result != 0) {
709                         result = -EACCES;
710
711                         if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) {
712                                 /* NetWare seems not to allow this. I
713                                    do not know why. So, just tell the
714                                    user everything went fine. This is
715                                    a terrible hack, but I do not know
716                                    how to do this correctly. */
717                                 result = 0;
718                         }
719                 }
720 #ifdef CONFIG_NCPFS_STRONG              
721                 if ((!result) && (info_mask & DM_ATTRIBUTES))
722                         NCP_FINFO(inode)->nwattr = info.attributes;
723 #endif
724         }
725         if ((attr->ia_valid & ATTR_SIZE) != 0) {
726                 int written;
727
728                 DPRINTK("ncpfs: trying to change size to %ld\n",
729                         attr->ia_size);
730
731                 if ((result = ncp_make_open(inode, O_WRONLY)) < 0) {
732                         return -EACCES;
733                 }
734                 ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
735                           attr->ia_size, 0, "", &written);
736
737                 /* According to ndir, the changes only take effect after
738                    closing the file */
739                 ncp_inode_close(inode);
740                 result = ncp_make_closed(inode);
741                 if (!result)
742                         result = vmtruncate(inode, attr->ia_size);
743         }
744 out:
745         return result;
746 }
747
748 #ifdef DEBUG_NCP_MALLOC
749 int ncp_malloced;
750 int ncp_current_malloced;
751 #endif
752
753 static DECLARE_FSTYPE(ncp_fs_type, "ncpfs", ncp_read_super, 0);
754
755 static int __init init_ncp_fs(void)
756 {
757         int err;
758         DPRINTK("ncpfs: init_module called\n");
759
760 #ifdef DEBUG_NCP_MALLOC
761         ncp_malloced = 0;
762         ncp_current_malloced = 0;
763 #endif
764         err = init_inodecache();
765         if (err)
766                 goto out1;
767         err = register_filesystem(&ncp_fs_type);
768         if (err)
769                 goto out;
770         return 0;
771 out:
772         destroy_inodecache();
773 out1:
774         return err;
775 }
776
777 static void __exit exit_ncp_fs(void)
778 {
779         DPRINTK("ncpfs: cleanup_module called\n");
780         unregister_filesystem(&ncp_fs_type);
781         destroy_inodecache();
782 #ifdef DEBUG_NCP_MALLOC
783         PRINTK("ncp_malloced: %d\n", ncp_malloced);
784         PRINTK("ncp_current_malloced: %d\n", ncp_current_malloced);
785 #endif
786 }
787
788 EXPORT_NO_SYMBOLS;
789
790 module_init(init_ncp_fs)
791 module_exit(exit_ncp_fs)
792 MODULE_LICENSE("GPL");