Merge branch 'master'
[powerpc.git] / fs / cifs / cifsfs.c
index 5082fce..682b023 100644 (file)
@@ -59,6 +59,8 @@ unsigned int ntlmv2_support = 0;
 unsigned int sign_CIFS_PDUs = 1;
 extern struct task_struct * oplockThread; /* remove sparse warning */
 struct task_struct * oplockThread = NULL;
+extern struct task_struct * dnotifyThread; /* remove sparse warning */
+struct task_struct * dnotifyThread = NULL;
 unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
 module_param(CIFSMaxBufSize, int, 0);
 MODULE_PARM_DESC(CIFSMaxBufSize,"Network buffer size (not including header). Default: 16384 Range: 8192 to 130048");
@@ -73,6 +75,7 @@ module_param(cifs_max_pending, int, 0);
 MODULE_PARM_DESC(cifs_max_pending,"Simultaneous requests to server. Default: 50 Range: 2 to 256");
 
 static DECLARE_COMPLETION(cifs_oplock_exited);
+static DECLARE_COMPLETION(cifs_dnotify_exited);
 
 extern mempool_t *cifs_sm_req_poolp;
 extern mempool_t *cifs_req_poolp;
@@ -169,7 +172,8 @@ cifs_put_super(struct super_block *sb)
 static int
 cifs_statfs(struct super_block *sb, struct kstatfs *buf)
 {
-       int xid, rc = -EOPNOTSUPP;
+       int xid; 
+       int rc = -EOPNOTSUPP;
        struct cifs_sb_info *cifs_sb;
        struct cifsTconInfo *pTcon;
 
@@ -181,31 +185,38 @@ cifs_statfs(struct super_block *sb, struct kstatfs *buf)
        buf->f_type = CIFS_MAGIC_NUMBER;
 
        /* instead could get the real value via SMB_QUERY_FS_ATTRIBUTE_INFO */
-       buf->f_namelen = PATH_MAX;      /* PATH_MAX may be too long - it would presumably
-                                          be length of total path, note that some servers may be 
-                                          able to support more than this, but best to be safe
-                                          since Win2k and others can not handle very long filenames */
+       buf->f_namelen = PATH_MAX; /* PATH_MAX may be too long - it would 
+                                     presumably be total path, but note
+                                     that some servers (includinng Samba 3)
+                                     have a shorter maximum path */
        buf->f_files = 0;       /* undefined */
        buf->f_ffree = 0;       /* unlimited */
 
 #ifdef CONFIG_CIFS_EXPERIMENTAL
 /* BB we could add a second check for a QFS Unix capability bit */
-    if (pTcon->ses->capabilities & CAP_UNIX)
-           rc = CIFSSMBQFSPosixInfo(xid, pTcon, buf, cifs_sb->local_nls);
+/* BB FIXME check CIFS_POSIX_EXTENSIONS Unix cap first FIXME BB */
+    if ((pTcon->ses->capabilities & CAP_UNIX) && (CIFS_POSIX_EXTENSIONS &
+                       le64_to_cpu(pTcon->fsUnixInfo.Capability)))
+           rc = CIFSSMBQFSPosixInfo(xid, pTcon, buf);
 
     /* Only need to call the old QFSInfo if failed
     on newer one */
     if(rc)
 #endif /* CIFS_EXPERIMENTAL */
-       rc = CIFSSMBQFSInfo(xid, pTcon, buf, cifs_sb->local_nls);
+       rc = CIFSSMBQFSInfo(xid, pTcon, buf);
 
+       /* Old Windows servers do not support level 103, retry with level 
+          one if old server failed the previous call */ 
+       if(rc)
+               rc = SMBOldQFSInfo(xid, pTcon, buf);
        /*     
           int f_type;
           __fsid_t f_fsid;
           int f_namelen;  */
-       /* BB get from info put in tcon struct at mount time with call to QFSAttrInfo */
+       /* BB get from info in tcon struct at mount time call to QFSAttrInfo */
        FreeXid(xid);
-       return 0;               /* always return success? what if volume is no longer available? */
+       return 0;               /* always return success? what if volume is no
+                                  longer available? */
 }
 
 static int cifs_permission(struct inode * inode, int mask, struct nameidata *nd)
@@ -249,7 +260,7 @@ cifs_alloc_inode(struct super_block *sb)
        cifs_inode->clientCanCacheAll = FALSE;
        cifs_inode->vfs_inode.i_blksize = CIFS_MAX_MSGSIZE;
        cifs_inode->vfs_inode.i_blkbits = 14;  /* 2**14 = CIFS_MAX_MSGSIZE */
-
+       cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME;
        INIT_LIST_HEAD(&cifs_inode->openFileList);
        return &cifs_inode->vfs_inode;
 }
@@ -394,6 +405,37 @@ static struct quotactl_ops cifs_quotactl_ops = {
 };
 #endif
 
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+static void cifs_umount_begin(struct super_block * sblock)
+{
+       struct cifs_sb_info *cifs_sb;
+       struct cifsTconInfo * tcon;
+
+       cifs_sb = CIFS_SB(sblock);
+       if(cifs_sb == NULL)
+               return;
+
+       tcon = cifs_sb->tcon;
+       if(tcon == NULL)
+               return;
+       down(&tcon->tconSem);
+       if (atomic_read(&tcon->useCount) == 1)
+               tcon->tidStatus = CifsExiting;
+       up(&tcon->tconSem);
+
+       /* cancel_brl_requests(tcon); */
+       /* cancel_notify_requests(tcon); */
+       if(tcon->ses && tcon->ses->server)
+       {
+               cFYI(1,("wake up tasks now - umount begin not complete"));
+               wake_up_all(&tcon->ses->server->request_q);
+       }
+/* BB FIXME - finish add checks for tidStatus BB */
+
+       return;
+}
+#endif 
+
 static int cifs_remount(struct super_block *sb, int *flags, char *data)
 {
        *flags |= MS_NODIRATIME;
@@ -411,7 +453,9 @@ struct super_operations cifs_super_ops = {
    unless later we add lazy close of inodes or unless the kernel forgets to call
    us with the same number of releases (closes) as opens */
        .show_options = cifs_show_options,
-/*    .umount_begin   = cifs_umount_begin, *//* consider adding in the future */
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+       .umount_begin   = cifs_umount_begin,
+#endif
        .remount_fs = cifs_remount,
 };
 
@@ -559,6 +603,10 @@ struct file_operations cifs_file_ops = {
        .flush = cifs_flush,
        .mmap  = cifs_file_mmap,
        .sendfile = generic_file_sendfile,
+#ifdef CONFIG_CIFS_POSIX
+       .ioctl  = cifs_ioctl,
+#endif /* CONFIG_CIFS_POSIX */
+
 #ifdef CONFIG_CIFS_EXPERIMENTAL
        .readv = generic_file_readv,
        .writev = generic_file_writev,
@@ -579,6 +627,10 @@ struct file_operations cifs_file_direct_ops = {
        .fsync = cifs_fsync,
        .flush = cifs_flush,
        .sendfile = generic_file_sendfile, /* BB removeme BB */
+#ifdef CONFIG_CIFS_POSIX
+       .ioctl  = cifs_ioctl,
+#endif /* CONFIG_CIFS_POSIX */
+
 #ifdef CONFIG_CIFS_EXPERIMENTAL
        .dir_notify = cifs_dir_notify,
 #endif /* CONFIG_CIFS_EXPERIMENTAL */
@@ -591,6 +643,7 @@ struct file_operations cifs_dir_ops = {
 #ifdef CONFIG_CIFS_EXPERIMENTAL
        .dir_notify = cifs_dir_notify,
 #endif /* CONFIG_CIFS_EXPERIMENTAL */
+        .ioctl  = cifs_ioctl,
 };
 
 static void
@@ -768,9 +821,9 @@ static int cifs_oplock_thread(void * dummyarg)
 
        oplockThread = current;
        do {
-               set_current_state(TASK_INTERRUPTIBLE);
+               if (try_to_freeze()) 
+                       continue;
                
-               schedule_timeout(1*HZ);  
                spin_lock(&GlobalMid_Lock);
                if(list_empty(&GlobalOplock_Q)) {
                        spin_unlock(&GlobalMid_Lock);
@@ -819,11 +872,29 @@ static int cifs_oplock_thread(void * dummyarg)
                                }
                        } else
                                spin_unlock(&GlobalMid_Lock);
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout(1);  /* yield in case q were corrupt */
                }
        } while(!signal_pending(current));
+       oplockThread = NULL;
        complete_and_exit (&cifs_oplock_exited, 0);
 }
 
+static int cifs_dnotify_thread(void * dummyarg)
+{
+       daemonize("cifsdnotifyd");
+       allow_signal(SIGTERM);
+
+       dnotifyThread = current;
+       do {
+               if(try_to_freeze())
+                       continue;
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(39*HZ);
+       } while(!signal_pending(current));
+       complete_and_exit (&cifs_dnotify_exited, 0);
+}
+
 static int __init
 init_cifs(void)
 {
@@ -835,6 +906,10 @@ init_cifs(void)
        INIT_LIST_HEAD(&GlobalSMBSessionList);
        INIT_LIST_HEAD(&GlobalTreeConnectionList);
        INIT_LIST_HEAD(&GlobalOplock_Q);
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+       INIT_LIST_HEAD(&GlobalDnotifyReqList);
+       INIT_LIST_HEAD(&GlobalDnotifyRsp_Q);
+#endif 
 /*
  *  Initialize Global counters
  */
@@ -870,10 +945,16 @@ init_cifs(void)
                                if (!rc) {                
                                        rc = (int)kernel_thread(cifs_oplock_thread, NULL, 
                                                CLONE_FS | CLONE_FILES | CLONE_VM);
-                                       if(rc > 0)
-                                               return 0;
-                                       else 
+                                       if(rc > 0) {
+                                               rc = (int)kernel_thread(cifs_dnotify_thread, NULL,
+                                                       CLONE_FS | CLONE_FILES | CLONE_VM);
+                                               if(rc > 0)
+                                                       return 0;
+                                               else
+                                                       cERROR(1,("error %d create dnotify thread", rc));
+                                       } else {
                                                cERROR(1,("error %d create oplock thread",rc));
+                                       }
                                }
                                cifs_destroy_request_bufs();
                        }
@@ -902,6 +983,10 @@ exit_cifs(void)
                send_sig(SIGTERM, oplockThread, 1);
                wait_for_completion(&cifs_oplock_exited);
        }
+       if(dnotifyThread) {
+               send_sig(SIGTERM, dnotifyThread, 1);
+               wait_for_completion(&cifs_dnotify_exited);
+       }
 }
 
 MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>");