1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 2 3/* GIO - GLib Input, Output and Streaming Library 4 * 5 * Copyright (C) 2006-2007 Red Hat, Inc. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General 18 * Public License along with this library; if not, write to the 19 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, 20 * Boston, MA 02111-1307, USA. 21 * 22 * Author: Alexander Larsson <alexl@redhat.com> 23 */ 24 25#include "config.h" 26 27#ifdef HAVE_SYS_TIME_H 28#include <sys/time.h> 29#endif 30#include <sys/types.h> 31#include <sys/stat.h> 32#include <string.h> 33#ifdef HAVE_UNISTD_H 34#include <unistd.h> 35#endif 36#define _GNU_SOURCE 37#include <fcntl.h> 38#include <errno.h> 39#ifdef HAVE_GRP_H 40#include <grp.h> 41#endif 42#ifdef HAVE_PWD_H 43#include <pwd.h> 44#endif 45#ifdef HAVE_SELINUX 46#include <selinux/selinux.h> 47#endif 48 49#ifdef HAVE_XATTR 50 51#if defined HAVE_SYS_XATTR_H 52 #include <sys/xattr.h> 53#elif defined HAVE_ATTR_XATTR_H 54 #include <attr/xattr.h> 55#else 56 #error "Neither <sys/xattr.h> nor <attr/xattr.h> is present but extended attribute support is enabled." 57#endif /* defined HAVE_SYS_XATTR_H || HAVE_ATTR_XATTR_H */ 58 59#endif /* HAVE_XATTR */ 60 61#include <glib/gstdio.h> 62#include <gfileattribute-priv.h> 63 64#include "glibintl.h" 65 66#ifdef G_OS_WIN32 67#include <windows.h> 68#include <io.h> 69#ifndef W_OK 70#define W_OK 2 71#endif 72#ifndef R_OK 73#define R_OK 4 74#endif 75#ifndef X_OK 76#define X_OK 0 /* not really */ 77#endif 78#ifndef S_ISREG 79#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) 80#endif 81#ifndef S_ISDIR 82#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) 83#endif 84#ifndef S_IXUSR 85#define S_IXUSR _S_IEXEC 86#endif 87#endif 88 89#include "glocalfileinfo.h" 90#include "gioerror.h" 91#include "gthemedicon.h" 92#include "gcontenttype.h" 93#include "gcontenttypeprivate.h" 94 95#include "gioalias.h" 96 97struct ThumbMD5Context { 98 guint32 buf[4]; 99 guint32 bits[2]; 100 unsigned char in[64]; 101}; 102 103#ifndef G_OS_WIN32 104 105typedef struct { 106 char *user_name; 107 char *real_name; 108} UidData; 109 110G_LOCK_DEFINE_STATIC (uid_cache); 111static GHashTable *uid_cache = NULL; 112 113G_LOCK_DEFINE_STATIC (gid_cache); 114static GHashTable *gid_cache = NULL; 115 116#endif /* !G_OS_WIN32 */ 117 118char * 119_g_local_file_info_create_etag (GLocalFileStat *statbuf) 120{ 121 GTimeVal tv; 122 123 tv.tv_sec = statbuf->st_mtime; 124#if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC) 125 tv.tv_usec = statbuf->st_mtimensec / 1000; 126#elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC) 127 tv.tv_usec = statbuf->st_mtim.tv_nsec / 1000; 128#else 129 tv.tv_usec = 0; 130#endif 131 132 return g_strdup_printf ("%lu:%lu", tv.tv_sec, tv.tv_usec); 133} 134 135static char * 136_g_local_file_info_create_file_id (GLocalFileStat *statbuf) 137{ 138 return g_strdup_printf ("l%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT, 139 (guint64) statbuf->st_dev, 140 (guint64) statbuf->st_ino); 141} 142 143static char * 144_g_local_file_info_create_fs_id (GLocalFileStat *statbuf) 145{ 146 return g_strdup_printf ("l%" G_GUINT64_FORMAT, 147 (guint64) statbuf->st_dev); 148} 149 150 151#ifdef S_ISLNK 152 153static gchar * 154read_link (const gchar *full_name) 155{ 156#ifdef HAVE_READLINK 157 gchar *buffer; 158 guint size; 159 160 size = 256; 161 buffer = g_malloc (size); 162 163 while (1) 164 { 165 int read_size; 166 167 read_size = readlink (full_name, buffer, size); 168 if (read_size < 0) 169 { 170 g_free (buffer); 171 return NULL; 172 } 173 if (read_size < size) 174 { 175 buffer[read_size] = 0; 176 return buffer; 177 } 178 size *= 2; 179 buffer = g_realloc (buffer, size); 180 } 181#else 182 return NULL; 183#endif 184} 185 186#endif /* S_ISLNK */ 187 188#ifdef HAVE_SELINUX 189/* Get the SELinux security context */ 190static void 191get_selinux_context (const char *path, 192 GFileInfo *info, 193 GFileAttributeMatcher *attribute_matcher, 194 gboolean follow_symlinks) 195{ 196 char *context; 197 198 if (!g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_SELINUX_CONTEXT)) 199 return; 200 201 if (is_selinux_enabled ()) 202 { 203 if (follow_symlinks) 204 { 205 if (lgetfilecon_raw (path, &context) < 0) 206 return; 207 } 208 else 209 { 210 if (getfilecon_raw (path, &context) < 0) 211 return; 212 } 213 214 if (context) 215 { 216 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT, context); 217 freecon (context); 218 } 219 } 220} 221#endif 222 223#ifdef HAVE_XATTR 224 225/* Wrappers to hide away differences between (Linux) getxattr/lgetxattr and 226 * (Mac) getxattr(..., XATTR_NOFOLLOW) 227 */ 228#ifdef HAVE_XATTR_NOFOLLOW 229#define g_fgetxattr(fd,name,value,size) fgetxattr(fd,name,value,size,0,0) 230#define g_flistxattr(fd,name,size) flistxattr(fd,name,size,0) 231#define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0,0) 232#else 233#define g_fgetxattr fgetxattr 234#define g_flistxattr flistxattr 235#define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0) 236#endif 237 238static ssize_t 239g_getxattr (const char *path, const char *name, void *value, size_t size, 240 gboolean follow_symlinks) 241{ 242#ifdef HAVE_XATTR_NOFOLLOW 243 return getxattr (path, name, value, size, 0, follow_symlinks ? 0 : XATTR_NOFOLLOW); 244#else 245 if (follow_symlinks) 246 return getxattr (path, name, value, size); 247 else 248 return lgetxattr (path, name, value, size); 249#endif 250} 251 252static ssize_t 253g_listxattr(const char *path, char *namebuf, size_t size, 254 gboolean follow_symlinks) 255{ 256#ifdef HAVE_XATTR_NOFOLLOW 257 return listxattr (path, namebuf, size, follow_symlinks ? 0 : XATTR_NOFOLLOW); 258#else 259 if (follow_symlinks) 260 return listxattr (path, namebuf, size); 261 else 262 return llistxattr (path, namebuf, size); 263#endif 264} 265 266static gboolean 267valid_char (char c) 268{ 269 return c >= 32 && c <= 126 && c != '\\'; 270} 271 272static gboolean 273name_is_valid (const char *str) 274{ 275 while (*str) 276 { 277 if (!valid_char (*str++)) 278 return FALSE; 279 } 280 return TRUE; 281} 282 283static char * 284hex_escape_string (const char *str, 285 gboolean *free_return) 286{ 287 int num_invalid, i; 288 char *escaped_str, *p; 289 unsigned char c; 290 static char *hex_digits = "0123456789abcdef"; 291 int len; 292 293 len = strlen (str); 294 295 num_invalid = 0; 296 for (i = 0; i < len; i++) 297 { 298 if (!valid_char (str[i])) 299 num_invalid++; 300 } 301 302 if (num_invalid == 0) 303 { 304 *free_return = FALSE; 305 return (char *)str; 306 } 307 308 escaped_str = g_malloc (len + num_invalid*3 + 1); 309 310 p = escaped_str; 311 for (i = 0; i < len; i++) 312 { 313 if (valid_char (str[i])) 314 *p++ = str[i]; 315 else 316 { 317 c = str[i]; 318 *p++ = '\\'; 319 *p++ = 'x'; 320 *p++ = hex_digits[(c >> 4) & 0xf]; 321 *p++ = hex_digits[c & 0xf]; 322 } 323 } 324 *p++ = 0; 325 326 *free_return = TRUE; 327 return escaped_str; 328} 329 330static char * 331hex_unescape_string (const char *str, 332 int *out_len, 333 gboolean *free_return) 334{ 335 int i; 336 char *unescaped_str, *p; 337 unsigned char c; 338 int len; 339 340 len = strlen (str); 341 342 if (strchr (str, '\\') == NULL) 343 { 344 if (out_len) 345 *out_len = len; 346 *free_return = FALSE; 347 return (char *)str; 348 } 349 350 unescaped_str = g_malloc (len + 1); 351 352 p = unescaped_str; 353 for (i = 0; i < len; i++) 354 { 355 if (str[i] == '\\' && 356 str[i+1] == 'x' && 357 len - i >= 4) 358 { 359 c = 360 (g_ascii_xdigit_value (str[i+2]) << 4) | 361 g_ascii_xdigit_value (str[i+3]); 362 *p++ = c; 363 i += 3; 364 } 365 else 366 *p++ = str[i]; 367 } 368 *p++ = 0; 369 370 if (out_len) 371 *out_len = p - unescaped_str; 372 *free_return = TRUE; 373 return unescaped_str; 374} 375 376static void 377escape_xattr (GFileInfo *info, 378 const char *gio_attr, /* gio attribute name */ 379 const char *value, /* Is zero terminated */ 380 size_t len /* not including zero termination */) 381{ 382 char *escaped_val; 383 gboolean free_escaped_val; 384 385 escaped_val = hex_escape_string (value, &free_escaped_val); 386 387 g_file_info_set_attribute_string (info, gio_attr, escaped_val); 388 389 if (free_escaped_val) 390 g_free (escaped_val); 391} 392 393static void 394get_one_xattr (const char *path, 395 GFileInfo *info, 396 const char *gio_attr, 397 const char *xattr, 398 gboolean follow_symlinks) 399{ 400 char value[64]; 401 char *value_p; 402 ssize_t len; 403 404 len = g_getxattr (path, xattr, value, sizeof (value)-1, follow_symlinks); 405 406 value_p = NULL; 407 if (len >= 0) 408 value_p = value; 409 else if (len == -1 && errno == ERANGE) 410 { 411 len = g_getxattr (path, xattr, NULL, 0, follow_symlinks); 412 413 if (len < 0) 414 return; 415 416 value_p = g_malloc (len+1); 417 418 len = g_getxattr (path, xattr, value_p, len, follow_symlinks); 419 420 if (len < 0) 421 { 422 g_free (value_p); 423 return; 424 } 425 } 426 else 427 return; 428 429 /* Null terminate */ 430 value_p[len] = 0; 431 432 escape_xattr (info, gio_attr, value_p, len); 433 434 if (value_p != value) 435 g_free (value_p); 436} 437 438#endif /* defined HAVE_XATTR */ 439 440static void 441get_xattrs (const char *path, 442 gboolean user, 443 GFileInfo *info, 444 GFileAttributeMatcher *matcher, 445 gboolean follow_symlinks) 446{ 447#ifdef HAVE_XATTR 448 gboolean all; 449 gsize list_size; 450 ssize_t list_res_size; 451 size_t len; 452 char *list; 453 const char *attr, *attr2; 454 455 if (user) 456 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr"); 457 else 458 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys"); 459 460 if (all) 461 { 462 list_res_size = g_listxattr (path, NULL, 0, follow_symlinks); 463 464 if (list_res_size == -1 || 465 list_res_size == 0) 466 return; 467 468 list_size = list_res_size; 469 list = g_malloc (list_size); 470 471 retry: 472 473 list_res_size = g_listxattr (path, list, list_size, follow_symlinks); 474 475 if (list_res_size == -1 && errno == ERANGE) 476 { 477 list_size = list_size * 2; 478 list = g_realloc (list, list_size); 479 goto retry; 480 } 481 482 if (list_res_size == -1) 483 return; 484 485 attr = list; 486 while (list_res_size > 0) 487 { 488 if ((user && g_str_has_prefix (attr, "user.")) || 489 (!user && !g_str_has_prefix (attr, "user."))) 490 { 491 char *escaped_attr, *gio_attr; 492 gboolean free_escaped_attr; 493 494 if (user) 495 { 496 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr); 497 gio_attr = g_strconcat ("xattr::", escaped_attr, NULL); 498 } 499 else 500 { 501 escaped_attr = hex_escape_string (attr, &free_escaped_attr); 502 gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL); 503 } 504 505 if (free_escaped_attr) 506 g_free (escaped_attr); 507 508 get_one_xattr (path, info, gio_attr, attr, follow_symlinks); 509 510 g_free (gio_attr); 511 } 512 513 len = strlen (attr) + 1; 514 attr += len; 515 list_res_size -= len; 516 } 517 518 g_free (list); 519 } 520 else 521 { 522 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL) 523 { 524 char *unescaped_attribute, *a; 525 gboolean free_unescaped_attribute; 526 527 attr2 = strchr (attr, ':'); 528 if (attr2) 529 { 530 attr2 += 2; /* Skip '::' */ 531 unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute); 532 if (user) 533 a = g_strconcat ("user.", unescaped_attribute, NULL); 534 else 535 a = unescaped_attribute; 536 537 get_one_xattr (path, info, attr, a, follow_symlinks); 538 539 if (user) 540 g_free (a); 541 542 if (free_unescaped_attribute) 543 g_free (unescaped_attribute); 544 } 545 } 546 } 547#endif /* defined HAVE_XATTR */ 548} 549 550#ifdef HAVE_XATTR 551static void 552get_one_xattr_from_fd (int fd, 553 GFileInfo *info, 554 const char *gio_attr, 555 const char *xattr) 556{ 557 char value[64]; 558 char *value_p; 559 ssize_t len; 560 561 len = g_fgetxattr (fd, xattr, value, sizeof (value) - 1); 562 563 value_p = NULL; 564 if (len >= 0) 565 value_p = value; 566 else if (len == -1 && errno == ERANGE) 567 { 568 len = g_fgetxattr (fd, xattr, NULL, 0); 569 570 if (len < 0) 571 return; 572 573 value_p = g_malloc (len + 1); 574 575 len = g_fgetxattr (fd, xattr, value_p, len); 576 577 if (len < 0) 578 { 579 g_free (value_p); 580 return; 581 } 582 } 583 else 584 return; 585 586 /* Null terminate */ 587 value_p[len] = 0; 588 589 escape_xattr (info, gio_attr, value_p, len); 590 591 if (value_p != value) 592 g_free (value_p); 593} 594#endif /* defined HAVE_XATTR */ 595 596static void 597get_xattrs_from_fd (int fd, 598 gboolean user, 599 GFileInfo *info, 600 GFileAttributeMatcher *matcher) 601{ 602#ifdef HAVE_XATTR 603 gboolean all; 604 gsize list_size; 605 ssize_t list_res_size; 606 size_t len; 607 char *list; 608 const char *attr, *attr2; 609 610 if (user) 611 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr"); 612 else 613 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys"); 614 615 if (all) 616 { 617 list_res_size = g_flistxattr (fd, NULL, 0); 618 619 if (list_res_size == -1 || 620 list_res_size == 0) 621 return; 622 623 list_size = list_res_size; 624 list = g_malloc (list_size); 625 626 retry: 627 628 list_res_size = g_flistxattr (fd, list, list_size); 629 630 if (list_res_size == -1 && errno == ERANGE) 631 { 632 list_size = list_size * 2; 633 list = g_realloc (list, list_size); 634 goto retry; 635 } 636 637 if (list_res_size == -1) 638 return; 639 640 attr = list; 641 while (list_res_size > 0) 642 { 643 if ((user && g_str_has_prefix (attr, "user.")) || 644 (!user && !g_str_has_prefix (attr, "user."))) 645 { 646 char *escaped_attr, *gio_attr; 647 gboolean free_escaped_attr; 648 649 if (user) 650 { 651 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr); 652 gio_attr = g_strconcat ("xattr::", escaped_attr, NULL); 653 } 654 else 655 { 656 escaped_attr = hex_escape_string (attr, &free_escaped_attr); 657 gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL); 658 } 659 660 if (free_escaped_attr) 661 g_free (escaped_attr); 662 663 get_one_xattr_from_fd (fd, info, gio_attr, attr); 664 } 665 666 len = strlen (attr) + 1; 667 attr += len; 668 list_res_size -= len; 669 } 670 671 g_free (list); 672 } 673 else 674 { 675 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL) 676 { 677 char *unescaped_attribute, *a; 678 gboolean free_unescaped_attribute; 679 680 attr2 = strchr (attr, ':'); 681 if (attr2) 682 { 683 attr2++; /* Skip ':' */ 684 unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute); 685 if (user) 686 a = g_strconcat ("user.", unescaped_attribute, NULL); 687 else 688 a = unescaped_attribute; 689 690 get_one_xattr_from_fd (fd, info, attr, a); 691 692 if (user) 693 g_free (a); 694 695 if (free_unescaped_attribute) 696 g_free (unescaped_attribute); 697 } 698 } 699 } 700#endif /* defined HAVE_XATTR */ 701} 702 703#ifdef HAVE_XATTR 704static gboolean 705set_xattr (char *filename, 706 const char *escaped_attribute, 707 const GFileAttributeValue *attr_value, 708 GError **error) 709{ 710 char *attribute, *value; 711 gboolean free_attribute, free_value; 712 int val_len, res, errsv; 713 gboolean is_user; 714 char *a; 715 716 if (attr_value == NULL) 717 { 718 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, 719 _("Attribute value must be non-NULL")); 720 return FALSE; 721 } 722 723 if (attr_value->type != G_FILE_ATTRIBUTE_TYPE_STRING) 724 { 725 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, 726 _("Invalid attribute type (string expected)")); 727 return FALSE; 728 } 729 730 if (!name_is_valid (escaped_attribute)) 731 { 732 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, 733 _("Invalid extended attribute name")); 734 return FALSE; 735 } 736 737 if (g_str_has_prefix (escaped_attribute, "xattr::")) 738 { 739 escaped_attribute += strlen ("xattr::"); 740 is_user = TRUE; 741 } 742 else 743 { 744 g_warn_if_fail (g_str_has_prefix (escaped_attribute, "xattr-sys::")); 745 escaped_attribute += strlen ("xattr-sys::"); 746 is_user = FALSE; 747 } 748 749 attribute = hex_unescape_string (escaped_attribute, NULL, &free_attribute); 750 value = hex_unescape_string (attr_value->u.string, &val_len, &free_value); 751 752 if (is_user) 753 a = g_strconcat ("user.", attribute, NULL); 754 else 755 a = attribute; 756 757 res = g_setxattr (filename, a, value, val_len); 758 errsv = errno; 759 760 if (is_user) 761 g_free (a); 762 763 if (free_attribute) 764 g_free (attribute); 765 766 if (free_value) 767 g_free (value); 768 769 if (res == -1) 770 { 771 g_set_error (error, G_IO_ERROR, 772 g_io_error_from_errno (errsv), 773 _("Error setting extended attribute '%s': %s"), 774 escaped_attribute, g_strerror (errsv)); 775 return FALSE; 776 } 777 778 return TRUE; 779} 780 781#endif 782 783 784void 785_g_local_file_info_get_parent_info (const char *dir, 786 GFileAttributeMatcher *attribute_matcher, 787 GLocalParentFileInfo *parent_info) 788{ 789 /* Use plain struct stat for now as long as we only look at the 790 * S_ISVTX bit which doesn't exist on Win32 anyway. 791 */ 792 struct stat statbuf; 793 int res; 794 795 parent_info->writable = FALSE; 796 parent_info->is_sticky = FALSE; 797 parent_info->has_trash_dir = FALSE; 798 parent_info->device = 0; 799 800 if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME) || 801 g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE) || 802 g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH) || 803 g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT)) 804 { 805 /* FIXME: Windows: The underlying _waccess() call in the C 806 * library is mostly pointless as it only looks at the READONLY 807 * FAT-style attribute of the file, it doesn't check the ACL at 808 * all. 809 */ 810 parent_info->writable = (g_access (dir, W_OK) == 0); 811 812 res = g_stat (dir, &statbuf); 813 814 /* 815 * The sticky bit (S_ISVTX) on a directory means that a file in that directory can be 816 * renamed or deleted only by the owner of the file, by the owner of the directory, and 817 * by a privileged process. 818 */ 819 if (res == 0) 820 { 821#ifdef S_ISVTX 822 parent_info->is_sticky = (statbuf.st_mode & S_ISVTX) != 0; 823#else 824 parent_info->is_sticky = FALSE; 825#endif 826 parent_info->owner = statbuf.st_uid; 827 parent_info->device = statbuf.st_dev; 828 /* No need to find trash dir if it's not writable anyway */ 829 if (parent_info->writable && 830 g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH)) 831 parent_info->has_trash_dir = _g_local_file_has_trash_dir (dir, statbuf.st_dev); 832 } 833 } 834} 835 836static void 837get_access_rights (GFileAttributeMatcher *attribute_matcher, 838 GFileInfo *info, 839 const gchar *path, 840 GLocalFileStat *statbuf, 841 GLocalParentFileInfo *parent_info) 842{ 843 /* FIXME: Windows: The underlyin _waccess() is mostly pointless */ 844 if (g_file_attribute_matcher_matches (attribute_matcher, 845 G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) 846 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, 847 g_access (path, R_OK) == 0); 848 849 if (g_file_attribute_matcher_matches (attribute_matcher, 850 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) 851 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, 852 g_access (path, W_OK) == 0); 853 854 if (g_file_attribute_matcher_matches (attribute_matcher, 855 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) 856 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, 857 g_access (path, X_OK) == 0); 858 859 860 if (parent_info) 861 { 862 gboolean writable; 863 864 writable = FALSE; 865 if (parent_info->writable) 866 { 867 if (parent_info->is_sticky) 868 { 869#ifndef G_OS_WIN32 870 uid_t uid = geteuid (); 871 872 if (uid == statbuf->st_uid || 873 uid == parent_info->owner || 874 uid == 0) 875#endif 876 writable = TRUE; 877 } 878 else 879 writable = TRUE; 880 } 881 882 if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME)) 883 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, 884 writable); 885 886 if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE)) 887 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, 888 writable); 889 890 if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH)) 891 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, 892 writable && parent_info->has_trash_dir); 893 } 894} 895 896static void 897set_info_from_stat (GFileInfo *info, 898 GLocalFileStat *statbuf, 899 GFileAttributeMatcher *attribute_matcher) 900{ 901 GFileType file_type; 902 903 file_type = G_FILE_TYPE_UNKNOWN; 904 905 if (S_ISREG (statbuf->st_mode)) 906 file_type = G_FILE_TYPE_REGULAR; 907 else if (S_ISDIR (statbuf->st_mode)) 908 file_type = G_FILE_TYPE_DIRECTORY; 909#ifndef G_OS_WIN32 910 else if (S_ISCHR (statbuf->st_mode) || 911 S_ISBLK (statbuf->st_mode) || 912 S_ISFIFO (statbuf->st_mode) 913#ifdef S_ISSOCK 914 || S_ISSOCK (statbuf->st_mode) 915#endif 916 ) 917 file_type = G_FILE_TYPE_SPECIAL; 918#endif 919#ifdef S_ISLNK 920 else if (S_ISLNK (statbuf->st_mode)) 921 file_type = G_FILE_TYPE_SYMBOLIC_LINK; 922#endif 923 924 g_file_info_set_file_type (info, file_type); 925 g_file_info_set_size (info, statbuf->st_size); 926 927 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_DEVICE, statbuf->st_dev); 928#ifndef G_OS_WIN32 929 /* Pointless setting these on Windows even if they exist in the struct */ 930 g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE, statbuf->st_ino); 931 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_NLINK, statbuf->st_nlink); 932 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, statbuf->st_uid); 933 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, statbuf->st_gid); 934 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_RDEV, statbuf->st_rdev); 935#endif 936 /* FIXME: st_mode is mostly pointless on Windows, too. Set the attribute or not? */ 937 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, statbuf->st_mode); 938#if defined (HAVE_STRUCT_STAT_ST_BLKSIZE) 939 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE, statbuf->st_blksize); 940#endif 941#if defined (HAVE_STRUCT_STAT_ST_BLOCKS) 942 g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_BLOCKS, statbuf->st_blocks); 943 g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE, 944 statbuf->st_blocks * G_GUINT64_CONSTANT (512)); 945#endif 946 947 g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, statbuf->st_mtime); 948#if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC) 949 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, statbuf->st_mtimensec / 1000); 950#elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC) 951 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, statbuf->st_mtim.tv_nsec / 1000); 952#endif 953 954 g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS, statbuf->st_atime); 955#if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC) 956 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC, statbuf->st_atimensec / 1000); 957#elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC) 958 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC, statbuf->st_atim.tv_nsec / 1000); 959#endif 960 961 g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CHANGED, statbuf->st_ctime); 962#if defined (HAVE_STRUCT_STAT_ST_CTIMENSEC) 963 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CHANGED_USEC, statbuf->st_ctimensec / 1000); 964#elif defined (HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC) 965 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CHANGED_USEC, statbuf->st_ctim.tv_nsec / 1000); 966#endif 967 968 if (g_file_attribute_matcher_matches (attribute_matcher, 969 G_FILE_ATTRIBUTE_ETAG_VALUE)) 970 { 971 char *etag = _g_local_file_info_create_etag (statbuf); 972 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE, etag); 973 g_free (etag); 974 } 975 976 if (g_file_attribute_matcher_matches (attribute_matcher, 977 G_FILE_ATTRIBUTE_ID_FILE)) 978 { 979 char *id = _g_local_file_info_create_file_id (statbuf); 980 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE, id); 981 g_free (id); 982 } 983 984 if (g_file_attribute_matcher_matches (attribute_matcher, 985 G_FILE_ATTRIBUTE_ID_FILESYSTEM)) 986 { 987 char *id = _g_local_file_info_create_fs_id (statbuf); 988 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM, id); 989 g_free (id); 990 } 991} 992 993#ifndef G_OS_WIN32 994 995static char * 996make_valid_utf8 (const char *name) 997{ 998 GString *string; 999 const gchar *remainder, *invalid; 1000 gint remaining_bytes, valid_bytes; 1001 1002 string = NULL; 1003 remainder = name; 1004 remaining_bytes = strlen (name); 1005 1006 while (remaining_bytes != 0) 1007 { 1008 if (g_utf8_validate (remainder, remaining_bytes, &invalid)) 1009 break; 1010 valid_bytes = invalid - remainder; 1011 1012 if (string == NULL) 1013 string = g_string_sized_new (remaining_bytes); 1014 1015 g_string_append_len (string, remainder, valid_bytes); 1016 /* append U+FFFD REPLACEMENT CHARACTER */ 1017 g_string_append (string, "\357\277\275"); 1018 1019 remaining_bytes -= valid_bytes + 1; 1020 remainder = invalid + 1; 1021 } 1022 1023 if (string == NULL) 1024 return g_strdup (name); 1025 1026 g_string_append (string, remainder); 1027 1028 g_warn_if_fail (g_utf8_validate (string->str, -1, NULL)); 1029 1030 return g_string_free (string, FALSE); 1031} 1032 1033static char * 1034convert_pwd_string_to_utf8 (char *pwd_str) 1035{ 1036 char *utf8_string; 1037 1038 if (!g_utf8_validate (pwd_str, -1, NULL)) 1039 { 1040 utf8_string = g_locale_to_utf8 (pwd_str, -1, NULL, NULL, NULL); 1041 if (utf8_string == NULL) 1042 utf8_string = make_valid_utf8 (pwd_str); 1043 } 1044 else 1045 utf8_string = g_strdup (pwd_str); 1046 1047 return utf8_string; 1048} 1049 1050static void 1051uid_data_free (UidData *data) 1052{ 1053 g_free (data->user_name); 1054 g_free (data->real_name); 1055 g_free (data); 1056} 1057 1058/* called with lock held */ 1059static UidData * 1060lookup_uid_data (uid_t uid) 1061{ 1062 UidData *data; 1063 char buffer[4096]; 1064 struct passwd pwbuf; 1065 struct passwd *pwbufp; 1066 char *gecos, *comma; 1067 1068 if (uid_cache == NULL) 1069 uid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)uid_data_free); 1070 1071 data = g_hash_table_lookup (uid_cache, GINT_TO_POINTER (uid)); 1072 1073 if (data) 1074 return data; 1075 1076 data = g_new0 (UidData, 1); 1077 1078#if defined(HAVE_POSIX_GETPWUID_R) 1079 getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer), &pwbufp); 1080#elif defined(HAVE_NONPOSIX_GETPWUID_R) 1081 pwbufp = getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer)); 1082#else 1083 pwbufp = getpwuid (uid); 1084#endif 1085 1086 if (pwbufp != NULL) 1087 { 1088 if (pwbufp->pw_name != NULL && pwbufp->pw_name[0] != 0) 1089 data->user_name = convert_pwd_string_to_utf8 (pwbufp->pw_name); 1090 1091 gecos = pwbufp->pw_gecos; 1092 1093 if (gecos) 1094 { 1095 comma = strchr (gecos, ','); 1096 if (comma) 1097 *comma = 0; 1098 data->real_name = convert_pwd_string_to_utf8 (gecos); 1099 } 1100 } 1101 1102 /* Default fallbacks */ 1103 if (data->real_name == NULL) 1104 { 1105 if (data->user_name != NULL) 1106 data->real_name = g_strdup (data->user_name); 1107 else 1108 data->real_name = g_strdup_printf ("user #%d", (int)uid); 1109 } 1110 1111 if (data->user_name == NULL) 1112 data->user_name = g_strdup_printf ("%d", (int)uid); 1113 1114 g_hash_table_replace (uid_cache, GINT_TO_POINTER (uid), data); 1115 1116 return data; 1117} 1118 1119static char * 1120get_username_from_uid (uid_t uid) 1121{ 1122 char *res; 1123 UidData *data; 1124 1125 G_LOCK (uid_cache); 1126 data = lookup_uid_data (uid); 1127 res = g_strdup (data->user_name); 1128 G_UNLOCK (uid_cache); 1129 1130 return res; 1131} 1132 1133static char * 1134get_realname_from_uid (uid_t uid) 1135{ 1136 char *res; 1137 UidData *data; 1138 1139 G_LOCK (uid_cache); 1140 data = lookup_uid_data (uid); 1141 res = g_strdup (data->real_name); 1142 G_UNLOCK (uid_cache); 1143 1144 return res; 1145} 1146 1147/* called with lock held */ 1148static char * 1149lookup_gid_name (gid_t gid) 1150{ 1151 char *name; 1152 char buffer[4096]; 1153 struct group gbuf; 1154 struct group *gbufp; 1155 1156 if (gid_cache == NULL) 1157 gid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free); 1158 1159 name = g_hash_table_lookup (gid_cache, GINT_TO_POINTER (gid)); 1160 1161 if (name) 1162 return name; 1163 1164#if defined (HAVE_POSIX_GETGRGID_R) 1165 getgrgid_r (gid, &gbuf, buffer, sizeof(buffer), &gbufp); 1166#elif defined (HAVE_NONPOSIX_GETGRGID_R) 1167 gbufp = getgrgid_r (gid, &gbuf, buffer, sizeof(buffer)); 1168#else 1169 gbufp = getgrgid (gid); 1170#endif 1171 1172 if (gbufp != NULL && 1173 gbufp->gr_name != NULL && 1174 gbufp->gr_name[0] != 0) 1175 name = convert_pwd_string_to_utf8 (gbufp->gr_name); 1176 else 1177 name = g_strdup_printf("%d", (int)gid); 1178 1179 g_hash_table_replace (gid_cache, GINT_TO_POINTER (gid), name); 1180 1181 return name; 1182} 1183 1184static char * 1185get_groupname_from_gid (gid_t gid) 1186{ 1187 char *res; 1188 char *name; 1189 1190 G_LOCK (gid_cache); 1191 name = lookup_gid_name (gid); 1192 res = g_strdup (name); 1193 G_UNLOCK (gid_cache); 1194 return res; 1195} 1196 1197#endif /* !G_OS_WIN32 */ 1198 1199static char * 1200get_content_type (const char *basename, 1201 const char *path, 1202 GLocalFileStat *statbuf, 1203 gboolean is_symlink, 1204 gboolean symlink_broken, 1205 GFileQueryInfoFlags flags, 1206 gboolean fast) 1207{ 1208 if (is_symlink && 1209 (symlink_broken || (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))) 1210 return g_strdup ("inode/symlink"); 1211 else if (S_ISDIR(statbuf->st_mode)) 1212 return g_strdup ("inode/directory"); 1213#ifndef G_OS_WIN32 1214 else if (S_ISCHR(statbuf->st_mode)) 1215 return g_strdup ("inode/chardevice"); 1216 else if (S_ISBLK(statbuf->st_mode)) 1217 return g_strdup ("inode/blockdevice"); 1218 else if (S_ISFIFO(statbuf->st_mode)) 1219 return g_strdup ("inode/fifo"); 1220#endif 1221#ifdef S_ISSOCK 1222 else if (S_ISSOCK(statbuf->st_mode)) 1223 return g_strdup ("inode/socket"); 1224#endif 1225 else 1226 { 1227 char *content_type; 1228 gboolean result_uncertain; 1229 1230 content_type = g_content_type_guess (basename, NULL, 0, &result_uncertain); 1231 1232#ifndef G_OS_WIN32 1233 if (!fast && result_uncertain && path != NULL) 1234 { 1235 guchar sniff_buffer[4096]; 1236 gsize sniff_length; 1237 int fd; 1238 1239 sniff_length = _g_unix_content_type_get_sniff_len (); 1240 if (sniff_length > 4096) 1241 sniff_length = 4096; 1242 1243#ifdef O_NOATIME 1244 fd = open (path, O_RDONLY | O_NOATIME); 1245 if (fd < 0 && errno == EPERM) 1246#endif 1247 fd = open (path, O_RDONLY); 1248 1249 if (fd != -1) 1250 { 1251 ssize_t res; 1252 1253 res = read (fd, sniff_buffer, sniff_length); 1254 close (fd); 1255 if (res >= 0) 1256 { 1257 g_free (content_type); 1258 content_type = g_content_type_guess (basename, sniff_buffer, res, NULL); 1259 } 1260 } 1261 } 1262#endif 1263 1264 return content_type; 1265 } 1266 1267} 1268 1269static void 1270get_thumbnail_attributes (const char *path, 1271 GFileInfo *info) 1272{ 1273 GChecksum *checksum; 1274 char *uri; 1275 char *filename; 1276 char *basename; 1277 1278 uri = g_filename_to_uri (path, NULL, NULL); 1279 1280 checksum = g_checksum_new (G_CHECKSUM_MD5); 1281 g_checksum_update (checksum, (const guchar *) uri, strlen (uri)); 1282 1283 g_free (uri); 1284 1285 basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL); 1286 g_checksum_free (checksum); 1287 1288 filename = g_build_filename (g_get_home_dir (), 1289 ".thumbnails", "normal", basename, 1290 NULL); 1291 1292 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) 1293 g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, filename); 1294 else 1295 { 1296 g_free (filename); 1297 filename = g_build_filename (g_get_home_dir (), 1298 ".thumbnails", "fail", 1299 "gnome-thumbnail-factory", 1300 basename, 1301 NULL); 1302 1303 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) 1304 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED, TRUE); 1305 } 1306 g_free (basename); 1307 g_free (filename); 1308} 1309 1310#ifdef G_OS_WIN32 1311static void 1312win32_get_file_user_info (const gchar *filename, 1313 gchar **group_name, 1314 gchar **user_name, 1315 gchar **real_name) 1316{ 1317 PSECURITY_DESCRIPTOR psd = NULL; 1318 DWORD sd_size = 0; /* first call calculates the size required */ 1319 1320 wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL); 1321 if ((GetFileSecurityW (wfilename, 1322 GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, 1323 NULL, 1324 sd_size, 1325 &sd_size) || (ERROR_INSUFFICIENT_BUFFER == GetLastError())) && 1326 (psd = g_try_malloc (sd_size)) != NULL && 1327 GetFileSecurityW (wfilename, 1328 GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, 1329 psd, 1330 sd_size, 1331 &sd_size)) 1332 { 1333 PSID psid = 0; 1334 BOOL defaulted; 1335 SID_NAME_USE name_use = 0; /* don't care? */ 1336 wchar_t *name = NULL; 1337 wchar_t *domain = NULL; 1338 DWORD name_len = 0; 1339 DWORD domain_len = 0; 1340 /* get the user name */ 1341 do { 1342 if (!user_name) 1343 break; 1344 if (!GetSecurityDescriptorOwner (psd, &psid, &defaulted)) 1345 break; 1346 if (!LookupAccountSidW (NULL, /* local machine */ 1347 psid, 1348 name, &name_len, 1349 domain, &domain_len, /* no domain info yet */ 1350 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError())) 1351 break; 1352 name = g_try_malloc (name_len * sizeof (wchar_t)); 1353 domain = g_try_malloc (domain_len * sizeof (wchar_t)); 1354 if (name && domain && 1355 LookupAccountSidW (NULL, /* local machine */ 1356 psid, 1357 name, &name_len, 1358 domain, &domain_len, /* no domain info yet */ 1359 &name_use)) 1360 { 1361 *user_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL); 1362 } 1363 g_free (name); 1364 g_free (domain); 1365 } while (FALSE); 1366 1367 /* get the group name */ 1368 do { 1369 if (!group_name) 1370 break; 1371 if (!GetSecurityDescriptorGroup (psd, &psid, &defaulted)) 1372 break; 1373 if (!LookupAccountSidW (NULL, /* local machine */ 1374 psid, 1375 name, &name_len, 1376 domain, &domain_len, /* no domain info yet */ 1377 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError())) 1378 break; 1379 name = g_try_malloc (name_len * sizeof (wchar_t)); 1380 domain = g_try_malloc (domain_len * sizeof (wchar_t)); 1381 if (name && domain && 1382 LookupAccountSidW (NULL, /* local machine */ 1383 psid, 1384 name, &name_len, 1385 domain, &domain_len, /* no domain info yet */ 1386 &name_use)) 1387 { 1388 *group_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL); 1389 } 1390 g_free (name); 1391 g_free (domain); 1392 } while (FALSE); 1393 1394 /* TODO: get real name */ 1395 1396 g_free (psd); 1397 } 1398 g_free (wfilename); 1399} 1400#endif /* G_OS_WIN32 */ 1401 1402GFileInfo * 1403_g_local_file_info_get (const char *basename, 1404 const char *path, 1405 GFileAttributeMatcher *attribute_matcher, 1406 GFileQueryInfoFlags flags, 1407 GLocalParentFileInfo *parent_info, 1408 GError **error) 1409{ 1410 GFileInfo *info; 1411 GLocalFileStat statbuf; 1412#ifdef S_ISLNK 1413 struct stat statbuf2; 1414#endif 1415 int res; 1416 gboolean is_symlink, symlink_broken; 1417#ifdef G_OS_WIN32 1418 DWORD dos_attributes; 1419#endif 1420 1421 info = g_file_info_new (); 1422 1423 /* Make sure we don't set any unwanted attributes */ 1424 g_file_info_set_attribute_mask (info, attribute_matcher); 1425 1426 g_file_info_set_name (info, basename); 1427 1428 /* Avoid stat in trivial case */ 1429 if (attribute_matcher == NULL) 1430 return info; 1431 1432#ifndef G_OS_WIN32 1433 res = g_lstat (path, &statbuf); 1434#else 1435 { 1436 wchar_t *wpath = g_utf8_to_utf16 (path, -1, NULL, NULL, error); 1437 int len; 1438 1439 if (wpath == NULL) 1440 { 1441 g_object_unref (info); 1442 return NULL; 1443 } 1444 1445 len = wcslen (wpath); 1446 while (len > 0 && G_IS_DIR_SEPARATOR (wpath[len-1])) 1447 len--; 1448 if (len > 0 && 1449 (!g_path_is_absolute (path) || len > g_path_skip_root (path) - path)) 1450 wpath[len] = '\0'; 1451 1452 res = _wstati64 (wpath, &statbuf); 1453 dos_attributes = GetFileAttributesW (wpath); 1454 1455 g_free (wpath); 1456 } 1457#endif 1458 1459 if (res == -1) 1460 { 1461 int errsv = errno; 1462 char *display_name = g_filename_display_name (path); 1463 g_object_unref (info); 1464 g_set_error (error, G_IO_ERROR, 1465 g_io_error_from_errno (errsv), 1466 _("Error stating file '%s': %s"), 1467 display_name, g_strerror (errsv)); 1468 g_free (display_name); 1469 return NULL; 1470 } 1471 1472#ifdef S_ISLNK 1473 is_symlink = S_ISLNK (statbuf.st_mode); 1474#else 1475 is_symlink = FALSE; 1476#endif 1477 symlink_broken = FALSE; 1478#ifdef S_ISLNK 1479 if (is_symlink) 1480 { 1481 g_file_info_set_is_symlink (info, TRUE); 1482 1483 /* Unless NOFOLLOW was set we default to following symlinks */ 1484 if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)) 1485 { 1486 res = stat (path, &statbuf2); 1487 1488 /* Report broken links as symlinks */ 1489 if (res != -1) 1490 statbuf = statbuf2; 1491 else 1492 symlink_broken = TRUE; 1493 } 1494 } 1495#endif 1496 1497 set_info_from_stat (info, &statbuf, attribute_matcher); 1498 1499#ifndef G_OS_WIN32 1500 if (basename != NULL && basename[0] == '.') 1501 g_file_info_set_is_hidden (info, TRUE); 1502 1503 if (basename != NULL && basename[strlen (basename) -1] == '~' && 1504 S_ISREG (statbuf.st_mode)) 1505 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP, TRUE); 1506#else 1507 if (dos_attributes & FILE_ATTRIBUTE_HIDDEN) 1508 g_file_info_set_is_hidden (info, TRUE); 1509 1510 if (dos_attributes & FILE_ATTRIBUTE_ARCHIVE) 1511 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_DOS_IS_ARCHIVE, TRUE); 1512 1513 if (dos_attributes & FILE_ATTRIBUTE_SYSTEM) 1514 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_DOS_IS_SYSTEM, TRUE); 1515#endif 1516 1517#ifdef S_ISLNK 1518 if (is_symlink && 1519 g_file_attribute_matcher_matches (attribute_matcher, 1520 G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET)) 1521 { 1522 char *link = read_link (path); 1523 g_file_info_set_symlink_target (info, link); 1524 g_free (link); 1525 } 1526#endif 1527 1528 if (g_file_attribute_matcher_matches (attribute_matcher, 1529 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME)) 1530 { 1531 char *display_name = g_filename_display_basename (path); 1532 1533 /* look for U+FFFD REPLACEMENT CHARACTER */ 1534 if (strstr (display_name, "\357\277\275") != NULL) 1535 { 1536 char *p = display_name; 1537 display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL); 1538 g_free (p); 1539 } 1540 g_file_info_set_display_name (info, display_name); 1541 g_free (display_name); 1542 } 1543 1544 if (g_file_attribute_matcher_matches (attribute_matcher, 1545 G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME)) 1546 { 1547 char *edit_name = g_filename_display_basename (path); 1548 g_file_info_set_edit_name (info, edit_name); 1549 g_free (edit_name); 1550 } 1551 1552 1553 if (g_file_attribute_matcher_matches (attribute_matcher, 1554 G_FILE_ATTRIBUTE_STANDARD_COPY_NAME)) 1555 { 1556 char *copy_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL); 1557 if (copy_name) 1558 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME, copy_name); 1559 g_free (copy_name); 1560 } 1561 1562 if (g_file_attribute_matcher_matches (attribute_matcher, 1563 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE) || 1564 g_file_attribute_matcher_matches (attribute_matcher, 1565 G_FILE_ATTRIBUTE_STANDARD_ICON)) 1566 { 1567 char *content_type = get_content_type (basename, path, &statbuf, is_symlink, symlink_broken, flags, FALSE); 1568 1569 if (content_type) 1570 { 1571 g_file_info_set_content_type (info, content_type); 1572 1573 if (g_file_attribute_matcher_matches (attribute_matcher, 1574 G_FILE_ATTRIBUTE_STANDARD_ICON)) 1575 { 1576 GIcon *icon; 1577 1578 if (strcmp (path, g_get_home_dir ()) == 0) 1579 icon = g_themed_icon_new ("user-home"); 1580 else if (strcmp (path, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) == 0) 1581 icon = g_themed_icon_new ("user-desktop"); 1582 else 1583 { 1584 icon = g_content_type_get_icon (content_type); 1585 if (G_IS_THEMED_ICON (icon)) 1586 { 1587 const char *type_icon = NULL; 1588 1589 if (S_ISDIR (statbuf.st_mode)) 1590 type_icon = "folder"; 1591 if (type_icon) 1592 g_themed_icon_append_name (G_THEMED_ICON (icon), type_icon); 1593 } 1594 } 1595 1596 if (icon != NULL) 1597 { 1598 g_file_info_set_icon (info, icon); 1599 g_object_unref (icon); 1600 } 1601 } 1602 1603 g_free (content_type); 1604 } 1605 } 1606 1607 if (g_file_attribute_matcher_matches (attribute_matcher, 1608 G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE)) 1609 { 1610 char *content_type = get_content_type (basename, path, &statbuf, is_symlink, symlink_broken, flags, TRUE); 1611 1612 if (content_type) 1613 { 1614 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, content_type); 1615 g_free (content_type); 1616 } 1617 } 1618 1619 if (g_file_attribute_matcher_matches (attribute_matcher, 1620 G_FILE_ATTRIBUTE_OWNER_USER)) 1621 { 1622 char *name = NULL; 1623 1624#ifdef G_OS_WIN32 1625 win32_get_file_user_info (path, NULL, &name, NULL); 1626#else 1627 name = get_username_from_uid (statbuf.st_uid); 1628#endif 1629 if (name) 1630 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER, name); 1631 g_free (name); 1632 } 1633 1634 if (g_file_attribute_matcher_matches (attribute_matcher, 1635 G_FILE_ATTRIBUTE_OWNER_USER_REAL)) 1636 { 1637 char *name = NULL; 1638#ifdef G_OS_WIN32 1639 win32_get_file_user_info (path, NULL, NULL, &name); 1640#else 1641 name = get_realname_from_uid (statbuf.st_uid); 1642#endif 1643 if (name) 1644 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER_REAL, name); 1645 g_free (name); 1646 } 1647 1648 if (g_file_attribute_matcher_matches (attribute_matcher, 1649 G_FILE_ATTRIBUTE_OWNER_GROUP)) 1650 { 1651 char *name = NULL; 1652#ifdef G_OS_WIN32 1653 win32_get_file_user_info (path, &name, NULL, NULL); 1654#else 1655 name = get_groupname_from_gid (statbuf.st_gid); 1656#endif 1657 if (name) 1658 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_GROUP, name); 1659 g_free (name); 1660 } 1661 1662 if (parent_info && parent_info->device != 0 && 1663 g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT) && 1664 statbuf.st_dev != parent_info->device) 1665 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT, TRUE); 1666 1667 get_access_rights (attribute_matcher, info, path, &statbuf, parent_info); 1668 1669#ifdef HAVE_SELINUX 1670 get_selinux_context (path, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0); 1671#endif 1672 get_xattrs (path, TRUE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0); 1673 get_xattrs (path, FALSE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0); 1674 1675 if (g_file_attribute_matcher_matches (attribute_matcher, 1676 G_FILE_ATTRIBUTE_THUMBNAIL_PATH)) 1677 get_thumbnail_attributes (path, info); 1678 1679 g_file_info_unset_attribute_mask (info); 1680 1681 return info; 1682} 1683 1684GFileInfo * 1685_g_local_file_info_get_from_fd (int fd, 1686 const char *attributes, 1687 GError **error) 1688{ 1689 GLocalFileStat stat_buf; 1690 GFileAttributeMatcher *matcher; 1691 GFileInfo *info; 1692 1693#ifdef G_OS_WIN32 1694#define FSTAT _fstati64 1695#else 1696#define FSTAT fstat 1697#endif 1698 1699 if (FSTAT (fd, &stat_buf) == -1) 1700 { 1701 int errsv = errno; 1702 1703 g_set_error (error, G_IO_ERROR, 1704 g_io_error_from_errno (errsv), 1705 _("Error stating file descriptor: %s"), 1706 g_strerror (errsv)); 1707 return NULL; 1708 } 1709 1710 info = g_file_info_new (); 1711 1712 matcher = g_file_attribute_matcher_new (attributes); 1713 1714 /* Make sure we don't set any unwanted attributes */ 1715 g_file_info_set_attribute_mask (info, matcher); 1716 1717 set_info_from_stat (info, &stat_buf, matcher); 1718 1719#ifdef HAVE_SELINUX 1720 if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_SELINUX_CONTEXT) && 1721 is_selinux_enabled ()) 1722 { 1723 char *context; 1724 if (fgetfilecon_raw (fd, &context) >= 0) 1725 { 1726 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT, context); 1727 freecon (context); 1728 } 1729 } 1730#endif 1731 1732 get_xattrs_from_fd (fd, TRUE, info, matcher); 1733 get_xattrs_from_fd (fd, FALSE, info, matcher); 1734 1735 g_file_attribute_matcher_unref (matcher); 1736 1737 g_file_info_unset_attribute_mask (info); 1738 1739 return info; 1740} 1741 1742static gboolean 1743get_uint32 (const GFileAttributeValue *value, 1744 guint32 *val_out, 1745 GError **error) 1746{ 1747 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT32) 1748 { 1749 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, 1750 _("Invalid attribute type (uint32 expected)")); 1751 return FALSE; 1752 } 1753 1754 *val_out = value->u.uint32; 1755 1756 return TRUE; 1757} 1758 1759#ifdef HAVE_UTIMES 1760static gboolean 1761get_uint64 (const GFileAttributeValue *value, 1762 guint64 *val_out, 1763 GError **error) 1764{ 1765 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT64) 1766 { 1767 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, 1768 _("Invalid attribute type (uint64 expected)")); 1769 return FALSE; 1770 } 1771 1772 *val_out = value->u.uint64; 1773 1774 return TRUE; 1775} 1776#endif 1777 1778#if defined(HAVE_SYMLINK) 1779static gboolean 1780get_byte_string (const GFileAttributeValue *value, 1781 const char **val_out, 1782 GError **error) 1783{ 1784 if (value->type != G_FILE_ATTRIBUTE_TYPE_BYTE_STRING) 1785 { 1786 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, 1787 _("Invalid attribute type (byte string expected)")); 1788 return FALSE; 1789 } 1790 1791 *val_out = value->u.string; 1792 1793 return TRUE; 1794} 1795#endif 1796 1797#ifdef HAVE_SELINUX 1798static gboolean 1799get_string (const GFileAttributeValue *value, 1800 const char **val_out, 1801 GError **error) 1802{ 1803 if (value->type != G_FILE_ATTRIBUTE_TYPE_STRING) 1804 { 1805 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, 1806 _("Invalid attribute type (byte string expected)")); 1807 return FALSE; 1808 } 1809 1810 *val_out = value->u.string; 1811 1812 return TRUE; 1813} 1814#endif 1815 1816static gboolean 1817set_unix_mode (char *filename, 1818 const GFileAttributeValue *value, 1819 GError **error) 1820{ 1821 guint32 val; 1822 1823 if (!get_uint32 (value, &val, error)) 1824 return FALSE; 1825 1826 if (g_chmod (filename, val) == -1) 1827 { 1828 int errsv = errno; 1829 1830 g_set_error (error, G_IO_ERROR, 1831 g_io_error_from_errno (errsv), 1832 _("Error setting permissions: %s"), 1833 g_strerror (errsv)); 1834 return FALSE; 1835 } 1836 return TRUE; 1837} 1838 1839#ifdef HAVE_CHOWN 1840static gboolean 1841set_unix_uid_gid (char *filename, 1842 const GFileAttributeValue *uid_value, 1843 const GFileAttributeValue *gid_value, 1844 GFileQueryInfoFlags flags, 1845 GError **error) 1846{ 1847 int res; 1848 guint32 val; 1849 uid_t uid; 1850 gid_t gid; 1851 1852 if (uid_value) 1853 { 1854 if (!get_uint32 (uid_value, &val, error)) 1855 return FALSE; 1856 uid = val; 1857 } 1858 else 1859 uid = -1; 1860 1861 if (gid_value) 1862 { 1863 if (!get_uint32 (gid_value, &val, error)) 1864 return FALSE; 1865 gid = val; 1866 } 1867 else 1868 gid = -1; 1869 1870#ifdef HAVE_LCHOWN 1871 if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) 1872 res = lchown (filename, uid, gid); 1873 else 1874#endif 1875 res = chown (filename, uid, gid); 1876 1877 if (res == -1) 1878 { 1879 int errsv = errno; 1880 1881 g_set_error (error, G_IO_ERROR, 1882 g_io_error_from_errno (errsv), 1883 _("Error setting owner: %s"), 1884 g_strerror (errsv)); 1885 return FALSE; 1886 } 1887 return TRUE; 1888} 1889#endif 1890 1891#ifdef HAVE_SYMLINK 1892static gboolean 1893set_symlink (char *filename, 1894 const GFileAttributeValue *value, 1895 GError **error) 1896{ 1897 const char *val; 1898 struct stat statbuf; 1899 1900 if (!get_byte_string (value, &val, error)) 1901 return FALSE; 1902 1903 if (val == NULL) 1904 { 1905 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, 1906 _("symlink must be non-NULL")); 1907 return FALSE; 1908 } 1909 1910 if (g_lstat (filename, &statbuf)) 1911 { 1912 int errsv = errno; 1913 1914 g_set_error (error, G_IO_ERROR, 1915 g_io_error_from_errno (errsv), 1916 _("Error setting symlink: %s"), 1917 g_strerror (errsv)); 1918 return FALSE; 1919 } 1920 1921 if (!S_ISLNK (statbuf.st_mode)) 1922 { 1923 g_set_error_literal (error, G_IO_ERROR, 1924 G_IO_ERROR_NOT_SYMBOLIC_LINK, 1925 _("Error setting symlink: file is not a symlink")); 1926 return FALSE; 1927 } 1928 1929 if (g_unlink (filename)) 1930 { 1931 int errsv = errno; 1932 1933 g_set_error (error, G_IO_ERROR, 1934 g_io_error_from_errno (errsv), 1935 _("Error setting symlink: %s"), 1936 g_strerror (errsv)); 1937 return FALSE; 1938 } 1939 1940 if (symlink (filename, val) != 0) 1941 { 1942 int errsv = errno; 1943 1944 g_set_error (error, G_IO_ERROR, 1945 g_io_error_from_errno (errsv), 1946 _("Error setting symlink: %s"), 1947 g_strerror (errsv)); 1948 return FALSE; 1949 } 1950 1951 return TRUE; 1952} 1953#endif 1954 1955#ifdef HAVE_UTIMES 1956static int 1957lazy_stat (char *filename, 1958 struct stat *statbuf, 1959 gboolean *called_stat) 1960{ 1961 int res; 1962 1963 if (*called_stat) 1964 return 0; 1965 1966 res = g_stat (filename, statbuf); 1967 1968 if (res == 0) 1969 *called_stat = TRUE; 1970 1971 return res; 1972} 1973 1974 1975static gboolean 1976set_mtime_atime (char *filename, 1977 const GFileAttributeValue *mtime_value, 1978 const GFileAttributeValue *mtime_usec_value, 1979 const GFileAttributeValue *atime_value, 1980 const GFileAttributeValue *atime_usec_value, 1981 GError **error) 1982{ 1983 int res; 1984 guint64 val; 1985 guint32 val_usec; 1986 struct stat statbuf; 1987 gboolean got_stat = FALSE; 1988 struct timeval times[2] = { {0, 0}, {0, 0} }; 1989 1990 /* ATIME */ 1991 if (atime_value) 1992 { 1993 if (!get_uint64 (atime_value, &val, error)) 1994 return FALSE; 1995 times[0].tv_sec = val; 1996 } 1997 else 1998 { 1999 if (lazy_stat (filename, &statbuf, &got_stat) == 0) 2000 { 2001 times[0].tv_sec = statbuf.st_mtime; 2002#if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC) 2003 times[0].tv_usec = statbuf.st_atimensec / 1000; 2004#elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC) 2005 times[0].tv_usec = statbuf.st_atim.tv_nsec / 1000; 2006#endif 2007 } 2008 } 2009 2010 if (atime_usec_value) 2011 { 2012 if (!get_uint32 (atime_usec_value, &val_usec, error)) 2013 return FALSE; 2014 times[0].tv_usec = val_usec; 2015 } 2016 2017 /* MTIME */ 2018 if (mtime_value) 2019 { 2020 if (!get_uint64 (mtime_value, &val, error)) 2021 return FALSE; 2022 times[1].tv_sec = val; 2023 } 2024 else 2025 { 2026 if (lazy_stat (filename, &statbuf, &got_stat) == 0) 2027 { 2028 times[1].tv_sec = statbuf.st_mtime; 2029#if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC) 2030 times[1].tv_usec = statbuf.st_mtimensec / 1000; 2031#elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC) 2032 times[1].tv_usec = statbuf.st_mtim.tv_nsec / 1000; 2033#endif 2034 } 2035 } 2036 2037 if (mtime_usec_value) 2038 { 2039 if (!get_uint32 (mtime_usec_value, &val_usec, error)) 2040 return FALSE; 2041 times[1].tv_usec = val_usec; 2042 } 2043 2044 res = utimes (filename, times); 2045 if (res == -1) 2046 { 2047 int errsv = errno; 2048 2049 g_set_error (error, G_IO_ERROR, 2050 g_io_error_from_errno (errsv), 2051 _("Error setting owner: %s"), 2052 g_strerror (errsv)); 2053 return FALSE; 2054 } 2055 return TRUE; 2056} 2057#endif 2058 2059 2060#ifdef HAVE_SELINUX 2061static gboolean 2062set_selinux_context (char *filename, 2063 const GFileAttributeValue *value, 2064 GError **error) 2065{ 2066 const char *val; 2067 2068 if (!get_string (value, &val, error)) 2069 return FALSE; 2070 2071 if (val == NULL) 2072 { 2073 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, 2074 _("SELinux context must be non-NULL")); 2075 return FALSE; 2076 } 2077 2078 if (is_selinux_enabled ()) { 2079 security_context_t val_s; 2080 2081 val_s = g_strdup (val); 2082 2083 if (setfilecon_raw (filename, val_s) < 0) 2084 { 2085 int errsv = errno; 2086 2087 g_set_error (error, G_IO_ERROR, 2088 g_io_error_from_errno (errsv), 2089 _("Error setting SELinux context: %s"), 2090 g_strerror (errsv)); 2091 return FALSE; 2092 } 2093 g_free (val_s); 2094 } else { 2095 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, 2096 _("SELinux is not enabled on this system")); 2097 return FALSE; 2098 } 2099 2100 return TRUE; 2101} 2102#endif 2103 2104 2105gboolean 2106_g_local_file_info_set_attribute (char *filename, 2107 const char *attribute, 2108 GFileAttributeType type, 2109 gpointer value_p, 2110 GFileQueryInfoFlags flags, 2111 GCancellable *cancellable, 2112 GError **error) 2113{ 2114 GFileAttributeValue value = { 0 }; 2115 2116 _g_file_attribute_value_set_from_pointer (&value, type, value_p, FALSE); 2117 2118 if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0) 2119 return set_unix_mode (filename, &value, error); 2120 2121#ifdef HAVE_CHOWN 2122 else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_UID) == 0) 2123 return set_unix_uid_gid (filename, &value, NULL, flags, error); 2124 else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_GID) == 0) 2125 return set_unix_uid_gid (filename, NULL, &value, flags, error); 2126#endif 2127 2128#ifdef HAVE_SYMLINK 2129 else if (strcmp (attribute, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET) == 0) 2130 return set_symlink (filename, &value, error); 2131#endif 2132 2133#ifdef HAVE_UTIMES 2134 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) == 0) 2135 return set_mtime_atime (filename, &value, NULL, NULL, NULL, error); 2136 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC) == 0) 2137 return set_mtime_atime (filename, NULL, &value, NULL, NULL, error); 2138 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS) == 0) 2139 return set_mtime_atime (filename, NULL, NULL, &value, NULL, error); 2140 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC) == 0) 2141 return set_mtime_atime (filename, NULL, NULL, NULL, &value, error); 2142#endif 2143 2144#ifdef HAVE_XATTR 2145 else if (g_str_has_prefix (attribute, "xattr::")) 2146 return set_xattr (filename, attribute, &value, error); 2147 else if (g_str_has_prefix (attribute, "xattr-sys::")) 2148 return set_xattr (filename, attribute, &value, error); 2149#endif 2150 2151#ifdef HAVE_SELINUX 2152 else if (strcmp (attribute, G_FILE_ATTRIBUTE_SELINUX_CONTEXT) == 0) 2153 return set_selinux_context (filename, &value, error); 2154#endif 2155 2156 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, 2157 _("Setting attribute %s not supported"), attribute); 2158 return FALSE; 2159} 2160 2161gboolean 2162_g_local_file_info_set_attributes (char *filename, 2163 GFileInfo *info, 2164 GFileQueryInfoFlags flags, 2165 GCancellable *cancellable, 2166 GError **error) 2167{ 2168 GFileAttributeValue *value; 2169#ifdef HAVE_CHOWN 2170 GFileAttributeValue *uid, *gid; 2171#endif 2172#ifdef HAVE_UTIMES 2173 GFileAttributeValue *mtime, *mtime_usec, *atime, *atime_usec; 2174#endif 2175#if defined (HAVE_CHOWN) || defined (HAVE_UTIMES) 2176 GFileAttributeStatus status; 2177#endif 2178 gboolean res; 2179 2180 /* Handles setting multiple specified data in a single set, and takes care 2181 of ordering restrictions when setting attributes */ 2182 2183 res = TRUE; 2184 2185 /* Set symlink first, since this recreates the file */ 2186#ifdef HAVE_SYMLINK 2187 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET); 2188 if (value) 2189 { 2190 if (!set_symlink (filename, value, error)) 2191 { 2192 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING; 2193 res = FALSE; 2194 /* Don't set error multiple times */ 2195 error = NULL; 2196 } 2197 else 2198 value->status = G_FILE_ATTRIBUTE_STATUS_SET; 2199 2200 } 2201#endif 2202 2203#ifdef HAVE_CHOWN 2204 /* Group uid and gid setting into one call 2205 * Change ownership before permissions, since ownership changes can 2206 change permissions (e.g. setuid) 2207 */ 2208 uid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_UID); 2209 gid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_GID); 2210 2211 if (uid || gid) 2212 { 2213 if (!set_unix_uid_gid (filename, uid, gid, flags, error)) 2214 { 2215 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING; 2216 res = FALSE; 2217 /* Don't set error multiple times */ 2218 error = NULL; 2219 } 2220 else 2221 status = G_FILE_ATTRIBUTE_STATUS_SET; 2222 if (uid) 2223 uid->status = status; 2224 if (gid) 2225 gid->status = status; 2226 } 2227#endif 2228 2229 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_MODE); 2230 if (value) 2231 { 2232 if (!set_unix_mode (filename, value, error)) 2233 { 2234 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING; 2235 res = FALSE; 2236 /* Don't set error multiple times */ 2237 error = NULL; 2238 } 2239 else 2240 value->status = G_FILE_ATTRIBUTE_STATUS_SET; 2241 2242 } 2243 2244#ifdef HAVE_UTIMES 2245 /* Group all time settings into one call 2246 * Change times as the last thing to avoid it changing due to metadata changes 2247 */ 2248 2249 mtime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED); 2250 mtime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC); 2251 atime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS); 2252 atime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC); 2253 2254 if (mtime || mtime_usec || atime || atime_usec) 2255 { 2256 if (!set_mtime_atime (filename, mtime, mtime_usec, atime, atime_usec, error)) 2257 { 2258 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING; 2259 res = FALSE; 2260 /* Don't set error multiple times */ 2261 error = NULL; 2262 } 2263 else 2264 status = G_FILE_ATTRIBUTE_STATUS_SET; 2265 2266 if (mtime) 2267 mtime->status = status; 2268 if (mtime_usec) 2269 mtime_usec->status = status; 2270 if (atime) 2271 atime->status = status; 2272 if (atime_usec) 2273 atime_usec->status = status; 2274 } 2275#endif 2276 2277 /* xattrs are handled by default callback */ 2278 2279 2280 /* SELinux context */ 2281#ifdef HAVE_SELINUX 2282 if (is_selinux_enabled ()) { 2283 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT); 2284 if (value) 2285 { 2286 if (!set_selinux_context (filename, value, error)) 2287 { 2288 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING; 2289 res = FALSE; 2290 /* Don't set error multiple times */ 2291 error = NULL; 2292 } 2293 else 2294 value->status = G_FILE_ATTRIBUTE_STATUS_SET; 2295 } 2296 } 2297#endif 2298 2299 return res; 2300} 2301