1 /* 2 * 9p xattr callback 3 * 4 * Copyright IBM, Corp. 2010 5 * 6 * Authors: 7 * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2. See 10 * the COPYING file in the top-level directory. 11 * 12 */ 13 14 #include "qemu/osdep.h" 15 #include "9p.h" 16 #include "fsdev/file-op-9p.h" 17 #include "9p-xattr.h" 18 #include "9p-util.h" 19 #include "9p-local.h" 20 21 22 static XattrOperations *get_xattr_operations(XattrOperations **h, 23 const char *name) 24 { 25 XattrOperations *xops; 26 for (xops = *(h)++; xops != NULL; xops = *(h)++) { 27 if (!strncmp(name, xops->name, strlen(xops->name))) { 28 return xops; 29 } 30 } 31 return NULL; 32 } 33 34 ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, 35 const char *name, void *value, size_t size) 36 { 37 XattrOperations *xops = get_xattr_operations(ctx->xops, name); 38 if (xops) { 39 return xops->getxattr(ctx, path, name, value, size); 40 } 41 errno = EOPNOTSUPP; 42 return -1; 43 } 44 45 ssize_t pt_listxattr(FsContext *ctx, const char *path, 46 char *name, void *value, size_t size) 47 { 48 int name_size = strlen(name) + 1; 49 if (!value) { 50 return name_size; 51 } 52 53 if (size < name_size) { 54 errno = ERANGE; 55 return -1; 56 } 57 58 /* no need for strncpy: name_size is strlen(name)+1 */ 59 memcpy(value, name, name_size); 60 return name_size; 61 } 62 63 static ssize_t flistxattrat_nofollow(int dirfd, const char *filename, 64 char *list, size_t size) 65 { 66 char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); 67 int ret; 68 69 ret = llistxattr(proc_path, list, size); 70 g_free(proc_path); 71 return ret; 72 } 73 74 /* 75 * Get the list and pass to each layer to find out whether 76 * to send the data or not 77 */ 78 ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, 79 void *value, size_t vsize) 80 { 81 ssize_t size = 0; 82 void *ovalue = value; 83 XattrOperations *xops; 84 char *orig_value, *orig_value_start; 85 ssize_t xattr_len, parsed_len = 0, attr_len; 86 char *dirpath, *name; 87 int dirfd; 88 89 /* Get the actual len */ 90 dirpath = g_path_get_dirname(path); 91 dirfd = local_opendir_nofollow(ctx, dirpath); 92 g_free(dirpath); 93 if (dirfd == -1) { 94 return -1; 95 } 96 97 name = g_path_get_basename(path); 98 xattr_len = flistxattrat_nofollow(dirfd, name, value, 0); 99 if (xattr_len <= 0) { 100 g_free(name); 101 close_preserve_errno(dirfd); 102 return xattr_len; 103 } 104 105 /* Now fetch the xattr and find the actual size */ 106 orig_value = g_malloc(xattr_len); 107 xattr_len = flistxattrat_nofollow(dirfd, name, orig_value, xattr_len); 108 g_free(name); 109 close_preserve_errno(dirfd); 110 if (xattr_len < 0) { 111 return -1; 112 } 113 114 /* store the orig pointer */ 115 orig_value_start = orig_value; 116 while (xattr_len > parsed_len) { 117 xops = get_xattr_operations(ctx->xops, orig_value); 118 if (!xops) { 119 goto next_entry; 120 } 121 122 if (!value) { 123 size += xops->listxattr(ctx, path, orig_value, value, vsize); 124 } else { 125 size = xops->listxattr(ctx, path, orig_value, value, vsize); 126 if (size < 0) { 127 goto err_out; 128 } 129 value += size; 130 vsize -= size; 131 } 132 next_entry: 133 /* Got the next entry */ 134 attr_len = strlen(orig_value) + 1; 135 parsed_len += attr_len; 136 orig_value += attr_len; 137 } 138 if (value) { 139 size = value - ovalue; 140 } 141 142 err_out: 143 g_free(orig_value_start); 144 return size; 145 } 146 147 int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name, 148 void *value, size_t size, int flags) 149 { 150 XattrOperations *xops = get_xattr_operations(ctx->xops, name); 151 if (xops) { 152 return xops->setxattr(ctx, path, name, value, size, flags); 153 } 154 errno = EOPNOTSUPP; 155 return -1; 156 157 } 158 159 int v9fs_remove_xattr(FsContext *ctx, 160 const char *path, const char *name) 161 { 162 XattrOperations *xops = get_xattr_operations(ctx->xops, name); 163 if (xops) { 164 return xops->removexattr(ctx, path, name); 165 } 166 errno = EOPNOTSUPP; 167 return -1; 168 169 } 170 171 ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path, 172 const char *name, void *value, size_t size) 173 { 174 char *dirpath = g_path_get_dirname(path); 175 char *filename = g_path_get_basename(path); 176 int dirfd; 177 ssize_t ret = -1; 178 179 dirfd = local_opendir_nofollow(ctx, dirpath); 180 if (dirfd == -1) { 181 goto out; 182 } 183 184 ret = fgetxattrat_nofollow(dirfd, filename, name, value, size); 185 close_preserve_errno(dirfd); 186 out: 187 g_free(dirpath); 188 g_free(filename); 189 return ret; 190 } 191 192 ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name, 193 void *value, size_t size) 194 { 195 return local_getxattr_nofollow(ctx, path, name, value, size); 196 } 197 198 int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name, 199 void *value, size_t size, int flags) 200 { 201 char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); 202 int ret; 203 204 ret = lsetxattr(proc_path, name, value, size, flags); 205 g_free(proc_path); 206 return ret; 207 } 208 209 ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path, 210 const char *name, void *value, size_t size, 211 int flags) 212 { 213 char *dirpath = g_path_get_dirname(path); 214 char *filename = g_path_get_basename(path); 215 int dirfd; 216 ssize_t ret = -1; 217 218 dirfd = local_opendir_nofollow(ctx, dirpath); 219 if (dirfd == -1) { 220 goto out; 221 } 222 223 ret = fsetxattrat_nofollow(dirfd, filename, name, value, size, flags); 224 close_preserve_errno(dirfd); 225 out: 226 g_free(dirpath); 227 g_free(filename); 228 return ret; 229 } 230 231 int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value, 232 size_t size, int flags) 233 { 234 return local_setxattr_nofollow(ctx, path, name, value, size, flags); 235 } 236 237 static ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, 238 const char *name) 239 { 240 char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); 241 int ret; 242 243 ret = lremovexattr(proc_path, name); 244 g_free(proc_path); 245 return ret; 246 } 247 248 ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path, 249 const char *name) 250 { 251 char *dirpath = g_path_get_dirname(path); 252 char *filename = g_path_get_basename(path); 253 int dirfd; 254 ssize_t ret = -1; 255 256 dirfd = local_opendir_nofollow(ctx, dirpath); 257 if (dirfd == -1) { 258 goto out; 259 } 260 261 ret = fremovexattrat_nofollow(dirfd, filename, name); 262 close_preserve_errno(dirfd); 263 out: 264 g_free(dirpath); 265 g_free(filename); 266 return ret; 267 } 268 269 int pt_removexattr(FsContext *ctx, const char *path, const char *name) 270 { 271 return local_removexattr_nofollow(ctx, path, name); 272 } 273 274 ssize_t notsup_getxattr(FsContext *ctx, const char *path, const char *name, 275 void *value, size_t size) 276 { 277 errno = ENOTSUP; 278 return -1; 279 } 280 281 int notsup_setxattr(FsContext *ctx, const char *path, const char *name, 282 void *value, size_t size, int flags) 283 { 284 errno = ENOTSUP; 285 return -1; 286 } 287 288 ssize_t notsup_listxattr(FsContext *ctx, const char *path, char *name, 289 void *value, size_t size) 290 { 291 return 0; 292 } 293 294 int notsup_removexattr(FsContext *ctx, const char *path, const char *name) 295 { 296 errno = ENOTSUP; 297 return -1; 298 } 299 300 XattrOperations *mapped_xattr_ops[] = { 301 &mapped_user_xattr, 302 &mapped_pacl_xattr, 303 &mapped_dacl_xattr, 304 NULL, 305 }; 306 307 XattrOperations *passthrough_xattr_ops[] = { 308 &passthrough_user_xattr, 309 &passthrough_acl_xattr, 310 NULL, 311 }; 312 313 /* for .user none model should be same as passthrough */ 314 XattrOperations *none_xattr_ops[] = { 315 &passthrough_user_xattr, 316 &none_acl_xattr, 317 NULL, 318 }; 319