Ejemplo n.º 1
0
/* ARGSUSED7 */
int				/* ERRNO if error, 0 if successful. */
sam_create_ino(
	sam_node_t *pip,	/* pointer to parent directory inode. */
	char *cp,		/* pointer to the component name to create. */
	vattr_t *vap,		/* vattr ptr for type & mode information. */
	vcexcl_t ex,		/* exclusive create flag. */
	int mode,		/* file mode information. */
	vnode_t **vpp,		/* pointer pointer to returned vnode. */
	cred_t *credp,		/* credentials pointer. */
	int filemode)		/* open file mode */
{
	int error = 0;
	sam_node_t *ip;
	struct sam_name name;	/* If no entry, slot info is returned here */
	int trans_size;
	int issync;
	int truncflag = 0;
	int terr = 0;
#ifdef LQFS_TODO_LOCKFS
	struct ulockfs *ulp;
#endif /* LQFS_TODO_LOCKFS */

	/*
	 * Cannot set sticky bit unless superuser.
	 */
	if ((vap->va_mode & VSVTX) && secpolicy_vnode_stky_modify(credp)) {
		vap->va_mode &= ~VSVTX;
	}

lookup_name:

#ifdef LQFS_TODO_LOCKFS
	error = qfs_lockfs_begin(pip->mp, &ulp, ULOCKFS_CREATE_MASK);
	if (error) {
		return (error);
	}

	if (ulp) {
#endif /* LQFS_TODO_LOCKFS */
		/* Start LQFS create transaction */
		trans_size = (int)TOP_CREATE_SIZE(pip);
		TRANS_BEGIN_CSYNC(pip->mp, issync, TOP_CREATE, trans_size);
#ifdef LQFS_TODO_LOCKFS
	}
#endif /* LQFS_TODO_LOCKFS */

	RW_LOCK_OS(&pip->data_rwl, RW_WRITER);
	name.operation = SAM_CREATE;
	if ((error = sam_lookup_name(pip, cp, &ip, &name, credp)) == ENOENT) {
		if (((error = sam_create_name(pip, cp, &ip, &name,
		    vap, credp)) != 0) &&
		    IS_SAM_ENOSPC(error)) {
			RW_UNLOCK_OS(&pip->data_rwl, RW_WRITER);
			/*
			 * Temporarily end LQFS create transaction
			 */
#ifdef LQFS_TODO_LOCKFS
			if (ulp) {
#endif /* LQFS_TODO_LOCKFS */
				TRANS_END_CSYNC(pip->mp, terr, issync,
				    TOP_CREATE, trans_size);
#ifdef LQFS_TODO_LOCKFS
			}
#endif /* LQFS_TODO_LOCKFS */
			error = sam_wait_space(pip, error);
			if (error == 0) {
				error = terr;
			}
			if (error) {
				return (error);
			}
			goto lookup_name;
		}
		RW_UNLOCK_OS(&pip->data_rwl, RW_WRITER);

	} else if (error == 0) {	/* If entry already exists. */
		RW_UNLOCK_OS(&pip->data_rwl, RW_WRITER);
		error = EEXIST;

		if (ex == NONEXCL) {	/* If non-exclusive create */
			if ((S_ISDIR(ip->di.mode) ||
			    S_ISATTRDIR(ip->di.mode)) &&
			    (mode & S_IWRITE)) {
				/* Cannot create over an existing dir. */
				error = EISDIR;
			} else if (SAM_PRIVILEGE_INO(ip->di.version,
			    ip->di.id.ino)) {
				/* Cannot create over privileged inodes */
				error = EPERM;
			} else if (mode) {	/* Check mode if set */
				error = sam_access_ino(ip, mode, FALSE, credp);
			} else {
				error = 0;
			}
			if ((error == 0) && S_ISREG(ip->di.mode) &&
			    (vap->va_mask & AT_SIZE) && (vap->va_size == 0)) {
				/*
				 * If logging, do the truncate after the
				 * LQFS create transaction is logged.
				 */
				if (TRANS_ISTRANS(ip->mp)) {
					truncflag++;
				} else {
					RW_LOCK_OS(&ip->inode_rwl, RW_WRITER);
					error = sam_clear_file(ip, 0,
					    STALE_ARCHIVE, credp);
					RW_UNLOCK_OS(&ip->inode_rwl, RW_WRITER);
				}
				if (error == 0) {
					VNEVENT_CREATE_OS(SAM_ITOV(ip), NULL);
				}
			}
		}
		/*
		 * Cannot do the following as it caused a stale of
		 * offline copies.
		 */
#if	0
		if ((error == 0) && ((mode & O_CREAT) == 0)) {
			TRANS_INODE(ip->mp, ip);
			sam_mark_ino(ip, SAM_UPDATED|SAM_CHANGED);
		}
#endif
		if (error) {
			VN_RELE(SAM_ITOV(ip));	/* Decrement v_count if error */
		}
	} else {
		RW_UNLOCK_OS(&pip->data_rwl, RW_WRITER);
	}
#ifdef LQFS_TODO_LOCKFS
	if (ulp) {
#endif /* LQFS_TODO_LOCKFS */
		TRANS_END_CSYNC(pip->mp, terr, issync, TOP_CREATE, trans_size);
		/*
		 * If we haven't had a more interesting failure
		 * already, then anything that might've happened
		 * here should be reported.
		 */
		if (error == 0) {
			error = terr;
		}
#ifdef LQFS_TODO_LOCKFS
	}
#endif /* LQFS_TODO_LOCKFS */

	if (!error && truncflag) {
		(void) TRANS_ITRUNC(ip, (u_offset_t)0, STALE_ARCHIVE, credp);
	}

#ifdef LQFS_TODO_LOCKFS
	if (ulp) {
		qfs_lockfs_end(ulp);
	}
#endif /* LQFS_TODO_LOCKFS */

	if (error == 0) {
		*vpp = SAM_ITOV(ip);
		TRACE(T_SAM_CREATE_RET, SAM_ITOV(pip), (sam_tr_t)* vpp,
		    ip->di.id.ino, error);
	}
	return (error);
}
Ejemplo n.º 2
0
/*
 * ufs_alloc_data - supports allocating space and reads or writes
 * that involve changes to file length or space allocation.
 *
 * This function is more expensive, because of the UFS log transaction,
 * so ufs_rdwr_data() should be used when space or file length changes
 * will not occur.
 *
 * Inputs:
 * fdb - A null pointer instructs this function to only allocate
 *	space for the specified offset and length.
 *	An actual fdbuffer instructs this function to perform
 *	the read or write operation.
 * flags - defaults (zero value) to synchronous write
 *	B_READ - indicates read operation
 *	B_ASYNC - indicates perform operation asynchronously
 */
int
ufs_alloc_data(
	vnode_t		*vnodep,
	u_offset_t	offset,
	size_t		*len,
	fdbuffer_t	*fdbp,
	int		flags,
	cred_t		*credp)
{
	struct inode	*ip = VTOI(vnodep);
	size_t		done_len, io_len;
	int		contig;
	u_offset_t	uoff, io_off;
	int		error = 0;		/* No error occured */
	int		offsetn;		/* Start point this IO */
	int		nbytes;			/* Number bytes in this IO */
	daddr_t		bn;
	struct fs	*fs;
	struct ufsvfs	*ufsvfsp = ip->i_ufsvfs;
	int		i_size_changed = 0;
	u_offset_t	old_i_size;
	struct ulockfs	*ulp;
	int		trans_size;
	int		issync;			/* UFS Log transaction */
						/* synchronous when non-zero */

	int		io_started = 0;		/* No IO started */
	uint_t		protp = PROT_ALL;

	ASSERT((flags & B_WRITE) == 0);

	/*
	 * Obey the lockfs protocol
	 */
	error = ufs_lockfs_begin_getpage(ufsvfsp, &ulp, segkmap, 0, &protp);
	if (error) {
		if ((fdbp != NULL) && (flags & B_ASYNC)) {
			fdb_ioerrdone(fdbp, error);
		}
		return (error);
	}
	if (ulp) {
		/*
		 * Try to begin a UFS log transaction
		 */
		trans_size = TOP_GETPAGE_SIZE(ip);
		TRANS_TRY_BEGIN_CSYNC(ufsvfsp, issync, TOP_GETPAGE,
		    trans_size, error);
		if (error == EWOULDBLOCK) {
			ufs_lockfs_end(ulp);
			if ((fdbp != NULL) && (flags & B_ASYNC)) {
				fdb_ioerrdone(fdbp, EDEADLK);
			}
			return (EDEADLK);
		}
	}

	uoff = offset;
	io_off = offset;
	io_len = *len;
	done_len = 0;

	DEBUGF((CE_CONT, "?ufs_alloc: off %llx len %lx size %llx fdb: %p\n",
	    uoff, (io_len - done_len), ip->i_size, (void *)fdbp));

	rw_enter(&ip->i_ufsvfs->vfs_dqrwlock, RW_READER);
	rw_enter(&ip->i_contents, RW_WRITER);

	ASSERT((ip->i_mode & IFMT) == IFREG);

	fs = ip->i_fs;

	while (error == 0 && done_len < io_len) {
		uoff = (u_offset_t)(io_off + done_len);
		offsetn = (int)blkoff(fs, uoff);
		nbytes = (int)MIN(fs->fs_bsize - offsetn, io_len - done_len);

		DEBUGF((CE_CONT, "?ufs_alloc_data: offset: %llx len %x\n",
		    uoff, nbytes));

		if (uoff + nbytes > ip->i_size) {
			/*
			 * We are extending the length of the file.
			 * bmap is used so that we are sure that
			 * if we need to allocate new blocks, that it
			 * is done here before we up the file size.
			 */
			DEBUGF((CE_CONT, "?ufs_alloc_data: grow %llx -> %llx\n",
			    ip->i_size, uoff + nbytes));

			error = bmap_write(ip, uoff, (offsetn + nbytes),
			    BI_ALLOC_ONLY, NULL, credp);
			if (ip->i_flag & (ICHG|IUPD))
				ip->i_seq++;
			if (error) {
				DEBUGF((CE_CONT, "?ufs_alloc_data: grow "
				    "failed err: %d\n", error));
				break;
			}
			if (fdbp != NULL) {
				if (uoff >= ip->i_size) {
					/*
					 * Desired offset is past end of bytes
					 * in file, so we have a hole.
					 */
					fdb_add_hole(fdbp, uoff - offset,
					    nbytes);
				} else {
					int contig;
					buf_t *bp;

					error = bmap_read(ip, uoff, &bn,
					    &contig);
					if (error) {
						break;
					}

					contig = ip->i_size - uoff;
					contig = P2ROUNDUP(contig, DEV_BSIZE);

					bp = fdb_iosetup(fdbp, uoff - offset,
					    contig, vnodep, flags);

					bp->b_edev = ip->i_dev;
					bp->b_dev = cmpdev(ip->i_dev);
					bp->b_blkno = bn;
					bp->b_file = ip->i_vnode;
					bp->b_offset = (offset_t)uoff;

					if (ufsvfsp->vfs_snapshot) {
						fssnap_strategy(
						    &ufsvfsp->vfs_snapshot, bp);
					} else {
						(void) bdev_strategy(bp);
					}
					io_started = 1;

					lwp_stat_update(LWP_STAT_OUBLK, 1);

					if ((flags & B_ASYNC) == 0) {
						error = biowait(bp);
						fdb_iodone(bp);
						if (error) {
							break;
						}
					}
					if (contig > (ip->i_size - uoff)) {
						contig -= ip->i_size - uoff;

						fdb_add_hole(fdbp,
						    ip->i_size - offset,
						    contig);
					}
				}
			}

			i_size_changed = 1;
			old_i_size = ip->i_size;
			UFS_SET_ISIZE(uoff + nbytes, ip);
			TRANS_INODE(ip->i_ufsvfs, ip);
			/*
			 * file has grown larger than 2GB. Set flag
			 * in superblock to indicate this, if it
			 * is not already set.
			 */
			if ((ip->i_size > MAXOFF32_T) &&
			    !(fs->fs_flags & FSLARGEFILES)) {
				ASSERT(ufsvfsp->vfs_lfflags & UFS_LARGEFILES);
				mutex_enter(&ufsvfsp->vfs_lock);
				fs->fs_flags |= FSLARGEFILES;
				ufs_sbwrite(ufsvfsp);
				mutex_exit(&ufsvfsp->vfs_lock);
			}
		} else {
			/*
			 * The file length is not being extended.
			 */
			error = bmap_read(ip, uoff, &bn, &contig);
			if (error) {
				DEBUGF((CE_CONT, "?ufs_alloc_data: "
				    "bmap_read err: %d\n", error));
				break;
			}

			if (bn != UFS_HOLE) {
				/*
				 * Did not map a hole in the file
				 */
				int	contig = P2ROUNDUP(nbytes, DEV_BSIZE);
				buf_t	*bp;

				if (fdbp != NULL) {
					bp = fdb_iosetup(fdbp, uoff - offset,
					    contig, vnodep, flags);

					bp->b_edev = ip->i_dev;
					bp->b_dev = cmpdev(ip->i_dev);
					bp->b_blkno = bn;
					bp->b_file = ip->i_vnode;
					bp->b_offset = (offset_t)uoff;

					if (ufsvfsp->vfs_snapshot) {
						fssnap_strategy(
						    &ufsvfsp->vfs_snapshot, bp);
					} else {
						(void) bdev_strategy(bp);
					}
					io_started = 1;

					lwp_stat_update(LWP_STAT_OUBLK, 1);

					if ((flags & B_ASYNC) == 0) {
						error = biowait(bp);
						fdb_iodone(bp);
						if (error) {
							break;
						}
					}
				}
			} else {
				/*
				 * We read a hole in the file.
				 * We have to allocate blocks for the hole.
				 */
				error = bmap_write(ip, uoff, (offsetn + nbytes),
				    BI_ALLOC_ONLY, NULL, credp);
				if (ip->i_flag & (ICHG|IUPD))
					ip->i_seq++;
				if (error) {
					DEBUGF((CE_CONT, "?ufs_alloc_data: fill"
					    " hole failed error: %d\n", error));
					break;
				}
				if (fdbp != NULL) {
					fdb_add_hole(fdbp, uoff - offset,
					    nbytes);
				}
			}
		}
		done_len += nbytes;
	}

	if (error) {
		if (i_size_changed) {
			/*
			 * Allocation of the blocks for the file failed.
			 * So truncate the file size back to its original size.
			 */
			(void) ufs_itrunc(ip, old_i_size, 0, credp);
		}
	}

	DEBUGF((CE_CONT, "?ufs_alloc: uoff %llx len %lx\n",
	    uoff, (io_len - done_len)));

	if ((offset + *len) < (NDADDR * fs->fs_bsize)) {
		*len = (size_t)(roundup(offset + *len, fs->fs_fsize) - offset);
	} else {
		*len = (size_t)(roundup(offset + *len, fs->fs_bsize) - offset);
	}

	/*
	 * Flush cached pages.
	 *
	 * XXX - There should be no pages involved, since the I/O was performed
	 * through the device strategy routine and the page cache was bypassed.
	 * However, testing has demonstrated that this VOP_PUTPAGE is
	 * necessary. Without this, data might not always be read back as it
	 * was written.
	 *
	 */
	(void) VOP_PUTPAGE(vnodep, 0, 0, B_INVAL, credp);

	rw_exit(&ip->i_contents);
	rw_exit(&ip->i_ufsvfs->vfs_dqrwlock);

	if ((fdbp != NULL) && (flags & B_ASYNC)) {
		/*
		 * Show that no more asynchronous IO will be added
		 */
		fdb_ioerrdone(fdbp, error);
	}
	if (ulp) {
		/*
		 * End the UFS Log transaction
		 */
		TRANS_END_CSYNC(ufsvfsp, error, issync, TOP_GETPAGE,
		    trans_size);
		ufs_lockfs_end(ulp);
	}
	if (io_started && (flags & B_ASYNC)) {
		return (0);
	} else {
		return (error);
	}
}