/* * Using the Hadoop connector requires proper setup of GUCs * This procedure does sanity check on gp_hadoop_connector_jardir, * gp_hadoop_target_version and gp_hadoop_home. * It also update GUC gp_hadoop_connector_version for the current gp_hadoop_target_version. * * It checks the following: * 1. $GPHOME/<gp_hadoop_jardir>/$GP_HADOOP_CONN_VERSION.jar must exists. * 2. if gp_hadoop_home is set, then gp_hadoop_home must exists. */ static void checkHadoopGUCs() { char gphome[MAXPGPATH]; StringInfoData path; int jarFD; /* Check the existence of $GPHOME/<gp_hadoop_jardir>/$GP_HADOOP_CONN_VERSION.jar * * To get $GPHOME, we go from my_exec_path, which is $GPHOME/bin/postgres, and * go up 2 levels. * * Currently, gp_hadoop_connector_jardir is fixed. We look up $GP_HADOOP_CONN_VERSION * using gp_hadoop_target_version. */ snprintf(gphome, sizeof(gphome), "%s", my_exec_path); get_parent_directory(gphome); get_parent_directory(gphome); initStringInfoOfSize(&path, MAXPGPATH); gp_hadoop_connector_version = (char*)getConnectorVersion(); appendStringInfo(&path, "%s/%s/%s.jar", gphome, gp_hadoop_connector_jardir, gp_hadoop_connector_version); jarFD = BasicOpenFile(path.data, O_RDONLY | PG_BINARY, 0); if (jarFD == -1) { ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("cannot open Hadoop Cross Connect in %s: %m", path.data))); } close(jarFD); /* Check the existence of gp_hadoop_home, if specified. * * If user has already specified $HADOOP_HOME in the env, then * there's no need to setup this GUC. */ if (strlen(gp_hadoop_home)> 0) { int hdHomeFD = BasicOpenFile(gp_hadoop_home, O_RDONLY, 0); if (hdHomeFD == -1) { ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("cannot open gp_hadoop_home in %s: %m", gp_hadoop_home))); } close(hdHomeFD); } }
/* * createtbspc_abort_callback: Error cleanup callback for create-tablespace. * This function should be executed only on successful creation of tablespace * directory structure. This way we are sure that the directory and the symlink * that we are removing are created by the same transaction, and are not * pre-existing. Otherwise, we might delete any pre-existing directories. */ static void createtbspc_abort_callback(bool isCommit, void *arg) { Oid tablespaceoid = *(Oid *) arg; char *linkloc_with_version_dir; char *linkloc; struct stat st; if (isCommit) return; linkloc_with_version_dir = palloc(9 + 1 + OIDCHARS + 1 + strlen(PGXCNodeName) + 1 + strlen(TABLESPACE_VERSION_DIRECTORY)); sprintf(linkloc_with_version_dir, "pg_tblspc/%u/%s_%s", tablespaceoid, TABLESPACE_VERSION_DIRECTORY, PGXCNodeName); /* First, remove version directory */ if (rmdir(linkloc_with_version_dir) < 0) { ereport(WARNING, (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", linkloc_with_version_dir))); pfree(linkloc_with_version_dir); return; } /* * Now remove the symlink. * This has been borrowed from destroy_tablespace_directories(). */ linkloc = pstrdup(linkloc_with_version_dir); get_parent_directory(linkloc); if (lstat(linkloc, &st) == 0 && S_ISDIR(st.st_mode)) { /* * We are here possibly because this is Windows, and lstat has identified * the junction point as a directory. */ if (rmdir(linkloc) < 0) ereport(WARNING, (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", linkloc))); } else { if (unlink(linkloc) < 0) ereport(WARNING, (errcode_for_file_access(), errmsg("could not remove symbolic link \"%s\": %m", linkloc))); } pfree(linkloc_with_version_dir); pfree(linkloc); }
int fopen(char *path, int flags) { if (!fs_initialized()) { printf("fopen: file system is not initialized. use mkfs().\n"); return SYSERR; } int fd; for (fd = 0; fd < NUM_FD; fd++) if (oft[fd].state == O_CLOSED) break; if (fd >= NUM_FD) { printf("fopen: Can't open file, reached file descriptor limit (%d). " "Close some files first.\n", NUM_FD); return SYSERR; } directory parent_dir; if (get_parent_directory(&fsd.root_dir, path, &parent_dir) == SYSERR) { printf("fopen: Can't get parent directory of %s\n.", path); return SYSERR; } // now that we have parent directory, drop slashes and search the file in // directory char *filename = strrchr(path, '/'); if (filename == NULL) filename = path; else filename++; // skip '/' inode file_inode; if (get_file_inode(&parent_dir, filename, &file_inode, INODE_TYPE_FILE) == SYSERR) { printf("fopen: Can't get file inode: %s\n", filename); return SYSERR; } // Make sure the file is not already open int i; for (i = 0; i < NUM_FD; i++) if (oft[i].state != O_CLOSED && oft[i].in.inode_idx == file_inode.inode_idx) { printf("fopen: Can't open the file, it is already open.\n"); return SYSERR; } oft[fd].in = file_inode; oft[fd].state = flags; oft[fd].cursor = 0; return fd; }
/* * fsync_parent_path -- fsync the parent path of a file or directory * * This is aimed at making file operations persistent on disk in case of * an OS crash or power failure. */ int fsync_parent_path(const char *fname, const char *progname) { char parentpath[MAXPGPATH]; strlcpy(parentpath, fname, MAXPGPATH); get_parent_directory(parentpath); /* * get_parent_directory() returns an empty string if the input argument is * just a file name (see comments in path.c), so handle that as being the * current directory. */ if (strlen(parentpath) == 0) strlcpy(parentpath, ".", MAXPGPATH); if (fsync_fname(parentpath, true, progname) != 0) return -1; return 0; }
/* * Try open file. Also create parent directries if open for writes. * * mode can contain 'R', that is same as 'r' but missing ok. */ FILE * pgut_fopen(const char *path, const char *omode) { FILE *fp; bool missing_ok = false; char mode[16]; strlcpy(mode, omode, lengthof(mode)); if (mode[0] == 'R') { mode[0] = 'r'; missing_ok = true; } retry: if ((fp = fopen(path, mode)) == NULL) { if (errno == ENOENT) { if (missing_ok) return NULL; if (mode[0] == 'w' || mode[0] == 'a') { char dir[MAXPGPATH]; strlcpy(dir, path, MAXPGPATH); get_parent_directory(dir); pgut_mkdir(dir); goto retry; } } ereport(ERROR, (errcode_errno(), errmsg("could not open file \"%s\": ", path))); } return fp; }
void nautilus_directory_notify_files_moved (GList *file_pairs) { GList *p, *affected_files, *node; GFilePair *pair; NautilusFile *file; NautilusDirectory *old_directory, *new_directory; GHashTable *parent_directories; GList *new_files_list, *unref_list; GHashTable *added_lists, *changed_lists; char *name; NautilusFileAttributes cancel_attributes; GFile *to_location, *from_location; /* Make a list of added and changed files in each directory. */ new_files_list = NULL; added_lists = g_hash_table_new (NULL, NULL); changed_lists = g_hash_table_new (NULL, NULL); unref_list = NULL; /* Make a list of parent directories that will need their counts updated. */ parent_directories = g_hash_table_new (NULL, NULL); cancel_attributes = nautilus_file_get_all_attributes (); for (p = file_pairs; p != NULL; p = p->next) { pair = p->data; from_location = pair->from; to_location = pair->to; /* Handle overwriting a file. */ file = nautilus_file_get_existing (to_location); if (file != NULL) { /* Mark it gone and prepare to send the changed signal. */ nautilus_file_mark_gone (file); new_directory = file->details->directory; hash_table_list_prepend (changed_lists, new_directory, file); collect_parent_directories (parent_directories, new_directory); } /* Update any directory objects that are affected. */ affected_files = nautilus_directory_moved_internal (from_location, to_location); for (node = affected_files; node != NULL; node = node->next) { file = NAUTILUS_FILE (node->data); hash_table_list_prepend (changed_lists, file->details->directory, file); } unref_list = g_list_concat (unref_list, affected_files); /* Move an existing file. */ file = nautilus_file_get_existing (from_location); if (file == NULL) { /* Handle this as if it was a new file. */ new_files_list = g_list_prepend (new_files_list, to_location); } else { /* Handle notification in the old directory. */ old_directory = file->details->directory; collect_parent_directories (parent_directories, old_directory); /* Cancel loading of attributes in the old directory */ nautilus_directory_cancel_loading_file_attributes (old_directory, file, cancel_attributes); /* Locate the new directory. */ new_directory = get_parent_directory (to_location); collect_parent_directories (parent_directories, new_directory); /* We can unref now -- new_directory is in the * parent directories list so it will be * around until the end of this function * anyway. */ nautilus_directory_unref (new_directory); /* Update the file's name and directory. */ name = g_file_get_basename (to_location); nautilus_file_update_name_and_directory (file, name, new_directory); g_free (name); /* Update file attributes */ nautilus_file_invalidate_attributes (file, NAUTILUS_FILE_ATTRIBUTE_INFO); hash_table_list_prepend (changed_lists, old_directory, file); if (old_directory != new_directory) { hash_table_list_prepend (added_lists, new_directory, file); } /* Unref each file once to balance out nautilus_file_get_by_uri. */ unref_list = g_list_prepend (unref_list, file); } } /* Now send out the changed and added signals for existing file objects. */ g_hash_table_foreach (changed_lists, call_files_changed_free_list, NULL); g_hash_table_destroy (changed_lists); g_hash_table_foreach (added_lists, call_files_added_free_list, NULL); g_hash_table_destroy (added_lists); /* Let the file objects go. */ nautilus_file_list_free (unref_list); /* Invalidate count for each parent directory. */ g_hash_table_foreach (parent_directories, invalidate_count_and_unref, NULL); g_hash_table_destroy (parent_directories); /* Separate handling for brand new file objects. */ nautilus_directory_notify_files_added (new_files_list); g_list_free (new_files_list); }
/* * Each database using a table space is isolated into its own name space * by a subdirectory named for the database OID. On first creation of an * object in the tablespace, create the subdirectory. If the subdirectory * already exists, fall through quietly. * * isRedo indicates that we are creating an object during WAL replay. * In this case we will cope with the possibility of the tablespace * directory not being there either --- this could happen if we are * replaying an operation on a table in a subsequently-dropped tablespace. * We handle this by making a directory in the place where the tablespace * symlink would normally be. This isn't an exact replay of course, but * it's the best we can do given the available information. * * If tablespaces are not supported, we still need it in case we have to * re-create a database subdirectory (of $PGDATA/base) during WAL replay. */ void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo) { struct stat st; char *dir; /* * The global tablespace doesn't have per-database subdirectories, so * nothing to do for it. */ if (spcNode == GLOBALTABLESPACE_OID) return; Assert(OidIsValid(spcNode)); Assert(OidIsValid(dbNode)); dir = GetDatabasePath(dbNode, spcNode); if (stat(dir, &st) < 0) { /* Directory does not exist? */ if (errno == ENOENT) { /* * Acquire TablespaceCreateLock to ensure that no DROP TABLESPACE * or TablespaceCreateDbspace is running concurrently. */ LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE); /* * Recheck to see if someone created the directory while we were * waiting for lock. */ if (stat(dir, &st) == 0 && S_ISDIR(st.st_mode)) { /* Directory was created */ } else { /* Directory creation failed? */ if (mkdir(dir, S_IRWXU) < 0) { char *parentdir; /* Failure other than not exists or not in WAL replay? */ if (errno != ENOENT || !isRedo) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", dir))); /* * Parent directories are missing during WAL replay, so * continue by creating simple parent directories rather * than a symlink. */ /* create two parents up if not exist */ parentdir = pstrdup(dir); get_parent_directory(parentdir); get_parent_directory(parentdir); /* Can't create parent and it doesn't already exist? */ if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", parentdir))); pfree(parentdir); /* create one parent up if not exist */ parentdir = pstrdup(dir); get_parent_directory(parentdir); /* Can't create parent and it doesn't already exist? */ if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", parentdir))); pfree(parentdir); /* Create database directory */ if (mkdir(dir, S_IRWXU) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", dir))); } } LWLockRelease(TablespaceCreateLock); } else { ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat directory \"%s\": %m", dir))); } } else { /* Is it not a directory? */ if (!S_ISDIR(st.st_mode)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" exists but is not a directory", dir))); } pfree(dir); }
/* * destroy_tablespace_directories * * Attempt to remove filesystem infrastructure for the tablespace. * * 'redo' indicates we are redoing a drop from XLOG; in that case we should * not throw an ERROR for problems, just LOG them. The worst consequence of * not removing files here would be failure to release some disk space, which * does not justify throwing an error that would require manual intervention * to get the database running again. * * Returns TRUE if successful, FALSE if some subdirectory is not empty */ static bool destroy_tablespace_directories(Oid tablespaceoid, bool redo) { char *linkloc; char *linkloc_with_version_dir; DIR *dirdesc; struct dirent *de; char *subfile; struct stat st; linkloc_with_version_dir = psprintf("pg_tblspc/%u/%s", tablespaceoid, TABLESPACE_VERSION_DIRECTORY); /* * Check if the tablespace still contains any files. We try to rmdir each * per-database directory we find in it. rmdir failure implies there are * still files in that subdirectory, so give up. (We do not have to worry * about undoing any already completed rmdirs, since the next attempt to * use the tablespace from that database will simply recreate the * subdirectory via TablespaceCreateDbspace.) * * Since we hold TablespaceCreateLock, no one else should be creating any * fresh subdirectories in parallel. It is possible that new files are * being created within subdirectories, though, so the rmdir call could * fail. Worst consequence is a less friendly error message. * * If redo is true then ENOENT is a likely outcome here, and we allow it * to pass without comment. In normal operation we still allow it, but * with a warning. This is because even though ProcessUtility disallows * DROP TABLESPACE in a transaction block, it's possible that a previous * DROP failed and rolled back after removing the tablespace directories * and/or symlink. We want to allow a new DROP attempt to succeed at * removing the catalog entries (and symlink if still present), so we * should not give a hard error here. */ dirdesc = AllocateDir(linkloc_with_version_dir); if (dirdesc == NULL) { if (errno == ENOENT) { if (!redo) ereport(WARNING, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", linkloc_with_version_dir))); /* The symlink might still exist, so go try to remove it */ goto remove_symlink; } else if (redo) { /* in redo, just log other types of error */ ereport(LOG, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", linkloc_with_version_dir))); pfree(linkloc_with_version_dir); return false; } /* else let ReadDir report the error */ } while ((de = ReadDir(dirdesc, linkloc_with_version_dir)) != NULL) { if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue; subfile = psprintf("%s/%s", linkloc_with_version_dir, de->d_name); /* This check is just to deliver a friendlier error message */ if (!redo && !directory_is_empty(subfile)) { FreeDir(dirdesc); pfree(subfile); pfree(linkloc_with_version_dir); return false; } /* remove empty directory */ if (rmdir(subfile) < 0) ereport(redo ? LOG : ERROR, (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", subfile))); pfree(subfile); } FreeDir(dirdesc); /* remove version directory */ if (rmdir(linkloc_with_version_dir) < 0) { ereport(redo ? LOG : ERROR, (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", linkloc_with_version_dir))); pfree(linkloc_with_version_dir); return false; } /* * Try to remove the symlink. We must however deal with the possibility * that it's a directory instead of a symlink --- this could happen during * WAL replay (see TablespaceCreateDbspace), and it is also the case on * Windows where junction points lstat() as directories. * * Note: in the redo case, we'll return true if this final step fails; * there's no point in retrying it. Also, ENOENT should provoke no more * than a warning. */ remove_symlink: linkloc = pstrdup(linkloc_with_version_dir); get_parent_directory(linkloc); if (lstat(linkloc, &st) < 0) { int saved_errno = errno; ereport(redo ? LOG : (saved_errno == ENOENT ? WARNING : ERROR), (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", linkloc))); } else if (S_ISDIR(st.st_mode)) { if (rmdir(linkloc) < 0) { int saved_errno = errno; ereport(redo ? LOG : (saved_errno == ENOENT ? WARNING : ERROR), (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", linkloc))); } } #ifdef S_ISLNK else if (S_ISLNK(st.st_mode)) { if (unlink(linkloc) < 0) { int saved_errno = errno; ereport(redo ? LOG : (saved_errno == ENOENT ? WARNING : ERROR), (errcode_for_file_access(), errmsg("could not remove symbolic link \"%s\": %m", linkloc))); } } #endif else { /* Refuse to remove anything that's not a directory or symlink */ ereport(redo ? LOG : ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("\"%s\" is not a directory or symbolic link", linkloc))); } pfree(linkloc_with_version_dir); pfree(linkloc); return true; }
/** * @brief Entry point for pg_bulkload command. * * Flow: * <ol> * <li> Parses command arguments. </li> * <li> Without -r option: Starts the loading. </li> * <li> With -r option: Starts the recovery. </li> * </ol> * * @param argc [in] Number of arguments. * @param argv [in] Argument list. * @return Returns zero if successful, 1 otherwise. */ int main(int argc, char *argv[]) { char cwd[MAXPGPATH]; char control_file[MAXPGPATH] = ""; int i; pgut_init(argc, argv); if (argc < 2) { help(false); return E_PG_OTHER; } if (getcwd(cwd, MAXPGPATH) == NULL) ereport(ERROR, (errcode(EXIT_FAILURE), errmsg("cannot read current directory: "))); i = pgut_getopt(argc, argv, options); for (; i < argc; i++) { if (control_file[0]) ereport(ERROR, (errcode(EXIT_FAILURE), errmsg("too many arguments"))); /* make absolute control file path */ if (is_absolute_path(argv[i])) strlcpy(control_file, argv[i], MAXPGPATH); else join_path_components(control_file, cwd, argv[i]); canonicalize_path(control_file); } /* * Determines data loading or recovery. */ if (recovery) { /* verify arguments */ if (!DataDir && (DataDir = getenv("PGDATA")) == NULL) elog(ERROR, "no $PGDATA specified"); if (strlen(DataDir) + MAX_LOADSTATUS_NAME >= MAXPGPATH) elog(ERROR, "too long $PGDATA path length"); if (control_file[0] != '\0') elog(ERROR, "invalid argument 'control file' for recovery"); return LoaderRecoveryMain(); } else { /* verify arguments */ if (DataDir) elog(ERROR, "invalid option '-D' for data load"); if (control_file[0]) bulkload_options = list_concat( ParseControlFile(control_file), bulkload_options); /* chdir control_file to the parent directory */ get_parent_directory(control_file); /* add path options */ for (i = 0; i < NUM_PATH_OPTIONS; i++) { const pgut_option *opt = &options[i]; const char *path = *(const char **) opt->var; char abspath[MAXPGPATH]; char item[MAXPGPATH + 32]; if (path == NULL) continue; if ((i == 0 || i == 1) && (pg_strcasecmp(path, "stdin") == 0 || type_function)) { /* special case for stdin and input from function */ strlcpy(abspath, path, lengthof(abspath)); } else if (is_absolute_path(path) || (i == 2 && !writer_binary)) { /* absolute path */ strlcpy(abspath, path, lengthof(abspath)); } else if (opt->source == SOURCE_FILE) { /* control file relative path */ join_path_components(abspath, control_file, path); } else { /* current working directory relative path */ join_path_components(abspath, cwd, path); } canonicalize_path(abspath); snprintf(item, lengthof(item), "%s=%s", opt->lname, abspath); bulkload_options = lappend(bulkload_options, pgut_strdup(item)); } return LoaderLoadMain(bulkload_options); } }
/* * Each database using a table space is isolated into its own name space * by a subdirectory named for the database OID. On first creation of an * object in the tablespace, create the subdirectory. If the subdirectory * already exists, just fall through quietly. * * If tablespaces are not supported, this is just a no-op; CREATE DATABASE * is expected to create the default subdirectory for the database. * * isRedo indicates that we are creating an object during WAL replay; * we can skip doing locking in that case (and should do so to avoid * any possible problems with pg_tablespace not being valid). * * Also, when isRedo is true, we will cope with the possibility of the * tablespace not being there either --- this could happen if we are * replaying an operation on a table in a subsequently-dropped tablespace. * We handle this by making a directory in the place where the tablespace * symlink would normally be. This isn't an exact replay of course, but * it's the best we can do given the available information. */ void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo) { #ifdef HAVE_SYMLINK struct stat st; char *dir; /* * The global tablespace doesn't have per-database subdirectories, so * nothing to do for it. */ if (spcNode == GLOBALTABLESPACE_OID) return; Assert(OidIsValid(spcNode)); Assert(OidIsValid(dbNode)); dir = GetDatabasePath(dbNode, spcNode); if (stat(dir, &st) < 0) { if (errno == ENOENT) { /* * Acquire ExclusiveLock on pg_tablespace to ensure that no DROP * TABLESPACE or TablespaceCreateDbspace is running concurrently. * Simple reads from pg_tablespace are OK. */ Relation rel; if (!isRedo) rel = heap_open(TableSpaceRelationId, ExclusiveLock); else rel = NULL; /* * Recheck to see if someone created the directory while we were * waiting for lock. */ if (stat(dir, &st) == 0 && S_ISDIR(st.st_mode)) { /* need not do anything */ } else { /* OK, go for it */ if (mkdir(dir, S_IRWXU) < 0) { char *parentdir; if (errno != ENOENT || !isRedo) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", dir))); /* Try to make parent directory too */ parentdir = pstrdup(dir); get_parent_directory(parentdir); if (mkdir(parentdir, S_IRWXU) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", parentdir))); pfree(parentdir); if (mkdir(dir, S_IRWXU) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", dir))); } } /* OK to drop the exclusive lock */ if (!isRedo) heap_close(rel, ExclusiveLock); } else { ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat directory \"%s\": %m", dir))); } } else { /* be paranoid */ if (!S_ISDIR(st.st_mode)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" exists but is not a directory", dir))); } pfree(dir); #endif /* HAVE_SYMLINK */ }
/* * destroy_tablespace_directories * * Attempt to remove filesystem infrastructure * * 'redo' indicates we are redoing a drop from XLOG; okay if nothing there * * Returns TRUE if successful, FALSE if some subdirectory is not empty */ static bool destroy_tablespace_directories(Oid tablespaceoid, bool redo) { char *linkloc; char *linkloc_with_version_dir; DIR *dirdesc; struct dirent *de; char *subfile; struct stat st; linkloc_with_version_dir = palloc(9 + 1 + OIDCHARS + 1 + strlen(TABLESPACE_VERSION_DIRECTORY)); sprintf(linkloc_with_version_dir, "pg_tblspc/%u/%s", tablespaceoid, TABLESPACE_VERSION_DIRECTORY); /* * Check if the tablespace still contains any files. We try to rmdir each * per-database directory we find in it. rmdir failure implies there are * still files in that subdirectory, so give up. (We do not have to worry * about undoing any already completed rmdirs, since the next attempt to * use the tablespace from that database will simply recreate the * subdirectory via TablespaceCreateDbspace.) * * Since we hold TablespaceCreateLock, no one else should be creating any * fresh subdirectories in parallel. It is possible that new files are * being created within subdirectories, though, so the rmdir call could * fail. Worst consequence is a less friendly error message. * * If redo is true then ENOENT is a likely outcome here, and we allow it * to pass without comment. In normal operation we still allow it, but * with a warning. This is because even though ProcessUtility disallows * DROP TABLESPACE in a transaction block, it's possible that a previous * DROP failed and rolled back after removing the tablespace directories * and symlink. We want to allow a new DROP attempt to succeed at * removing the catalog entries, so we should not give a hard error here. */ dirdesc = AllocateDir(linkloc_with_version_dir); if (dirdesc == NULL) { if (errno == ENOENT) { if (!redo) ereport(WARNING, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", linkloc_with_version_dir))); pfree(linkloc_with_version_dir); return true; } /* else let ReadDir report the error */ } while ((de = ReadDir(dirdesc, linkloc_with_version_dir)) != NULL) { if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue; subfile = palloc(strlen(linkloc_with_version_dir) + 1 + strlen(de->d_name) + 1); sprintf(subfile, "%s/%s", linkloc_with_version_dir, de->d_name); /* This check is just to deliver a friendlier error message */ if (!directory_is_empty(subfile)) { FreeDir(dirdesc); pfree(subfile); pfree(linkloc_with_version_dir); return false; } /* remove empty directory */ if (rmdir(subfile) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", subfile))); pfree(subfile); } FreeDir(dirdesc); /* remove version directory */ if (rmdir(linkloc_with_version_dir) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", linkloc_with_version_dir))); /* * Try to remove the symlink. We must however deal with the possibility * that it's a directory instead of a symlink --- this could happen during * WAL replay (see TablespaceCreateDbspace), and it is also the case on * Windows where junction points lstat() as directories. */ linkloc = pstrdup(linkloc_with_version_dir); get_parent_directory(linkloc); if (lstat(linkloc, &st) == 0 && S_ISDIR(st.st_mode)) { if (rmdir(linkloc) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", linkloc))); } else { if (unlink(linkloc) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove symbolic link \"%s\": %m", linkloc))); } pfree(linkloc_with_version_dir); pfree(linkloc); return true; }
static int ReadStringFromToast(const char *buffer, unsigned int buff_size, unsigned int* out_size) { int result = 0; /* If toasted value is on disk, we'll try to restore it. */ if (VARATT_IS_EXTERNAL_ONDISK(buffer)) { varatt_external toast_ptr; char *toast_data = NULL; /* Number of chunks the TOAST data is divided into */ int32 num_chunks; /* Actual size of external TOASTed value */ int32 toast_ext_size; /* Path to directory with TOAST realtion file */ char *toast_relation_path; /* Filename of TOAST relation file */ char toast_relation_filename[MAXPGPATH]; FILE *toast_rel_fp; unsigned int block_options = 0; unsigned int control_options = 0; VARATT_EXTERNAL_GET_POINTER(toast_ptr, buffer); printf(" TOAST value. Raw size: %8d, external size: %8d, " "value id: %6d, toast relation id: %6d\n", toast_ptr.va_rawsize, toast_ptr.va_extsize, toast_ptr.va_valueid, toast_ptr.va_toastrelid); /* Extract TOASTed value */ toast_ext_size = toast_ptr.va_extsize; num_chunks = (toast_ext_size - 1) / TOAST_MAX_CHUNK_SIZE + 1; printf(" Number of chunks: %d\n", num_chunks); /* Open TOAST relation file */ toast_relation_path = strdup(fileName); get_parent_directory(toast_relation_path); sprintf(toast_relation_filename, "%s/%d", toast_relation_path, toast_ptr.va_toastrelid); printf(" Read TOAST relation %s\n", toast_relation_filename); toast_rel_fp = fopen(toast_relation_filename, "rb"); if (!toast_rel_fp) { printf("Cannot open TOAST relation %s\n", toast_relation_filename); result = -1; } if (result == 0) { unsigned int toast_relation_block_size = GetBlockSize(toast_rel_fp); fseek(toast_rel_fp, 0, SEEK_SET); toast_data = malloc(toast_ptr.va_rawsize); result = DumpFileContents(block_options, control_options, toast_rel_fp, toast_relation_block_size, -1, /* no start block */ -1, /* no end block */ true, /* is toast relation */ toast_ptr.va_valueid, toast_ptr.va_extsize, toast_data); if (result == 0) { if (VARATT_EXTERNAL_IS_COMPRESSED(toast_ptr)) result = DumpCompressedString(toast_data, toast_ext_size); else CopyAppendEncode(toast_data, toast_ext_size); } else { printf("Error in TOAST file.\n"); } free(toast_data); } fclose(toast_rel_fp); free(toast_relation_path); } /* If tag is indirect or expanded, it was stored in memory. */ else { CopyAppend("(TOASTED IN MEMORY)"); } return result; }
int main(int argc, char *argv[]) { /* * options with no short version return a low integer, the rest return * their short version value */ static struct option long_options[] = { {"pgdata", required_argument, NULL, 'D'}, {"help", no_argument, NULL, '?'}, {"version", no_argument, NULL, 'V'}, {"debug", no_argument, NULL, 'd'}, {"show", no_argument, NULL, 's'}, {"noclean", no_argument, NULL, 'n'}, {NULL, 0, NULL, 0} }; int c, ret; int option_index; char *effective_user; char bin_dir[MAXPGPATH]; char *pg_data_native; bool node_type_specified = false; progname = get_progname(argv[0]); set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("initgtm")); if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { usage(progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { puts("initgtm (Postgres-XL) " PGXC_VERSION); exit(0); } } /* process command-line options */ while ((c = getopt_long(argc, argv, "dD:nsZ:", long_options, &option_index)) != -1) { switch (c) { case 'D': pg_data = xstrdup(optarg); break; case 'd': debug = true; printf(_("Running in debug mode.\n")); break; case 'n': noclean = true; printf(_("Running in noclean mode. Mistakes will not be cleaned up.\n")); break; case 's': show_setting = true; break; case 'Z': if (strcmp(xstrdup(optarg), "gtm") == 0) is_gtm = true; else if (strcmp(xstrdup(optarg), "gtm_proxy") == 0) is_gtm = false; else { fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } node_type_specified = true; break; default: /* getopt_long already emitted a complaint */ fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } } /* Non-option argument specifies data directory */ if (optind < argc) { pg_data = xstrdup(argv[optind]); optind++; } if (optind < argc) { fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"), progname, argv[optind + 1]); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } /* Check on definition of GTM data folder */ if (strlen(pg_data) == 0) { fprintf(stderr, _("%s: no data directory specified\n" "You must identify the directory where the data for this GTM system\n" "will reside. Do this with either the invocation option -D or the\n" "environment variable PGDATA.\n"), progname); exit(1); } if (!node_type_specified) { fprintf(stderr, _("%s: no node type specified\n" "You must identify the node type chosen for initialization.\n" "Do this with the invocation option -Z by choosing \"gtm\" or" "\"gtm_proxy\"\n"), progname); exit(1); } pg_data_native = pg_data; canonicalize_path(pg_data); #ifdef WIN32 /* * Before we execute another program, make sure that we are running with a * restricted token. If not, re-execute ourselves with one. */ if ((restrict_env = getenv("PG_RESTRICT_EXEC")) == NULL || strcmp(restrict_env, "1") != 0) { PROCESS_INFORMATION pi; char *cmdline; ZeroMemory(&pi, sizeof(pi)); cmdline = xstrdup(GetCommandLine()); putenv("PG_RESTRICT_EXEC=1"); if (!CreateRestrictedProcess(cmdline, &pi)) { fprintf(stderr, "Failed to re-exec with restricted token: %lu.\n", GetLastError()); } else { /* * Successfully re-execed. Now wait for child process to capture * exitcode. */ DWORD x; CloseHandle(pi.hThread); WaitForSingleObject(pi.hProcess, INFINITE); if (!GetExitCodeProcess(pi.hProcess, &x)) { fprintf(stderr, "Failed to get exit code from subprocess: %lu\n", GetLastError()); exit(1); } exit(x); } } #endif /* Like for initdb, check if a valid version of Postgres is running */ if ((ret = find_other_exec(argv[0], "postgres", PG_BACKEND_VERSIONSTR, backend_exec)) < 0) { char full_path[MAXPGPATH]; if (find_my_exec(argv[0], full_path) < 0) strlcpy(full_path, progname, sizeof(full_path)); if (ret == -1) fprintf(stderr, _("The program \"postgres\" is needed by %s " "but was not found in the\n" "same directory as \"%s\".\n" "Check your installation.\n"), progname, full_path); else fprintf(stderr, _("The program \"postgres\" was found by \"%s\"\n" "but was not the same version as %s.\n" "Check your installation.\n"), full_path, progname); exit(1); } /* store binary directory */ strcpy(bin_path, backend_exec); *last_dir_separator(bin_path) = '\0'; canonicalize_path(bin_path); if (!share_path) { share_path = pg_malloc(MAXPGPATH); get_share_path(backend_exec, share_path); } else if (!is_absolute_path(share_path)) { fprintf(stderr, _("%s: input file location must be an absolute path\n"), progname); exit(1); } canonicalize_path(share_path); effective_user = get_id(); /* Take into account GTM and GTM-proxy cases */ if (is_gtm) set_input(&conf_file, "gtm.conf.sample"); else set_input(&conf_file, "gtm_proxy.conf.sample"); if (show_setting || debug) { fprintf(stderr, "VERSION=%s\n" "GTMDATA=%s\nshare_path=%s\nGTMPATH=%s\n" "GTM_CONF_SAMPLE=%s\n", PGXC_VERSION, pg_data, share_path, bin_path, conf_file); if (show_setting) exit(0); } check_input(conf_file); printf(_("The files belonging to this GTM system will be owned " "by user \"%s\".\n" "This user must also own the server process.\n\n"), effective_user); printf("\n"); umask(S_IRWXG | S_IRWXO); /* * now we are starting to do real work, trap signals so we can clean up */ /* some of these are not valid on Windows */ #ifdef SIGHUP pqsignal(SIGHUP, trapsig); #endif #ifdef SIGINT pqsignal(SIGINT, trapsig); #endif #ifdef SIGQUIT pqsignal(SIGQUIT, trapsig); #endif #ifdef SIGTERM pqsignal(SIGTERM, trapsig); #endif /* Ignore SIGPIPE when writing to backend, so we can clean up */ #ifdef SIGPIPE pqsignal(SIGPIPE, SIG_IGN); #endif switch (pg_check_dir(pg_data)) { case 0: /* PGDATA not there, must create it */ printf(_("creating directory %s ... "), pg_data); fflush(stdout); if (!mkdatadir(NULL)) exit_nicely(); else check_ok(); made_new_pgdata = true; break; case 1: /* Present but empty, fix permissions and use it */ printf(_("fixing permissions on existing directory %s ... "), pg_data); fflush(stdout); if (chmod(pg_data, S_IRWXU) != 0) { fprintf(stderr, _("%s: could not change permissions of directory \"%s\": %s\n"), progname, pg_data, strerror(errno)); exit_nicely(); } else check_ok(); found_existing_pgdata = true; break; case 2: /* Present and not empty */ fprintf(stderr, _("%s: directory \"%s\" exists but is not empty\n"), progname, pg_data); fprintf(stderr, _("If you want to create a new GTM system, either remove or empty\n" "the directory \"%s\" or run %s\n" "with an argument other than \"%s\".\n"), pg_data, progname, pg_data); exit(1); /* no further message needed */ default: /* Trouble accessing directory */ fprintf(stderr, _("%s: could not access directory \"%s\": %s\n"), progname, pg_data, strerror(errno)); exit_nicely(); } /* Select suitable configuration settings */ set_null_conf(); /* Now create all the text config files */ setup_config(); /* Get directory specification used to start this executable */ strcpy(bin_dir, argv[0]); get_parent_directory(bin_dir); if (is_gtm) printf(_("\nSuccess. You can now start the GTM server using:\n\n" " %s%s%sgtm%s -D %s%s%s\n" "or\n" " %s%s%sgtm_ctl%s -Z gtm -D %s%s%s -l logfile start\n\n"), QUOTE_PATH, bin_dir, (strlen(bin_dir) > 0) ? DIR_SEP : "", QUOTE_PATH, QUOTE_PATH, pg_data_native, QUOTE_PATH, QUOTE_PATH, bin_dir, (strlen(bin_dir) > 0) ? DIR_SEP : "", QUOTE_PATH, QUOTE_PATH, pg_data_native, QUOTE_PATH); else printf(_("\nSuccess. You can now start the GTM proxy server using:\n\n" " %s%s%sgtm_proxy%s -D %s%s%s\n" "or\n" " %s%s%sgtm_ctl%s -Z gtm_proxy -D %s%s%s -l logfile start\n\n"), QUOTE_PATH, bin_dir, (strlen(bin_dir) > 0) ? DIR_SEP : "", QUOTE_PATH, QUOTE_PATH, pg_data_native, QUOTE_PATH, QUOTE_PATH, bin_dir, (strlen(bin_dir) > 0) ? DIR_SEP : "", QUOTE_PATH, QUOTE_PATH, pg_data_native, QUOTE_PATH); return 0; }
CIRCA_EXPORT void circa_get_parent_directory(Value* filename, Value* result) { get_parent_directory(filename, result); }
/* * Each database using a table space is isolated into its own name space * by a subdirectory named for the database OID. On first creation of an * object in the tablespace, create the subdirectory. If the subdirectory * already exists, just fall through quietly. * * isRedo indicates that we are creating an object during WAL replay. * In this case we will cope with the possibility of the tablespace * directory not being there either --- this could happen if we are * replaying an operation on a table in a subsequently-dropped tablespace. * We handle this by making a directory in the place where the tablespace * symlink would normally be. This isn't an exact replay of course, but * it's the best we can do given the available information. * * If tablespaces are not supported, you might think this could be a no-op, * but you'd be wrong: we still need it in case we have to re-create a * database subdirectory (of $PGDATA/base) during WAL replay. */ void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo) { struct stat st; char *dir; /* * The global tablespace doesn't have per-database subdirectories, so * nothing to do for it. */ if (spcNode == GLOBALTABLESPACE_OID) return; Assert(OidIsValid(spcNode)); Assert(OidIsValid(dbNode)); dir = GetDatabasePath(dbNode, spcNode); if (stat(dir, &st) < 0) { if (errno == ENOENT) { /* * Acquire TablespaceCreateLock to ensure that no DROP TABLESPACE * or TablespaceCreateDbspace is running concurrently. */ LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE); /* * Recheck to see if someone created the directory while we were * waiting for lock. */ if (stat(dir, &st) == 0 && S_ISDIR(st.st_mode)) { /* need not do anything */ } else { /* OK, go for it */ if (mkdir(dir, S_IRWXU) < 0) { char *parentdir; if (errno != ENOENT || !isRedo) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", dir))); /* Try to make parent directory too */ parentdir = pstrdup(dir); get_parent_directory(parentdir); if (mkdir(parentdir, S_IRWXU) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", parentdir))); pfree(parentdir); if (mkdir(dir, S_IRWXU) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", dir))); } } LWLockRelease(TablespaceCreateLock); } else { ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat directory \"%s\": %m", dir))); } } else { /* be paranoid */ if (!S_ISDIR(st.st_mode)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" exists but is not a directory", dir))); } pfree(dir); }