- if ((mode & O_TRUNC) == 0) {
- /* XXX - should not ignore errors from VOP_CLOSE */
- VOP_LOCK(un->un_lowervp);
- error = VOP_OPEN(un->un_lowervp, FREAD, cred, p);
- if (error == 0) {
- error = union_copyfile(p, cred,
- un->un_lowervp, un->un_uppervp);
- (void) VOP_CLOSE(un->un_lowervp, FREAD);
+ tvp = un->un_lowervp;
+ if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
+ struct nameidata nd;
+ struct filedesc *fdp = p->p_fd;
+ int fmode;
+ int cmode;
+
+ /*
+ * Open the named file in the upper layer. Note that
+ * the file may have come into existence *since* the
+ * lookup was done, since the upper layer may really
+ * be a loopback mount of some other filesystem...
+ * so open the file with exclusive create and barf if
+ * it already exists.
+ * XXX - perhaps shoudl re-lookup the node (once more
+ * with feeling) and simply open that. Who knows.
+ */
+ NDINIT(&nd, CREATE, 0, UIO_SYSSPACE, un->un_path, p);
+ fmode = (O_CREAT|O_TRUNC|O_EXCL);
+ cmode = UN_FILEMODE & ~fdp->fd_cmask;
+ error = vn_open(&nd, fmode, cmode);
+ if (error)
+ return (error);
+ un->un_uppervp = nd.ni_vp; /* XXX */
+ /* at this point, uppervp is locked */
+
+ /*
+ * Now, if the file is being opened with truncation,
+ * then the (new) upper vnode is ready to fly,
+ * otherwise the data from the lower vnode must be
+ * copied to the upper layer first. This only works
+ * for regular files (check is made above).
+ */
+ if ((mode & O_TRUNC) == 0) {
+ /*
+ * XXX - should not ignore errors
+ * from VOP_CLOSE
+ */
+ VOP_LOCK(un->un_lowervp);
+ error = VOP_OPEN(tvp, FREAD, cred, p);
+ if (error == 0) {
+ error = union_copyfile(p, cred,
+ tvp, un->un_uppervp);
+ VOP_UNLOCK(tvp);
+ (void) VOP_CLOSE(tvp, FREAD);
+ } else {
+ VOP_UNLOCK(tvp);
+ }
+ VOP_UNLOCK(un->un_uppervp);
+ (void) VOP_CLOSE(un->un_uppervp, FWRITE);
+ VOP_LOCK(un->un_uppervp);