aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/linux-2.6/xfs_ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/linux-2.6/xfs_ioctl.c')
-rw-r--r--fs/xfs/linux-2.6/xfs_ioctl.c376
1 files changed, 337 insertions, 39 deletions
diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c
index 01939ba2d8d..acb978d9d08 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>
@@ -468,6 +470,12 @@ xfs_attrlist_by_handle(
468 if (al_hreq.buflen > XATTR_LIST_MAX) 470 if (al_hreq.buflen > XATTR_LIST_MAX)
469 return -XFS_ERROR(EINVAL); 471 return -XFS_ERROR(EINVAL);
470 472
473 /*
474 * Reject flags, only allow namespaces.
475 */
476 if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE))
477 return -XFS_ERROR(EINVAL);
478
471 error = xfs_vget_fsop_handlereq(mp, parinode, &al_hreq.hreq, &inode); 479 error = xfs_vget_fsop_handlereq(mp, parinode, &al_hreq.hreq, &inode);
472 if (error) 480 if (error)
473 goto out; 481 goto out;
@@ -587,7 +595,7 @@ xfs_attrmulti_by_handle(
587 goto out; 595 goto out;
588 596
589 error = E2BIG; 597 error = E2BIG;
590 size = am_hreq.opcount * sizeof(attr_multiop_t); 598 size = am_hreq.opcount * sizeof(xfs_attr_multiop_t);
591 if (!size || size > 16 * PAGE_SIZE) 599 if (!size || size > 16 * PAGE_SIZE)
592 goto out_vn_rele; 600 goto out_vn_rele;
593 601
@@ -680,9 +688,9 @@ xfs_ioc_space(
680 return -XFS_ERROR(EFAULT); 688 return -XFS_ERROR(EFAULT);
681 689
682 if (filp->f_flags & (O_NDELAY|O_NONBLOCK)) 690 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
683 attr_flags |= ATTR_NONBLOCK; 691 attr_flags |= XFS_ATTR_NONBLOCK;
684 if (ioflags & IO_INVIS) 692 if (ioflags & IO_INVIS)
685 attr_flags |= ATTR_DMI; 693 attr_flags |= XFS_ATTR_DMI;
686 694
687 error = xfs_change_file_space(ip, cmd, &bf, filp->f_pos, 695 error = xfs_change_file_space(ip, cmd, &bf, filp->f_pos,
688 NULL, attr_flags); 696 NULL, attr_flags);
@@ -873,6 +881,322 @@ xfs_ioc_fsgetxattr(
873 return 0; 881 return 0;
874} 882}
875 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
926STATIC void
927xfs_diflags_to_linux(
928 struct xfs_inode *ip)
929{
930 struct inode *inode = XFS_ITOV(ip);
931 unsigned int xflags = xfs_ip2xflags(ip);
932
933 if (xflags & XFS_XFLAG_IMMUTABLE)
934 inode->i_flags |= S_IMMUTABLE;
935 else
936 inode->i_flags &= ~S_IMMUTABLE;
937 if (xflags & XFS_XFLAG_APPEND)
938 inode->i_flags |= S_APPEND;
939 else
940 inode->i_flags &= ~S_APPEND;
941 if (xflags & XFS_XFLAG_SYNC)
942 inode->i_flags |= S_SYNC;
943 else
944 inode->i_flags &= ~S_SYNC;
945 if (xflags & XFS_XFLAG_NOATIME)
946 inode->i_flags |= S_NOATIME;
947 else
948 inode->i_flags &= ~S_NOATIME;
949}
950
951#define FSX_PROJID 1
952#define FSX_EXTSIZE 2
953#define FSX_XFLAGS 4
954#define FSX_NONBLOCK 8
955
956STATIC int
957xfs_ioctl_setattr(
958 xfs_inode_t *ip,
959 struct fsxattr *fa,
960 int mask)
961{
962 struct xfs_mount *mp = ip->i_mount;
963 struct xfs_trans *tp;
964 unsigned int lock_flags = 0;
965 struct xfs_dquot *udqp = NULL, *gdqp = NULL;
966 struct xfs_dquot *olddquot = NULL;
967 int code;
968
969 xfs_itrace_entry(ip);
970
971 if (mp->m_flags & XFS_MOUNT_RDONLY)
972 return XFS_ERROR(EROFS);
973 if (XFS_FORCED_SHUTDOWN(mp))
974 return XFS_ERROR(EIO);
975
976 /*
977 * If disk quotas is on, we make sure that the dquots do exist on disk,
978 * before we start any other transactions. Trying to do this later
979 * is messy. We don't care to take a readlock to look at the ids
980 * in inode here, because we can't hold it across the trans_reserve.
981 * If the IDs do change before we take the ilock, we're covered
982 * because the i_*dquot fields will get updated anyway.
983 */
984 if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) {
985 code = XFS_QM_DQVOPALLOC(mp, ip, ip->i_d.di_uid,
986 ip->i_d.di_gid, fa->fsx_projid,
987 XFS_QMOPT_PQUOTA, &udqp, &gdqp);
988 if (code)
989 return code;
990 }
991
992 /*
993 * For the other attributes, we acquire the inode lock and
994 * first do an error checking pass.
995 */
996 tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
997 code = xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0);
998 if (code)
999 goto error_return;
1000
1001 lock_flags = XFS_ILOCK_EXCL;
1002 xfs_ilock(ip, lock_flags);
1003
1004 /*
1005 * CAP_FOWNER overrides the following restrictions:
1006 *
1007 * The user ID of the calling process must be equal
1008 * to the file owner ID, except in cases where the
1009 * CAP_FSETID capability is applicable.
1010 */
1011 if (current->fsuid != ip->i_d.di_uid && !capable(CAP_FOWNER)) {
1012 code = XFS_ERROR(EPERM);
1013 goto error_return;
1014 }
1015
1016 /*
1017 * Do a quota reservation only if projid is actually going to change.
1018 */
1019 if (mask & FSX_PROJID) {
1020 if (XFS_IS_PQUOTA_ON(mp) &&
1021 ip->i_d.di_projid != fa->fsx_projid) {
1022 ASSERT(tp);
1023 code = XFS_QM_DQVOPCHOWNRESV(mp, tp, ip, udqp, gdqp,
1024 capable(CAP_FOWNER) ?
1025 XFS_QMOPT_FORCE_RES : 0);
1026 if (code) /* out of quota */
1027 goto error_return;
1028 }
1029 }
1030
1031 if (mask & FSX_EXTSIZE) {
1032 /*
1033 * Can't change extent size if any extents are allocated.
1034 */
1035 if (ip->i_d.di_nextents &&
1036 ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) !=
1037 fa->fsx_extsize)) {
1038 code = XFS_ERROR(EINVAL); /* EFBIG? */
1039 goto error_return;
1040 }
1041
1042 /*
1043 * Extent size must be a multiple of the appropriate block
1044 * size, if set at all.
1045 */
1046 if (fa->fsx_extsize != 0) {
1047 xfs_extlen_t size;
1048
1049 if (XFS_IS_REALTIME_INODE(ip) ||
1050 ((mask & FSX_XFLAGS) &&
1051 (fa->fsx_xflags & XFS_XFLAG_REALTIME))) {
1052 size = mp->m_sb.sb_rextsize <<
1053 mp->m_sb.sb_blocklog;
1054 } else {
1055 size = mp->m_sb.sb_blocksize;
1056 }
1057
1058 if (fa->fsx_extsize % size) {
1059 code = XFS_ERROR(EINVAL);
1060 goto error_return;
1061 }
1062 }
1063 }
1064
1065
1066 if (mask & FSX_XFLAGS) {
1067 /*
1068 * Can't change realtime flag if any extents are allocated.
1069 */
1070 if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
1071 (XFS_IS_REALTIME_INODE(ip)) !=
1072 (fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
1073 code = XFS_ERROR(EINVAL); /* EFBIG? */
1074 goto error_return;
1075 }
1076
1077 /*
1078 * If realtime flag is set then must have realtime data.
1079 */
1080 if ((fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
1081 if ((mp->m_sb.sb_rblocks == 0) ||
1082 (mp->m_sb.sb_rextsize == 0) ||
1083 (ip->i_d.di_extsize % mp->m_sb.sb_rextsize)) {
1084 code = XFS_ERROR(EINVAL);
1085 goto error_return;
1086 }
1087 }
1088
1089 /*
1090 * Can't modify an immutable/append-only file unless
1091 * we have appropriate permission.
1092 */
1093 if ((ip->i_d.di_flags &
1094 (XFS_DIFLAG_IMMUTABLE|XFS_DIFLAG_APPEND) ||
1095 (fa->fsx_xflags &
1096 (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) &&
1097 !capable(CAP_LINUX_IMMUTABLE)) {
1098 code = XFS_ERROR(EPERM);
1099 goto error_return;
1100 }
1101 }
1102
1103 xfs_trans_ijoin(tp, ip, lock_flags);
1104 xfs_trans_ihold(tp, ip);
1105
1106 /*
1107 * Change file ownership. Must be the owner or privileged.
1108 * If the system was configured with the "restricted_chown"
1109 * option, the owner is not permitted to give away the file,
1110 * and can change the group id only to a group of which he
1111 * or she is a member.
1112 */
1113 if (mask & FSX_PROJID) {
1114 /*
1115 * CAP_FSETID overrides the following restrictions:
1116 *
1117 * The set-user-ID and set-group-ID bits of a file will be
1118 * cleared upon successful return from chown()
1119 */
1120 if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
1121 !capable(CAP_FSETID))
1122 ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
1123
1124 /*
1125 * Change the ownerships and register quota modifications
1126 * in the transaction.
1127 */
1128 if (ip->i_d.di_projid != fa->fsx_projid) {
1129 if (XFS_IS_PQUOTA_ON(mp)) {
1130 olddquot = XFS_QM_DQVOPCHOWN(mp, tp, ip,
1131 &ip->i_gdquot, gdqp);
1132 }
1133 ip->i_d.di_projid = fa->fsx_projid;
1134
1135 /*
1136 * We may have to rev the inode as well as
1137 * the superblock version number since projids didn't
1138 * exist before DINODE_VERSION_2 and SB_VERSION_NLINK.
1139 */
1140 if (ip->i_d.di_version == XFS_DINODE_VERSION_1)
1141 xfs_bump_ino_vers2(tp, ip);
1142 }
1143
1144 }
1145
1146 if (mask & FSX_EXTSIZE)
1147 ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
1148 if (mask & FSX_XFLAGS) {
1149 xfs_set_diflags(ip, fa->fsx_xflags);
1150 xfs_diflags_to_linux(ip);
1151 }
1152
1153 xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
1154 xfs_ichgtime(ip, XFS_ICHGTIME_CHG);
1155
1156 XFS_STATS_INC(xs_ig_attrchg);
1157
1158 /*
1159 * If this is a synchronous mount, make sure that the
1160 * transaction goes to disk before returning to the user.
1161 * This is slightly sub-optimal in that truncates require
1162 * two sync transactions instead of one for wsync filesystems.
1163 * One for the truncate and one for the timestamps since we
1164 * don't want to change the timestamps unless we're sure the
1165 * truncate worked. Truncates are less than 1% of the laddis
1166 * mix so this probably isn't worth the trouble to optimize.
1167 */
1168 if (mp->m_flags & XFS_MOUNT_WSYNC)
1169 xfs_trans_set_sync(tp);
1170 code = xfs_trans_commit(tp, 0);
1171 xfs_iunlock(ip, lock_flags);
1172
1173 /*
1174 * Release any dquot(s) the inode had kept before chown.
1175 */
1176 XFS_QM_DQRELE(mp, olddquot);
1177 XFS_QM_DQRELE(mp, udqp);
1178 XFS_QM_DQRELE(mp, gdqp);
1179
1180 if (code)
1181 return code;
1182
1183 if (DM_EVENT_ENABLED(ip, DM_EVENT_ATTRIBUTE)) {
1184 XFS_SEND_NAMESP(mp, DM_EVENT_ATTRIBUTE, ip, DM_RIGHT_NULL,
1185 NULL, DM_RIGHT_NULL, NULL, NULL, 0, 0,
1186 (mask & FSX_NONBLOCK) ? DM_FLAGS_NDELAY : 0);
1187 }
1188
1189 return 0;
1190
1191 error_return:
1192 XFS_QM_DQRELE(mp, udqp);
1193 XFS_QM_DQRELE(mp, gdqp);
1194 xfs_trans_cancel(tp, 0);
1195 if (lock_flags)
1196 xfs_iunlock(ip, lock_flags);
1197 return code;
1198}
1199
876STATIC int 1200STATIC int
877xfs_ioc_fssetxattr( 1201xfs_ioc_fssetxattr(
878 xfs_inode_t *ip, 1202 xfs_inode_t *ip,
@@ -880,31 +1204,16 @@ xfs_ioc_fssetxattr(
880 void __user *arg) 1204 void __user *arg)
881{ 1205{
882 struct fsxattr fa; 1206 struct fsxattr fa;
883 struct bhv_vattr *vattr; 1207 unsigned int mask;
884 int error;
885 int attr_flags;
886 1208
887 if (copy_from_user(&fa, arg, sizeof(fa))) 1209 if (copy_from_user(&fa, arg, sizeof(fa)))
888 return -EFAULT; 1210 return -EFAULT;
889 1211
890 vattr = kmalloc(sizeof(*vattr), GFP_KERNEL); 1212 mask = FSX_XFLAGS | FSX_EXTSIZE | FSX_PROJID;
891 if (unlikely(!vattr))
892 return -ENOMEM;
893
894 attr_flags = 0;
895 if (filp->f_flags & (O_NDELAY|O_NONBLOCK)) 1213 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
896 attr_flags |= ATTR_NONBLOCK; 1214 mask |= FSX_NONBLOCK;
897
898 vattr->va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | XFS_AT_PROJID;
899 vattr->va_xflags = fa.fsx_xflags;
900 vattr->va_extsize = fa.fsx_extsize;
901 vattr->va_projid = fa.fsx_projid;
902 1215
903 error = -xfs_setattr(ip, vattr, attr_flags, NULL); 1216 return -xfs_ioctl_setattr(ip, &fa, mask);
904 if (!error)
905 vn_revalidate(XFS_ITOV(ip)); /* update flags */
906 kfree(vattr);
907 return 0;
908} 1217}
909 1218
910STATIC int 1219STATIC int
@@ -926,10 +1235,9 @@ xfs_ioc_setxflags(
926 struct file *filp, 1235 struct file *filp,
927 void __user *arg) 1236 void __user *arg)
928{ 1237{
929 struct bhv_vattr *vattr; 1238 struct fsxattr fa;
930 unsigned int flags; 1239 unsigned int flags;
931 int attr_flags; 1240 unsigned int mask;
932 int error;
933 1241
934 if (copy_from_user(&flags, arg, sizeof(flags))) 1242 if (copy_from_user(&flags, arg, sizeof(flags)))
935 return -EFAULT; 1243 return -EFAULT;
@@ -939,22 +1247,12 @@ xfs_ioc_setxflags(
939 FS_SYNC_FL)) 1247 FS_SYNC_FL))
940 return -EOPNOTSUPP; 1248 return -EOPNOTSUPP;
941 1249
942 vattr = kmalloc(sizeof(*vattr), GFP_KERNEL); 1250 mask = FSX_XFLAGS;
943 if (unlikely(!vattr))
944 return -ENOMEM;
945
946 attr_flags = 0;
947 if (filp->f_flags & (O_NDELAY|O_NONBLOCK)) 1251 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
948 attr_flags |= ATTR_NONBLOCK; 1252 mask |= FSX_NONBLOCK;
949 1253 fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));
950 vattr->va_mask = XFS_AT_XFLAGS;
951 vattr->va_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));
952 1254
953 error = -xfs_setattr(ip, vattr, attr_flags, NULL); 1255 return -xfs_ioctl_setattr(ip, &fa, mask);
954 if (likely(!error))
955 vn_revalidate(XFS_ITOV(ip)); /* update flags */
956 kfree(vattr);
957 return error;
958} 1256}
959 1257
960STATIC int 1258STATIC int