windows-insert-locking-without-afsid-20060422
authorJeffrey Altman <jaltman@secure-endpoints.com>
Sat, 22 Apr 2006 20:44:28 +0000 (20:44 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Sat, 22 Apr 2006 20:44:28 +0000 (20:44 +0000)
This patch disables the lookup of the user's AFSID and instead solves
the problem by tracking which cm_user_t created the file.  If the user
is unable to obtain a write lock and is the creator, then we check for
the Insert bit.  Otherwise, we deny access.

12 files changed:
src/WINNT/afsd/cm_access.c
src/WINNT/afsd/cm_access.h
src/WINNT/afsd/cm_aclent.c
src/WINNT/afsd/cm_aclent.h
src/WINNT/afsd/cm_ioctl.c
src/WINNT/afsd/cm_scache.c
src/WINNT/afsd/cm_scache.h
src/WINNT/afsd/cm_user.h
src/WINNT/afsd/cm_vnodeops.c
src/WINNT/afsd/smb.c
src/WINNT/afsd/smb.h
src/WINNT/afsd/smb3.c

index b7c50ae..6720ec2 100644 (file)
@@ -32,8 +32,8 @@
  * can't be locked.  Thus, this must always be called in a while loop to stabilize
  * things, since we can always lose the race condition getting to the parent vnode.
  */
-int cm_HaveAccessRights(struct cm_scache *scp, struct cm_user *up, long rights,
-                        long *outRightsp)
+int cm_HaveAccessRights(struct cm_scache *scp, struct cm_user *up, afs_uint32 rights,
+                        afs_uint32 *outRightsp)
 {
     cm_scache_t *aclScp;
     long code;
index 7e5933e..b5f7d2a 100644 (file)
@@ -13,7 +13,7 @@
 #include "cm_user.h"
 
 extern int cm_HaveAccessRights(struct cm_scache *scp, struct cm_user *up,
-       long rights, long *outRights);
+       afs_uint32 rights, afs_uint32 *outRights);
 
 extern long cm_GetAccessRights(struct cm_scache *scp, struct cm_user *up,
        struct cm_req *reqp);
index 0a1779f..46799cd 100644 (file)
@@ -68,7 +68,7 @@ static void CleanupACLEnt(cm_aclent_t * aclp)
  * Get an acl cache entry for a particular user and file, or return that it doesn't exist.
  * Called with the scp locked.
  */
-long cm_FindACLCache(cm_scache_t *scp, cm_user_t *userp, long *rightsp)
+long cm_FindACLCache(cm_scache_t *scp, cm_user_t *userp, afs_uint32 *rightsp)
 {
     cm_aclent_t *aclp;
     long retval = -1;
@@ -147,7 +147,7 @@ static cm_aclent_t *GetFreeACLEnt(cm_scache_t * scp)
  *
  * The scp must be locked when this function is called.
  */
-long cm_AddACLCache(cm_scache_t *scp, cm_user_t *userp, long rights)
+long cm_AddACLCache(cm_scache_t *scp, cm_user_t *userp, afs_uint32 rights)
 {
     register struct cm_aclent *aclp;
 
index a209112..61eaa6b 100644 (file)
@@ -25,7 +25,7 @@ typedef struct cm_aclent {
     struct cm_aclent *nextp;   /* next guy same vnode */
     struct cm_scache *backp;   /* back ptr to vnode */
     struct cm_user *userp;     /* user whose access is cached */
-    long randomAccess;         /* watch for more rights in acl.h */
+    afs_uint32 randomAccess;   /* watch for more rights in acl.h */
     unsigned long tgtLifetime; /* time this expires */
 } cm_aclent_t;
 
@@ -33,11 +33,11 @@ extern osi_rwlock_t cm_aclLock;
 
 extern long cm_InitACLCache(int newFile, long size);
 
-extern long cm_FindACLCache(struct cm_scache *scp, struct cm_user *userp, long *rightsp);
+extern long cm_FindACLCache(struct cm_scache *scp, struct cm_user *userp, afs_uint32 *rightsp);
 
 static cm_aclent_t *GetFreeACLEnt(cm_scache_t * scp);
 
-extern long cm_AddACLCache(struct cm_scache *scp, struct cm_user *userp, long rights);
+extern long cm_AddACLCache(struct cm_scache *scp, struct cm_user *userp, afs_uint32 rights);
 
 extern void cm_FreeAllACLEnts(struct cm_scache *scp);
 
index 70b93cb..151350f 100644 (file)
@@ -1896,6 +1896,7 @@ long cm_IoctlDeletelink(struct smb_ioctl *ioctlp, struct cm_user *userp)
     return code;
 }
 
+#ifdef QUERY_AFSID
 long cm_UsernameToId(char *uname, cm_ucell_t * ucellp, afs_uint32* uid)
 {
     afs_int32 code;
@@ -1970,7 +1971,7 @@ long cm_UsernameToId(char *uname, cm_ucell_t * ucellp, afs_uint32* uid)
 
     return 0;
 }
-
+#endif /* QUERY_AFSID */
 
 long cm_IoctlSetToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
 {
@@ -2088,11 +2089,14 @@ long cm_IoctlSetToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
     ucellp->kvno = ct.AuthHandle;
     ucellp->expirationTime = ct.EndTimestamp;
     ucellp->gen++;
+#ifdef QUERY_AFSID
     ucellp->uid = ANONYMOUSID;
+#endif
     if (uname) {
         StringCbCopyA(ucellp->userName, MAXKTCNAMELEN, uname);
-
+#ifdef QUERY_AFSID
        cm_UsernameToId(uname, ucellp, &ucellp->uid);
+#endif
     }
     ucellp->flags |= CM_UCELLFLAG_RXKAD;
     lock_ReleaseMutex(&userp->mx);
index f80f7f7..c282692 100644 (file)
@@ -668,12 +668,12 @@ cm_scache_t * cm_FindSCacheParent(cm_scache_t * scp)
  * CM_SCACHESYNC_STOREDATA_EXCL and CM_SCACHEFLAG_DATASTORING.
  */
 long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *up, cm_req_t *reqp,
-               long rights, long flags)
+               afs_uint32 rights, afs_uint32 flags)
 {
     osi_queueData_t *qdp;
     long code;
     cm_buf_t *tbufp;
-    long outRights;
+    afs_uint32 outRights;
     int bufLocked;
 
     /* lookup this first */
@@ -874,7 +874,8 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *up, cm_req_t *reqp,
                 return CM_ERROR_READONLY;
 
             if (cm_HaveAccessRights(scp, up, rights, &outRights)) {
-                if (~outRights & rights) return CM_ERROR_NOACCESS;
+                if (~outRights & rights) 
+                   return CM_ERROR_NOACCESS;
             }
             else {
                 /* we don't know the required access rights */
@@ -990,7 +991,7 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *up, cm_req_t *reqp,
 /* for those syncops that setup for RPCs.
  * Called with scache locked.
  */
-void cm_SyncOpDone(cm_scache_t *scp, cm_buf_t *bufp, long flags)
+void cm_SyncOpDone(cm_scache_t *scp, cm_buf_t *bufp, afs_uint32 flags)
 {
     osi_queueData_t *qdp;
     cm_buf_t *tbufp;
@@ -1074,7 +1075,7 @@ void cm_SyncOpDone(cm_scache_t *scp, cm_buf_t *bufp, long flags)
  * started before that, can cause old info to be merged from the first call.
  */
 void cm_MergeStatus(cm_scache_t *scp, AFSFetchStatus *statusp, AFSVolSync *volp,
-                    cm_user_t *userp, int flags)
+                    cm_user_t *userp, afs_uint32 flags)
 {
     // yj: i want to create some fake status for the /afs directory and the
     // entries under that directory
index e111e18..389baac 100644 (file)
@@ -23,14 +23,6 @@ typedef struct cm_fid {
         unsigned long unique;
 } cm_fid_t;
 
-#if 0
-typedef struct cm_accessCache {
-       osi_queue_t q;                  /* queue header */
-        struct cm_user *userp;         /* user having access rights */
-        unsigned long rights;          /* rights */
-} cm_accessCache_t;
-#endif
-
 /* Key used for byte range locking.  Each unique key identifies a
    unique client per cm_scache_t for the purpose of locking. */
 typedef afs_uint64 cm_key_t;
@@ -94,49 +86,50 @@ typedef struct cm_prefetch {                /* last region scanned for prefetching */
 #define CM_SCACHE_MAGIC ('S' | 'C'<<8 | 'A'<<16 | 'C'<<24)
 
 typedef struct cm_scache {
-    osi_queue_t q;              /* lru queue; cm_scacheLock */
+    osi_queue_t q;                     /* lru queue; cm_scacheLock */
     afs_uint32      magic;
-    struct cm_scache *nextp;   /* next in hash; cm_scacheLock */
+    struct cm_scache *nextp;           /* next in hash; cm_scacheLock */
     cm_fid_t fid;
-    afs_uint32 flags;          /* flags; locked by mx */
+    afs_uint32 flags;                  /* flags; locked by mx */
 
     /* synchronization stuff */
-    osi_mutex_t mx;            /* mutex for this structure */
-    osi_rwlock_t bufCreateLock;        /* read-locked during buffer creation;
-                                * write-locked to prevent buffers from
-                                 * being created during a truncate op, etc.
-                                 */
+    osi_mutex_t mx;                    /* mutex for this structure */
+    osi_rwlock_t bufCreateLock;                /* read-locked during buffer creation;
+                                         * write-locked to prevent buffers from
+                                        * being created during a truncate op, etc.
+                                        */
     afs_uint32 refCount;               /* reference count; cm_scacheLock */
-    osi_queueData_t *bufReadsp;        /* queue of buffers being read */
+    osi_queueData_t *bufReadsp;                /* queue of buffers being read */
     osi_queueData_t *bufWritesp;       /* queue of buffers being written */
 
     /* parent info for ACLs */
     afs_uint32 parentVnode;            /* parent vnode for ACL callbacks */
-    afs_uint32 parentUnique;   /* for ACL callbacks */
+    afs_uint32 parentUnique;           /* for ACL callbacks */
 
     /* local modification stat */
-    afs_uint32 mask;           /* for clientModTime, length and
-                                 * truncPos */
+    afs_uint32 mask;                   /* for clientModTime, length and
+                                         * truncPos */
 
     /* file status */
     afs_uint32 fileType;               /* file type */
     time_t clientModTime;              /* mtime */
     time_t serverModTime;              /* at server, for concurrent call
                                          * comparisons */
-    osi_hyper_t length;                /* file length */
+    osi_hyper_t length;                        /* file length */
     cm_prefetch_t prefetch;            /* prefetch info structure */
-    afs_uint32 unixModeBits;   /* unix protection mode bits */
+    afs_uint32 unixModeBits;           /* unix protection mode bits */
     afs_uint32 linkCount;              /* link count */
     afs_uint32 dataVersion;            /* data version */
-    afs_uint32 owner;          /* file owner */
-    afs_uint32 group;          /* file owning group */
+    afs_uint32 owner;                  /* file owner */
+    afs_uint32 group;                  /* file owning group */
+    cm_user_t *creator;                        /* user, if new file */
 
     /* pseudo file status */
-    osi_hyper_t serverLength;  /* length known to server */
+    osi_hyper_t serverLength;          /* length known to server */
 
     /* aux file status */
-    osi_hyper_t truncPos;      /* file size to truncate to before
-                                 * storing data */
+    osi_hyper_t truncPos;              /* file size to truncate to before
+                                         * storing data */
 
     /* symlink and mount point info */
     char mountPointStringp[MOUNTPOINTLEN];     /* the string stored in a mount point;
@@ -317,12 +310,12 @@ extern cm_scache_t *cm_GetNewSCache(void);
 extern int cm_FidCmp(cm_fid_t *, cm_fid_t *);
 
 extern long cm_SyncOp(cm_scache_t *, struct cm_buf *, struct cm_user *,
-       struct cm_req *, long, long);
+       struct cm_req *, afs_uint32, afs_uint32);
 
-extern void cm_SyncOpDone(cm_scache_t *, struct cm_buf *, long);
+extern void cm_SyncOpDone(cm_scache_t *, struct cm_buf *, afs_uint32);
 
 extern void cm_MergeStatus(cm_scache_t *, struct AFSFetchStatus *, struct AFSVolSync *,
-       struct cm_user *, int flags);
+       struct cm_user *, afs_uint32 flags);
 
 extern void cm_AFSFidFromFid(struct AFSFid *, cm_fid_t *);
 
index 19013b1..361bec1 100644 (file)
@@ -32,7 +32,9 @@ typedef struct cm_ucell {
     int iterator;                      /* for use as ListTokens cookie */
     long flags;                                /* flags */
     char userName[MAXKTCNAMELEN];      /* user name */
+#ifdef QUERY_AFSID
     afs_uint32 uid;                    /* User's AFS ID in this cell */
+#endif
 } cm_ucell_t;
 
 #define CM_UCELLFLAG_HASTIX    1       /* has Kerberos tickets */
index 9ae2e0b..51f8072 100644 (file)
@@ -2270,6 +2270,7 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
         code = cm_GetSCache(&newFid, &scp, userp, reqp);
         if (code == 0) {
             lock_ObtainMutex(&scp->mx);
+           scp->creator = userp;               /* remember who created it */
             if (!cm_HaveCallback(scp)) {
                 cm_MergeStatus(scp, &newFileStatus, &volSync,
                                 userp, 0);
@@ -3434,7 +3435,8 @@ long cm_LockCheckPerms(cm_scache_t * scp,
                      CM_SCACHESYNC_NEEDCALLBACK);
 
     if (code == CM_ERROR_NOACCESS &&
-        lock_type == LockWrite) {
+        lock_type == LockWrite &&
+       scp->creator == userp) {
         /* check for PRSFS_INSERT. */
         cm_ucell_t * ucp;
 
@@ -3442,25 +3444,8 @@ long cm_LockCheckPerms(cm_scache_t * scp,
                          CM_SCACHESYNC_GETSTATUS |
                          CM_SCACHESYNC_NEEDCALLBACK);
 
-        if (code) {
-            if (code == CM_ERROR_NOACCESS)
-                osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits for scp");
-
-            goto return_code;
-        }
-
-        code = CM_ERROR_NOACCESS;
-
-        lock_ObtainMutex(&userp->mx);
-        for (ucp = userp->cellInfop; ucp; ucp = ucp->nextp) {
-            if (scp->fid.cell == ucp->cellp->cellID) {
-                if (scp->owner == ucp->uid)
-                    code = 0;
-
-                break;
-            }
-        }
-        lock_ReleaseMutex(&userp->mx);
+       if (code == CM_ERROR_NOACCESS)
+           osi_Log0(afsd_logp, "cm_LockCheckPerms user is creator but has no INSERT bits for scp");
     }
 
  return_code:
index 115a45f..977da1f 100644 (file)
@@ -5817,6 +5817,16 @@ long smb_CloseFID(smb_vc_t *vcp, smb_fid_t *fidp, cm_user_t *userp,
        fidp->flags &= ~SMB_FID_DELONCLOSE;
     }
 
+    /* if this was a newly created file, then clear the creator
+     * in the stat cache entry. */
+    if (fidp->flags & SMB_FID_CREATED) {
+        lock_ObtainMutex(&fidp->scp->mx);
+       if (fidp->scp->creator == userp)
+           fidp->scp->creator = NULL;
+       lock_ReleaseMutex(&fidp->scp->mx);
+       fidp->flags &= ~SMB_FID_CREATED;
+    }
+
     if (fidp->flags & SMB_FID_NTOPEN) {
        fidp->NTopen_dscp = NULL;
         fidp->NTopen_pathp = NULL;
@@ -6846,6 +6856,7 @@ long smb_ReceiveCoreCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     afs_uint32 dosTime;
     char *tidPathp;
     cm_req_t req;
+    int created = 0;                   /* the file was new */
 
     cm_InitReq(&req);
 
@@ -6945,11 +6956,13 @@ long smb_ReceiveCoreCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         smb_UnixTimeFromDosUTime(&setAttr.clientModTime, dosTime);
         code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
                          &req);
-        if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-            smb_NotifyChange(FILE_ACTION_ADDED,
-                             FILE_NOTIFY_CHANGE_FILE_NAME,
-                             dscp, lastNamep, NULL, TRUE);
-        if (!excl && code == CM_ERROR_EXISTS) {
+        if (code == 0) {
+           created = 1;
+           if (dscp->flags & CM_SCACHEFLAG_ANYWATCH)
+               smb_NotifyChange(FILE_ACTION_ADDED,     
+                                FILE_NOTIFY_CHANGE_FILE_NAME,
+                                dscp, lastNamep, NULL, TRUE);
+       } else if (!excl && code == CM_ERROR_EXISTS) {
             /* not an exclusive create, and someone else tried
              * creating it already, then we open it anyway.  We
              * don't bother retrying after this, since if this next
@@ -6994,6 +7007,10 @@ long smb_ReceiveCoreCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     /* always create it open for read/write */
     fidp->flags |= (SMB_FID_OPENREAD | SMB_FID_OPENWRITE);
 
+    /* remember that the file was newly created */
+    if (created)
+       fidp->flags |= SMB_FID_CREATED;
+
     /* save a pointer to the vnode */
     fidp->scp = scp;
     /* and the user */
index 30a66a1..f25c335 100644 (file)
@@ -358,7 +358,7 @@ typedef struct smb_fid {
 
 #define SMB_FID_OPENREAD               1       /* open for reading */
 #define SMB_FID_OPENWRITE              2       /* open for writing */
-#define SMB_FID_UNUSED                  4       /* free for use */
+#define SMB_FID_CREATED                 4       /* a new file */
 #define SMB_FID_IOCTL                  8       /* a file descriptor for the
                                                 * magic ioctl file */
 #define SMB_FID_OPENDELETE             0x10    /* open for deletion (NT) */
index f7dad97..4dfe8ac 100644 (file)
@@ -2062,6 +2062,7 @@ long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
     long returnEALength;
     char *tidPathp;
     cm_req_t req;
+    int created = 0;
 
     cm_InitReq(&req);
 
@@ -2280,11 +2281,13 @@ long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
         smb_UnixTimeFromSearchTime(&setAttr.clientModTime, dosTime);
         code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
                           &req);
-        if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-            smb_NotifyChange(FILE_ACTION_ADDED,
-                             FILE_NOTIFY_CHANGE_FILE_NAME,  
-                             dscp, lastNamep, NULL, TRUE);
-        if (!excl && code == CM_ERROR_EXISTS) {
+        if (code == 0) {
+           created = 1;
+           if (dscp->flags & CM_SCACHEFLAG_ANYWATCH)
+               smb_NotifyChange(FILE_ACTION_ADDED,
+                                FILE_NOTIFY_CHANGE_FILE_NAME,  
+                                 dscp, lastNamep, NULL, TRUE);
+       } else if (!excl && code == CM_ERROR_EXISTS) {
             /* not an exclusive create, and someone else tried
              * creating it already, then we open it anyway.  We
              * don't bother retrying after this, since if this next
@@ -2359,6 +2362,11 @@ long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
        fidp->flags |= SMB_FID_OPENREAD;
     if (openMode == 1 || openMode == 2)
         fidp->flags |= SMB_FID_OPENWRITE;
+
+    /* remember that the file was newly created */
+    if (created)
+       fidp->flags |= SMB_FID_CREATED;
+
     lock_ReleaseMutex(&fidp->mx);
 
     smb_ReleaseFID(fidp);
@@ -4515,6 +4523,7 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     int parmSlot;                      /* which parm we're dealing with */
     char *tidPathp;
     cm_req_t req;
+    int created = 0;
 
     cm_InitReq(&req);
 
@@ -4697,11 +4706,13 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         smb_UnixTimeFromDosUTime(&setAttr.clientModTime, dosTime);
         code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
                          &req);
-        if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-            smb_NotifyChange(FILE_ACTION_ADDED,
-                             FILE_NOTIFY_CHANGE_FILE_NAME,
-                             dscp, lastNamep, NULL, TRUE);
-        if (!excl && code == CM_ERROR_EXISTS) {
+        if (code == 0) {
+           created = 1;
+           if (dscp->flags & CM_SCACHEFLAG_ANYWATCH)
+               smb_NotifyChange(FILE_ACTION_ADDED,
+                                FILE_NOTIFY_CHANGE_FILE_NAME,
+                                dscp, lastNamep, NULL, TRUE);
+       } else if (!excl && code == CM_ERROR_EXISTS) {
             /* not an exclusive create, and someone else tried
              * creating it already, then we open it anyway.  We
              * don't bother retrying after this, since if this next
@@ -4757,6 +4768,10 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     if (openMode == 1 || openMode == 2)
         fidp->flags |= SMB_FID_OPENWRITE;
 
+    /* remember if the file was newly created */
+    if (created)
+       fidp->flags |= SMB_FID_CREATED;
+
     lock_ReleaseMutex(&fidp->mx);
     smb_ReleaseFID(fidp);
         
@@ -5366,6 +5381,7 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     char *tidPathp;
     BOOL foundscp;
     cm_req_t req;
+    int created = 0;
 
     cm_InitReq(&req);
 
@@ -5798,11 +5814,13 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
         setAttr.clientModTime = time(NULL);
         code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp, &req);
-        if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-            smb_NotifyChange(FILE_ACTION_ADDED,
-                              FILE_NOTIFY_CHANGE_FILE_NAME,
-                              dscp, lastNamep, NULL, TRUE);
-        if (code == CM_ERROR_EXISTS && createDisp != FILE_CREATE) {
+        if (code == 0) {
+           created = 1;
+           if (dscp->flags & CM_SCACHEFLAG_ANYWATCH)
+               smb_NotifyChange(FILE_ACTION_ADDED,
+                                FILE_NOTIFY_CHANGE_FILE_NAME,
+                                dscp, lastNamep, NULL, TRUE);
+       } else if (code == CM_ERROR_EXISTS && createDisp != FILE_CREATE) {
             /* Not an exclusive create, and someone else tried
              * creating it already, then we open it anyway.  We
              * don't bother retrying after this, since if this next
@@ -6027,6 +6045,10 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 
     fidp->flags = fidflags;
 
+    /* remember if the file was newly created */
+    if (created)
+       fidp->flags |= SMB_FID_CREATED;
+
     /* save parent dir and pathname for delete or change notification */
     if (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE)) {
         fidp->flags |= SMB_FID_NTOPEN;
@@ -6133,6 +6155,7 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
     ULONG *lparmp;
     char *outData;
     cm_req_t req;
+    int created = 0;
 
     cm_InitReq(&req);
 
@@ -6466,11 +6489,13 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
         setAttr.clientModTime = time(NULL);
         code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
                           &req);
-        if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-            smb_NotifyChange(FILE_ACTION_ADDED,
-                              FILE_NOTIFY_CHANGE_FILE_NAME,
-                              dscp, lastNamep, NULL, TRUE);
-        if (code == CM_ERROR_EXISTS && createDisp != FILE_CREATE) {
+        if (code == 0) {
+           created = 1;
+           if (dscp->flags & CM_SCACHEFLAG_ANYWATCH)
+               smb_NotifyChange(FILE_ACTION_ADDED,
+                                FILE_NOTIFY_CHANGE_FILE_NAME,
+                                dscp, lastNamep, NULL, TRUE);
+       } else if (code == CM_ERROR_EXISTS && createDisp != FILE_CREATE) {
             /* Not an exclusive create, and someone else tried
              * creating it already, then we open it anyway.  We
              * don't bother retrying after this, since if this next
@@ -6627,6 +6652,10 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
 
     fidp->flags = fidflags;
 
+    /* remember if the file was newly created */
+    if (created)
+       fidp->flags |= SMB_FID_CREATED;
+
     /* save parent dir and pathname for deletion or change notification */
     if (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE)) {
         fidp->flags |= SMB_FID_NTOPEN;