aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/linux-2.6/xfs_ioctl.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@infradead.org>2008-07-18 03:13:20 -0400
committerNiv Sardi <xaiki@debian.org>2008-07-28 02:59:36 -0400
commit25fe55e814a2964c7e16d16a5d08cae6e9313a3a (patch)
tree9337a4270cafe83a0bfd14e1c297aa5f24e02c33 /fs/xfs/linux-2.6/xfs_ioctl.c
parentc032bfcf468013643e05c8274824af10dd7cbb61 (diff)
[XFS] xfs_setattr currently doesn't just handle the attributes set through
->setattr but also addition XFS-specific attributes: project id, inode flags and extent size hint. Having these in a single function makes it more complicated and forces to have us a bhv_vattr intermediate structure eating up stackspace. This patch adds a new xfs_ioctl_setattr helper for the XFS ioctls that set these attributes and remove the code to set them through xfs_setattr. SGI-PV: 984564 SGI-Modid: xfs-linux-melb:xfs-kern:31677a Signed-off-by: Christoph Hellwig <hch@infradead.org> Signed-off-by: Tim Shimmin <tes@sgi.com> Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
Diffstat (limited to 'fs/xfs/linux-2.6/xfs_ioctl.c')
-rw-r--r--fs/xfs/linux-2.6/xfs_ioctl.c339
1 files changed, 303 insertions, 36 deletions
diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c
index 8eddaff36843..d2bbb0532ef6 100644
--- a/fs/xfs/linux-2.6/xfs_ioctl.c
+++ b/fs/xfs/linux-2.6/xfs_ioctl.c
@@ -48,6 +48,8 @@
48#include "xfs_dfrag.h" 48#include "xfs_dfrag.h"
49#include "xfs_fsops.h" 49#include "xfs_fsops.h"
50#include "xfs_vnodeops.h" 50#include "xfs_vnodeops.h"
51#include "xfs_quota.h"
52#include "xfs_inode_item.h"
51 53
52#include <linux/capability.h> 54#include <linux/capability.h>
53#include <linux/dcache.h> 55#include <linux/dcache.h>
@@ -879,6 +881,297 @@ xfs_ioc_fsgetxattr(
879 return 0; 881 return 0;
880} 882}
881 883
884STATIC void
885xfs_set_diflags(
886 struct xfs_inode *ip,
887 unsigned int xflags)
888{
889 unsigned int di_flags;
890
891 /* can't set PREALLOC this way, just preserve it */
892 di_flags = (ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);
893 if (xflags & XFS_XFLAG_IMMUTABLE)
894 di_flags |= XFS_DIFLAG_IMMUTABLE;
895 if (xflags & XFS_XFLAG_APPEND)
896 di_flags |= XFS_DIFLAG_APPEND;
897 if (xflags & XFS_XFLAG_SYNC)
898 di_flags |= XFS_DIFLAG_SYNC;
899 if (xflags & XFS_XFLAG_NOATIME)
900 di_flags |= XFS_DIFLAG_NOATIME;
901 if (xflags & XFS_XFLAG_NODUMP)
902 di_flags |= XFS_DIFLAG_NODUMP;
903 if (xflags & XFS_XFLAG_PROJINHERIT)
904 di_flags |= XFS_DIFLAG_PROJINHERIT;
905 if (xflags & XFS_XFLAG_NODEFRAG)
906 di_flags |= XFS_DIFLAG_NODEFRAG;
907 if (xflags & XFS_XFLAG_FILESTREAM)
908 di_flags |= XFS_DIFLAG_FILESTREAM;
909 if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
910 if (xflags & XFS_XFLAG_RTINHERIT)
911 di_flags |= XFS_DIFLAG_RTINHERIT;
912 if (xflags & XFS_XFLAG_NOSYMLINKS)
913 di_flags |= XFS_DIFLAG_NOSYMLINKS;
914 if (xflags & XFS_XFLAG_EXTSZINHERIT)
915 di_flags |= XFS_DIFLAG_EXTSZINHERIT;
916 } else if ((ip->i_d.di_mode & S_IFMT) == S_IFREG) {
917 if (xflags & XFS_XFLAG_REALTIME)
918 di_flags |= XFS_DIFLAG_REALTIME;
919 if (xflags & XFS_XFLAG_EXTSIZE)
920 di_flags |= XFS_DIFLAG_EXTSIZE;
921 }
922
923 ip->i_d.di_flags = di_flags;
924}
925
926
927#define FSX_PROJID 1
928#define FSX_EXTSIZE 2
929#define FSX_XFLAGS 4
930#define FSX_NONBLOCK 8
931
932STATIC int
933xfs_ioctl_setattr(
934 xfs_inode_t *ip,
935 struct fsxattr *fa,
936 int mask)
937{
938 struct xfs_mount *mp = ip->i_mount;
939 struct xfs_trans *tp;
940 unsigned int lock_flags = 0;
941 struct xfs_dquot *udqp = NULL, *gdqp = NULL;
942 struct xfs_dquot *olddquot = NULL;
943 int code;
944
945 xfs_itrace_entry(ip);
946
947 if (mp->m_flags & XFS_MOUNT_RDONLY)
948 return XFS_ERROR(EROFS);
949 if (XFS_FORCED_SHUTDOWN(mp))
950 return XFS_ERROR(EIO);
951
952 /*
953 * If disk quotas is on, we make sure that the dquots do exist on disk,
954 * before we start any other transactions. Trying to do this later
955 * is messy. We don't care to take a readlock to look at the ids
956 * in inode here, because we can't hold it across the trans_reserve.
957 * If the IDs do change before we take the ilock, we're covered
958 * because the i_*dquot fields will get updated anyway.
959 */
960 if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) {
961 code = XFS_QM_DQVOPALLOC(mp, ip, ip->i_d.di_uid,
962 ip->i_d.di_gid, fa->fsx_projid,
963 XFS_QMOPT_PQUOTA, &udqp, &gdqp);
964 if (code)
965 return code;
966 }
967
968 /*
969 * For the other attributes, we acquire the inode lock and
970 * first do an error checking pass.
971 */
972 tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
973 code = xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0);
974 if (code)
975 goto error_return;
976
977 lock_flags = XFS_ILOCK_EXCL;
978 xfs_ilock(ip, lock_flags);
979
980 /*
981 * CAP_FOWNER overrides the following restrictions:
982 *
983 * The user ID of the calling process must be equal
984 * to the file owner ID, except in cases where the
985 * CAP_FSETID capability is applicable.
986 */
987 if (current->fsuid != ip->i_d.di_uid && !capable(CAP_FOWNER)) {
988 code = XFS_ERROR(EPERM);
989 goto error_return;
990 }
991
992 /*
993 * Do a quota reservation only if projid is actually going to change.
994 */
995 if (mask & FSX_PROJID) {
996 if (XFS_IS_PQUOTA_ON(mp) &&
997 ip->i_d.di_projid != fa->fsx_projid) {
998 ASSERT(tp);
999 code = XFS_QM_DQVOPCHOWNRESV(mp, tp, ip, udqp, gdqp,
1000 capable(CAP_FOWNER) ?
1001 XFS_QMOPT_FORCE_RES : 0);
1002 if (code) /* out of quota */
1003 goto error_return;
1004 }
1005 }
1006
1007 if (mask & FSX_EXTSIZE) {
1008 /*
1009 * Can't change extent size if any extents are allocated.
1010 */
1011 if (ip->i_d.di_nextents &&
1012 ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) !=
1013 fa->fsx_extsize)) {
1014 code = XFS_ERROR(EINVAL); /* EFBIG? */
1015 goto error_return;
1016 }
1017
1018 /*
1019 * Extent size must be a multiple of the appropriate block
1020 * size, if set at all.
1021 */
1022 if (fa->fsx_extsize != 0) {
1023 xfs_extlen_t size;
1024
1025 if (XFS_IS_REALTIME_INODE(ip) ||
1026 ((mask & FSX_XFLAGS) &&
1027 (fa->fsx_xflags & XFS_XFLAG_REALTIME))) {
1028 size = mp->m_sb.sb_rextsize <<
1029 mp->m_sb.sb_blocklog;
1030 } else {
1031 size = mp->m_sb.sb_blocksize;
1032 }
1033
1034 if (fa->fsx_extsize % size) {
1035 code = XFS_ERROR(EINVAL);
1036 goto error_return;
1037 }
1038 }
1039 }
1040
1041
1042 if (mask & FSX_XFLAGS) {
1043 /*
1044 * Can't change realtime flag if any extents are allocated.
1045 */
1046 if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
1047 (XFS_IS_REALTIME_INODE(ip)) !=
1048 (fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
1049 code = XFS_ERROR(EINVAL); /* EFBIG? */
1050 goto error_return;
1051 }
1052
1053 /*
1054 * If realtime flag is set then must have realtime data.
1055 */
1056 if ((fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
1057 if ((mp->m_sb.sb_rblocks == 0) ||
1058 (mp->m_sb.sb_rextsize == 0) ||
1059 (ip->i_d.di_extsize % mp->m_sb.sb_rextsize)) {
1060 code = XFS_ERROR(EINVAL);
1061 goto error_return;
1062 }
1063 }
1064
1065 /*
1066 * Can't modify an immutable/append-only file unless
1067 * we have appropriate permission.
1068 */
1069 if ((ip->i_d.di_flags &
1070 (XFS_DIFLAG_IMMUTABLE|XFS_DIFLAG_APPEND) ||
1071 (fa->fsx_xflags &
1072 (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) &&
1073 !capable(CAP_LINUX_IMMUTABLE)) {
1074 code = XFS_ERROR(EPERM);
1075 goto error_return;
1076 }
1077 }
1078
1079 xfs_trans_ijoin(tp, ip, lock_flags);
1080 xfs_trans_ihold(tp, ip);
1081
1082 /*
1083 * Change file ownership. Must be the owner or privileged.
1084 * If the system was configured with the "restricted_chown"
1085 * option, the owner is not permitted to give away the file,
1086 * and can change the group id only to a group of which he
1087 * or she is a member.
1088 */
1089 if (mask & FSX_PROJID) {
1090 /*
1091 * CAP_FSETID overrides the following restrictions:
1092 *
1093 * The set-user-ID and set-group-ID bits of a file will be
1094 * cleared upon successful return from chown()
1095 */
1096 if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
1097 !capable(CAP_FSETID))
1098 ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
1099
1100 /*
1101 * Change the ownerships and register quota modifications
1102 * in the transaction.
1103 */
1104 if (ip->i_d.di_projid != fa->fsx_projid) {
1105 if (XFS_IS_PQUOTA_ON(mp)) {
1106 olddquot = XFS_QM_DQVOPCHOWN(mp, tp, ip,
1107 &ip->i_gdquot, gdqp);
1108 }
1109 ip->i_d.di_projid = fa->fsx_projid;
1110
1111 /*
1112 * We may have to rev the inode as well as
1113 * the superblock version number since projids didn't
1114 * exist before DINODE_VERSION_2 and SB_VERSION_NLINK.
1115 */
1116 if (ip->i_d.di_version == XFS_DINODE_VERSION_1)
1117 xfs_bump_ino_vers2(tp, ip);
1118 }
1119
1120 }
1121
1122 if (mask & FSX_EXTSIZE)
1123 ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
1124 if (mask & FSX_XFLAGS)
1125 xfs_set_diflags(ip, fa->fsx_xflags);
1126
1127 xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
1128 xfs_ichgtime(ip, XFS_ICHGTIME_CHG);
1129
1130 XFS_STATS_INC(xs_ig_attrchg);
1131
1132 /*
1133 * If this is a synchronous mount, make sure that the
1134 * transaction goes to disk before returning to the user.
1135 * This is slightly sub-optimal in that truncates require
1136 * two sync transactions instead of one for wsync filesystems.
1137 * One for the truncate and one for the timestamps since we
1138 * don't want to change the timestamps unless we're sure the
1139 * truncate worked. Truncates are less than 1% of the laddis
1140 * mix so this probably isn't worth the trouble to optimize.
1141 */
1142 if (mp->m_flags & XFS_MOUNT_WSYNC)
1143 xfs_trans_set_sync(tp);
1144 code = xfs_trans_commit(tp, 0);
1145 xfs_iunlock(ip, lock_flags);
1146
1147 /*
1148 * Release any dquot(s) the inode had kept before chown.
1149 */
1150 XFS_QM_DQRELE(mp, olddquot);
1151 XFS_QM_DQRELE(mp, udqp);
1152 XFS_QM_DQRELE(mp, gdqp);
1153
1154 if (code)
1155 return code;
1156
1157 if (DM_EVENT_ENABLED(ip, DM_EVENT_ATTRIBUTE)) {
1158 XFS_SEND_NAMESP(mp, DM_EVENT_ATTRIBUTE, ip, DM_RIGHT_NULL,
1159 NULL, DM_RIGHT_NULL, NULL, NULL, 0, 0,
1160 (mask & FSX_NONBLOCK) ? DM_FLAGS_NDELAY : 0);
1161 }
1162
1163 vn_revalidate(XFS_ITOV(ip)); /* update flags */
1164 return 0;
1165
1166 error_return:
1167 XFS_QM_DQRELE(mp, udqp);
1168 XFS_QM_DQRELE(mp, gdqp);
1169 xfs_trans_cancel(tp, 0);
1170 if (lock_flags)
1171 xfs_iunlock(ip, lock_flags);
1172 return code;
1173}
1174
882STATIC int 1175STATIC int
883xfs_ioc_fssetxattr( 1176xfs_ioc_fssetxattr(
884 xfs_inode_t *ip, 1177 xfs_inode_t *ip,
@@ -886,31 +1179,16 @@ xfs_ioc_fssetxattr(
886 void __user *arg) 1179 void __user *arg)
887{ 1180{
888 struct fsxattr fa; 1181 struct fsxattr fa;
889 struct bhv_vattr *vattr; 1182 unsigned int mask;
890 int error;
891 int attr_flags;
892 1183
893 if (copy_from_user(&fa, arg, sizeof(fa))) 1184 if (copy_from_user(&fa, arg, sizeof(fa)))
894 return -EFAULT; 1185 return -EFAULT;
895 1186
896 vattr = kmalloc(sizeof(*vattr), GFP_KERNEL); 1187 mask = FSX_XFLAGS | FSX_EXTSIZE | FSX_PROJID;
897 if (unlikely(!vattr))
898 return -ENOMEM;
899
900 attr_flags = 0;
901 if (filp->f_flags & (O_NDELAY|O_NONBLOCK)) 1188 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
902 attr_flags |= ATTR_NONBLOCK; 1189 mask |= FSX_NONBLOCK;
903 1190
904 vattr->va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | XFS_AT_PROJID; 1191 return -xfs_ioctl_setattr(ip, &fa, mask);
905 vattr->va_xflags = fa.fsx_xflags;
906 vattr->va_extsize = fa.fsx_extsize;
907 vattr->va_projid = fa.fsx_projid;
908
909 error = -xfs_setattr(ip, vattr, attr_flags, NULL);
910 if (!error)
911 vn_revalidate(XFS_ITOV(ip)); /* update flags */
912 kfree(vattr);
913 return 0;
914} 1192}
915 1193
916STATIC int 1194STATIC int
@@ -932,10 +1210,9 @@ xfs_ioc_setxflags(
932 struct file *filp, 1210 struct file *filp,
933 void __user *arg) 1211 void __user *arg)
934{ 1212{
935 struct bhv_vattr *vattr; 1213 struct fsxattr fa;
936 unsigned int flags; 1214 unsigned int flags;
937 int attr_flags; 1215 unsigned int mask;
938 int error;
939 1216
940 if (copy_from_user(&flags, arg, sizeof(flags))) 1217 if (copy_from_user(&flags, arg, sizeof(flags)))
941 return -EFAULT; 1218 return -EFAULT;
@@ -945,22 +1222,12 @@ xfs_ioc_setxflags(
945 FS_SYNC_FL)) 1222 FS_SYNC_FL))
946 return -EOPNOTSUPP; 1223 return -EOPNOTSUPP;
947 1224
948 vattr = kmalloc(sizeof(*vattr), GFP_KERNEL); 1225 mask = FSX_XFLAGS;
949 if (unlikely(!vattr))
950 return -ENOMEM;
951
952 attr_flags = 0;
953 if (filp->f_flags & (O_NDELAY|O_NONBLOCK)) 1226 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
954 attr_flags |= ATTR_NONBLOCK; 1227 mask |= FSX_NONBLOCK;
1228 fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));
955 1229
956 vattr->va_mask = XFS_AT_XFLAGS; 1230 return -xfs_ioctl_setattr(ip, &fa, mask);
957 vattr->va_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));
958
959 error = -xfs_setattr(ip, vattr, attr_flags, NULL);
960 if (likely(!error))
961 vn_revalidate(XFS_ITOV(ip)); /* update flags */
962 kfree(vattr);
963 return error;
964} 1231}
965 1232
966STATIC int 1233STATIC int