STATIC loff_t xfs_seek_hole( struct file *file, loff_t start, u32 type) { struct inode *inode = file->f_mapping->host; struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; loff_t uninitialized_var(offset); loff_t holeoff; xfs_fsize_t isize; xfs_fileoff_t fsbno; uint lock; int error; if (XFS_FORCED_SHUTDOWN(mp)) return -XFS_ERROR(EIO); lock = xfs_ilock_map_shared(ip); isize = i_size_read(inode); if (start >= isize) { error = ENXIO; goto out_unlock; } fsbno = XFS_B_TO_FSBT(mp, start); error = xfs_bmap_first_unused(NULL, ip, 1, &fsbno, XFS_DATA_FORK); if (error) goto out_unlock; holeoff = XFS_FSB_TO_B(mp, fsbno); if (holeoff <= start) offset = start; else { /* * xfs_bmap_first_unused() could return a value bigger than * isize if there are no more holes past the supplied offset. */ offset = min_t(loff_t, holeoff, isize); } if (offset != file->f_pos) file->f_pos = offset; out_unlock: xfs_iunlock_map_shared(ip, lock); if (error) return -error; return offset; }
/* * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to * a file or fs handle. * * XFS_IOC_PATH_TO_FSHANDLE * returns fs handle for a mount point or path within that mount point * XFS_IOC_FD_TO_HANDLE * returns full handle for a FD opened in user space * XFS_IOC_PATH_TO_HANDLE * returns full handle for a path */ int xfs_find_handle( unsigned int cmd, xfs_fsop_handlereq_t *hreq) { int hsize; xfs_handle_t handle; struct inode *inode; struct fd f = {0}; struct path path; int error; struct xfs_inode *ip; if (cmd == XFS_IOC_FD_TO_HANDLE) { f = fdget(hreq->fd); if (!f.file) return -EBADF; inode = file_inode(f.file); } else { error = user_lpath((const char __user *)hreq->path, &path); if (error) return error; inode = path.dentry->d_inode; } ip = XFS_I(inode); /* * We can only generate handles for inodes residing on a XFS filesystem, * and only for regular files, directories or symbolic links. */ error = -EINVAL; if (inode->i_sb->s_magic != XFS_SB_MAGIC) goto out_put; error = -EBADF; if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode) && !S_ISLNK(inode->i_mode)) goto out_put; memcpy(&handle.ha_fsid, ip->i_mount->m_fixedfsid, sizeof(xfs_fsid_t)); if (cmd == XFS_IOC_PATH_TO_FSHANDLE) { /* * This handle only contains an fsid, zero the rest. */ memset(&handle.ha_fid, 0, sizeof(handle.ha_fid)); hsize = sizeof(xfs_fsid_t); } else { int lock_mode; lock_mode = xfs_ilock_map_shared(ip); handle.ha_fid.fid_len = sizeof(xfs_fid_t) - sizeof(handle.ha_fid.fid_len); handle.ha_fid.fid_pad = 0; handle.ha_fid.fid_gen = ip->i_d.di_gen; handle.ha_fid.fid_ino = ip->i_ino; xfs_iunlock_map_shared(ip, lock_mode); hsize = XFS_HSIZE(handle); } error = -EFAULT; if (copy_to_user(hreq->ohandle, &handle, hsize) || copy_to_user(hreq->ohandlen, &hsize, sizeof(__s32))) goto out_put; error = 0; out_put: if (cmd == XFS_IOC_FD_TO_HANDLE) fdput(f); else path_put(&path); return error; }
/* * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to * a file or fs handle. * * XFS_IOC_PATH_TO_FSHANDLE * returns fs handle for a mount point or path within that mount point * XFS_IOC_FD_TO_HANDLE * returns full handle for a FD opened in user space * XFS_IOC_PATH_TO_HANDLE * returns full handle for a path */ STATIC int xfs_find_handle( unsigned int cmd, unsigned long arg) { int hsize; xfs_handle_t handle; xfs_fsop_handlereq_t hreq; struct inode *inode; struct vnode *vp; if (copy_from_user(&hreq, (xfs_fsop_handlereq_t *)arg, sizeof(hreq))) return -XFS_ERROR(EFAULT); memset((char *)&handle, 0, sizeof(handle)); switch (cmd) { case XFS_IOC_PATH_TO_FSHANDLE: case XFS_IOC_PATH_TO_HANDLE: { struct nameidata nd; int error; error = user_path_walk_link(hreq.path, &nd); if (error) return error; ASSERT(nd.dentry); ASSERT(nd.dentry->d_inode); inode = igrab(nd.dentry->d_inode); path_release(&nd); break; } case XFS_IOC_FD_TO_HANDLE: { struct file *file; file = fget(hreq.fd); if (!file) return -EBADF; ASSERT(file->f_dentry); ASSERT(file->f_dentry->d_inode); inode = igrab(file->f_dentry->d_inode); fput(file); break; } default: ASSERT(0); return -XFS_ERROR(EINVAL); } if (inode->i_sb->s_magic != XFS_SB_MAGIC) { /* we're not in XFS anymore, Toto */ iput(inode); return -XFS_ERROR(EINVAL); } /* we need the vnode */ vp = LINVFS_GET_VP(inode); if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) { iput(inode); return -XFS_ERROR(EBADF); } /* now we can grab the fsid */ memcpy(&handle.ha_fsid, vp->v_vfsp->vfs_altfsid, sizeof(xfs_fsid_t)); hsize = sizeof(xfs_fsid_t); if (cmd != XFS_IOC_PATH_TO_FSHANDLE) { xfs_inode_t *ip; bhv_desc_t *bhv; int lock_mode; /* need to get access to the xfs_inode to read the generation */ bhv = vn_bhv_lookup_unlocked(VN_BHV_HEAD(vp), &xfs_vnodeops); ASSERT(bhv); ip = XFS_BHVTOI(bhv); ASSERT(ip); lock_mode = xfs_ilock_map_shared(ip); /* fill in fid section of handle from inode */ handle.ha_fid.xfs_fid_len = sizeof(xfs_fid_t) - sizeof(handle.ha_fid.xfs_fid_len); handle.ha_fid.xfs_fid_pad = 0; handle.ha_fid.xfs_fid_gen = ip->i_d.di_gen; handle.ha_fid.xfs_fid_ino = ip->i_ino; xfs_iunlock_map_shared(ip, lock_mode); hsize = XFS_HSIZE(handle); } /* now copy our handle into the user buffer & write out the size */ if (copy_to_user((xfs_handle_t *)hreq.ohandle, &handle, hsize) || copy_to_user(hreq.ohandlen, &hsize, sizeof(__s32))) { iput(inode); return -XFS_ERROR(EFAULT); } iput(inode); return 0; }
STATIC loff_t xfs_seek_data( struct file *file, loff_t start, u32 type) { struct inode *inode = file->f_mapping->host; struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; struct xfs_bmbt_irec map[2]; int nmap = 2; loff_t uninitialized_var(offset); xfs_fsize_t isize; xfs_fileoff_t fsbno; xfs_filblks_t end; uint lock; int error; lock = xfs_ilock_map_shared(ip); isize = i_size_read(inode); if (start >= isize) { error = ENXIO; goto out_unlock; } fsbno = XFS_B_TO_FSBT(mp, start); /* * Try to read extents from the first block indicated * by fsbno to the end block of the file. */ end = XFS_B_TO_FSB(mp, isize); error = xfs_bmapi_read(ip, fsbno, end - fsbno, map, &nmap, XFS_BMAPI_ENTIRE); if (error) goto out_unlock; /* * Treat unwritten extent as data extent since it might * contains dirty data in page cache. */ if (map[0].br_startblock != HOLESTARTBLOCK) { offset = max_t(loff_t, start, XFS_FSB_TO_B(mp, map[0].br_startoff)); } else { if (nmap == 1) { error = ENXIO; goto out_unlock; } offset = max_t(loff_t, start, XFS_FSB_TO_B(mp, map[1].br_startoff)); } if (offset != file->f_pos) file->f_pos = offset; out_unlock: xfs_iunlock_map_shared(ip, lock); if (error) return -error; return offset; }
/* * The following routine will acquire the locks required for a rename * operation. The code understands the semantics of renames and will * validate that name1 exists under dp1 & that name2 may or may not * exist under dp2. * * We are renaming dp1/name1 to dp2/name2. * * Return ENOENT if dp1 does not exist, other lookup errors, or 0 for success. */ STATIC int xfs_lock_for_rename( xfs_inode_t *dp1, /* old (source) directory inode */ xfs_inode_t *dp2, /* new (target) directory inode */ bhv_vname_t *vname1,/* old entry name */ bhv_vname_t *vname2,/* new entry name */ xfs_inode_t **ipp1, /* inode of old entry */ xfs_inode_t **ipp2, /* inode of new entry, if it already exists, NULL otherwise. */ xfs_inode_t **i_tab,/* array of inode returned, sorted */ int *num_inodes) /* number of inodes in array */ { xfs_inode_t *ip1, *ip2, *temp; xfs_ino_t inum1, inum2; int error; int i, j; uint lock_mode; int diff_dirs = (dp1 != dp2); ip2 = NULL; /* * First, find out the current inums of the entries so that we * can determine the initial locking order. We'll have to * sanity check stuff after all the locks have been acquired * to see if we still have the right inodes, directories, etc. */ lock_mode = xfs_ilock_map_shared(dp1); error = xfs_get_dir_entry(vname1, &ip1); if (error) { xfs_iunlock_map_shared(dp1, lock_mode); return error; } inum1 = ip1->i_ino; ASSERT(ip1); ITRACE(ip1); /* * Unlock dp1 and lock dp2 if they are different. */ if (diff_dirs) { xfs_iunlock_map_shared(dp1, lock_mode); lock_mode = xfs_ilock_map_shared(dp2); } error = xfs_dir_lookup_int(XFS_ITOBHV(dp2), lock_mode, vname2, &inum2, &ip2); if (error == ENOENT) { /* target does not need to exist. */ inum2 = 0; } else if (error) { /* * If dp2 and dp1 are the same, the next line unlocks dp1. * Got it? */ xfs_iunlock_map_shared(dp2, lock_mode); IRELE (ip1); return error; } else { ITRACE(ip2); } /* * i_tab contains a list of pointers to inodes. We initialize * the table here & we'll sort it. We will then use it to * order the acquisition of the inode locks. * * Note that the table may contain duplicates. e.g., dp1 == dp2. */ i_tab[0] = dp1; i_tab[1] = dp2; i_tab[2] = ip1; if (inum2 == 0) { *num_inodes = 3; i_tab[3] = NULL; } else { *num_inodes = 4; i_tab[3] = ip2; } /* * Sort the elements via bubble sort. (Remember, there are at * most 4 elements to sort, so this is adequate.) */ for (i=0; i < *num_inodes; i++) { for (j=1; j < *num_inodes; j++) { if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) { temp = i_tab[j]; i_tab[j] = i_tab[j-1]; i_tab[j-1] = temp; } } } /* * We have dp2 locked. If it isn't first, unlock it. * If it is first, tell xfs_lock_inodes so it can skip it * when locking. if dp1 == dp2, xfs_lock_inodes will skip both * since they are equal. xfs_lock_inodes needs all these inodes * so that it can unlock and retry if there might be a dead-lock * potential with the log. */ if (i_tab[0] == dp2 && lock_mode == XFS_ILOCK_SHARED) { #ifdef DEBUG xfs_rename_skip++; #endif xfs_lock_inodes(i_tab, *num_inodes, 1, XFS_ILOCK_SHARED); } else { #ifdef DEBUG xfs_rename_nskip++; #endif xfs_iunlock_map_shared(dp2, lock_mode); xfs_lock_inodes(i_tab, *num_inodes, 0, XFS_ILOCK_SHARED); } /* * Set the return value. Null out any unused entries in i_tab. */ *ipp1 = *ipp2 = NULL; for (i=0; i < *num_inodes; i++) { if (i_tab[i]->i_ino == inum1) { *ipp1 = i_tab[i]; } if (i_tab[i]->i_ino == inum2) { *ipp2 = i_tab[i]; } } for (;i < 4; i++) { i_tab[i] = NULL; } return 0; }
/* * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to * a file or fs handle. * * XFS_IOC_PATH_TO_FSHANDLE * returns fs handle for a mount point or path within that mount point * XFS_IOC_FD_TO_HANDLE * returns full handle for a FD opened in user space * XFS_IOC_PATH_TO_HANDLE * returns full handle for a path */ STATIC int xfs_find_handle( unsigned int cmd, void __user *arg) { int hsize; xfs_handle_t handle; xfs_fsop_handlereq_t hreq; struct inode *inode; if (copy_from_user(&hreq, arg, sizeof(hreq))) return -XFS_ERROR(EFAULT); memset((char *)&handle, 0, sizeof(handle)); switch (cmd) { case XFS_IOC_PATH_TO_FSHANDLE: case XFS_IOC_PATH_TO_HANDLE: { struct nameidata nd; int error; error = user_path_walk_link((const char __user *)hreq.path, &nd); if (error) return error; ASSERT(nd.path.dentry); ASSERT(nd.path.dentry->d_inode); inode = igrab(nd.path.dentry->d_inode); path_put(&nd.path); break; } case XFS_IOC_FD_TO_HANDLE: { struct file *file; file = fget(hreq.fd); if (!file) return -EBADF; ASSERT(file->f_path.dentry); ASSERT(file->f_path.dentry->d_inode); inode = igrab(file->f_path.dentry->d_inode); fput(file); break; } default: ASSERT(0); return -XFS_ERROR(EINVAL); } if (inode->i_sb->s_magic != XFS_SB_MAGIC) { /* we're not in XFS anymore, Toto */ iput(inode); return -XFS_ERROR(EINVAL); } switch (inode->i_mode & S_IFMT) { case S_IFREG: case S_IFDIR: case S_IFLNK: break; default: iput(inode); return -XFS_ERROR(EBADF); } /* now we can grab the fsid */ memcpy(&handle.ha_fsid, XFS_I(inode)->i_mount->m_fixedfsid, sizeof(xfs_fsid_t)); hsize = sizeof(xfs_fsid_t); if (cmd != XFS_IOC_PATH_TO_FSHANDLE) { xfs_inode_t *ip = XFS_I(inode); int lock_mode; /* need to get access to the xfs_inode to read the generation */ lock_mode = xfs_ilock_map_shared(ip); /* fill in fid section of handle from inode */ handle.ha_fid.fid_len = sizeof(xfs_fid_t) - sizeof(handle.ha_fid.fid_len); handle.ha_fid.fid_pad = 0; handle.ha_fid.fid_gen = ip->i_d.di_gen; handle.ha_fid.fid_ino = ip->i_ino; xfs_iunlock_map_shared(ip, lock_mode); hsize = XFS_HSIZE(handle); } /* now copy our handle into the user buffer & write out the size */ if (copy_to_user(hreq.ohandle, &handle, hsize) || copy_to_user(hreq.ohandlen, &hsize, sizeof(__s32))) { iput(inode); return -XFS_ERROR(EFAULT); } iput(inode); return 0; }
STATIC loff_t xfs_seek_hole( struct file *file, loff_t start) { struct inode *inode = file->f_mapping->host; struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; loff_t uninitialized_var(offset); xfs_fsize_t isize; xfs_fileoff_t fsbno; xfs_filblks_t end; uint lock; int error; if (XFS_FORCED_SHUTDOWN(mp)) return -XFS_ERROR(EIO); lock = xfs_ilock_map_shared(ip); isize = i_size_read(inode); if (start >= isize) { error = ENXIO; goto out_unlock; } fsbno = XFS_B_TO_FSBT(mp, start); end = XFS_B_TO_FSB(mp, isize); for (;;) { struct xfs_bmbt_irec map[2]; int nmap = 2; unsigned int i; error = xfs_bmapi_read(ip, fsbno, end - fsbno, map, &nmap, XFS_BMAPI_ENTIRE); if (error) goto out_unlock; /* No extents at given offset, must be beyond EOF */ if (nmap == 0) { error = ENXIO; goto out_unlock; } for (i = 0; i < nmap; i++) { offset = max_t(loff_t, start, XFS_FSB_TO_B(mp, map[i].br_startoff)); /* Landed in a hole */ if (map[i].br_startblock == HOLESTARTBLOCK) goto out; /* * Landed in an unwritten extent, try to search hole * from page cache. */ if (map[i].br_state == XFS_EXT_UNWRITTEN) { if (xfs_find_get_desired_pgoff(inode, &map[i], HOLE_OFF, &offset)) goto out; } } /* * map[0] contains data or its unwritten but contains * data in page cache, probably means that we are * reading after EOF. We should fix offset to point * to the end of the file(i.e., there is an implicit * hole at the end of any file). */ if (nmap == 1) { offset = isize; break; } ASSERT(i > 1); /* * Both mappings contains data, proceed to the next round of * search if the current reading offset not beyond or hit EOF. */ fsbno = map[i - 1].br_startoff + map[i - 1].br_blockcount; start = XFS_FSB_TO_B(mp, fsbno); if (start >= isize) { offset = isize; break; } } out: /* * At this point, we must have found a hole. However, the returned * offset may be bigger than the file size as it may be aligned to * page boundary for unwritten extents, we need to deal with this * situation in particular. */ offset = min_t(loff_t, offset, isize); offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes); out_unlock: xfs_iunlock_map_shared(ip, lock); if (error) return -error; return offset; }
STATIC loff_t xfs_seek_data( struct file *file, loff_t start) { struct inode *inode = file->f_mapping->host; struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; loff_t uninitialized_var(offset); xfs_fsize_t isize; xfs_fileoff_t fsbno; xfs_filblks_t end; uint lock; int error; lock = xfs_ilock_map_shared(ip); isize = i_size_read(inode); if (start >= isize) { error = ENXIO; goto out_unlock; } /* * Try to read extents from the first block indicated * by fsbno to the end block of the file. */ fsbno = XFS_B_TO_FSBT(mp, start); end = XFS_B_TO_FSB(mp, isize); for (;;) { struct xfs_bmbt_irec map[2]; int nmap = 2; unsigned int i; error = xfs_bmapi_read(ip, fsbno, end - fsbno, map, &nmap, XFS_BMAPI_ENTIRE); if (error) goto out_unlock; /* No extents at given offset, must be beyond EOF */ if (nmap == 0) { error = ENXIO; goto out_unlock; } for (i = 0; i < nmap; i++) { offset = max_t(loff_t, start, XFS_FSB_TO_B(mp, map[i].br_startoff)); /* Landed in a data extent */ if (map[i].br_startblock == DELAYSTARTBLOCK || (map[i].br_state == XFS_EXT_NORM && !isnullstartblock(map[i].br_startblock))) goto out; /* * Landed in an unwritten extent, try to search data * from page cache. */ if (map[i].br_state == XFS_EXT_UNWRITTEN) { if (xfs_find_get_desired_pgoff(inode, &map[i], DATA_OFF, &offset)) goto out; } } /* * map[0] is hole or its an unwritten extent but * without data in page cache. Probably means that * we are reading after EOF if nothing in map[1]. */ if (nmap == 1) { error = ENXIO; goto out_unlock; } ASSERT(i > 1); /* * Nothing was found, proceed to the next round of search * if reading offset not beyond or hit EOF. */ fsbno = map[i - 1].br_startoff + map[i - 1].br_blockcount; start = XFS_FSB_TO_B(mp, fsbno); if (start >= isize) { error = ENXIO; goto out_unlock; } } out: offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes); out_unlock: xfs_iunlock_map_shared(ip, lock); if (error) return -error; return offset; }