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