Example #1
0
int create_directory( wchar_t *d )
{
	int ok = 0;
	struct stat buf;
	int stat_res = 0;

	while( (stat_res = wstat(d, &buf ) ) != 0 )
	{
		if( errno != EAGAIN )
			break;
	}

	if( stat_res == 0 )
	{
		if( S_ISDIR( buf.st_mode ) )
		{
			ok = 1;
		}
	}
	else
	{
		if( errno == ENOENT )
		{
			wchar_t *dir = wcsdup( d );
			dir = wdirname( dir );
			if( !create_directory( dir ) )
			{
				if( !wmkdir( d, 0700 ) )
				{
					ok = 1;
				}
			}
			free(dir);
		}
	}

	return ok?0:-1;
}
bool env_universal_t::load()
{
    scoped_lock locker(lock);
    callback_data_list_t callbacks;
    const wcstring vars_path = explicit_vars_path.empty() ? default_vars_path() : explicit_vars_path;
    bool success = load_from_path(vars_path, &callbacks);
    if (! success && ! tried_renaming && errno == ENOENT)
    {
        /* We failed to load, because the file was not found. Older fish used the hostname only. Try *moving* the filename based on the hostname into place; if that succeeds try again. Silently "upgraded." */
        tried_renaming = true;
        std::string hostname_id;
        if (get_hostname_identifier(&hostname_id))
        {
            const wcstring hostname_path = wdirname(vars_path) + L'/' + str2wcstring(hostname_id);
            if (0 == wrename(hostname_path, vars_path))
            {
                /* We renamed - try again */
                success = this->load();
            }
        }
    }
    return success;
}
Example #3
0
/* Tests whether the specified string cpath is the prefix of anything we could cd to. directories is a list of possible parent directories (typically either the working directory, or the cdpath). This does I/O!

   We expect the path to already be unescaped.
*/
bool is_potential_path(const wcstring &const_path, const wcstring_list_t &directories, path_flags_t flags, wcstring *out_path)
{
    ASSERT_IS_BACKGROUND_THREAD();
    
    const bool require_dir = !! (flags & PATH_REQUIRE_DIR);
    wcstring clean_path;
	int has_magic = 0;
	bool result = false;
    
    wcstring path(const_path);
    if (flags & PATH_EXPAND_TILDE)
        expand_tilde(path);    
    
    //	debug( 1, L"%ls -> %ls ->%ls", path, tilde, unescaped );
    
    for( size_t i=0; i < path.size(); i++)
    {
        wchar_t c = path.at(i);
        switch( c )
        {
            case PROCESS_EXPAND:
            case VARIABLE_EXPAND:
            case VARIABLE_EXPAND_SINGLE:
            case BRACKET_BEGIN:
            case BRACKET_END:
            case BRACKET_SEP:
            case ANY_CHAR:
            case ANY_STRING:
            case ANY_STRING_RECURSIVE:
            {
                has_magic = 1;
                break;		
            }
				
            case INTERNAL_SEPARATOR:
            {
                break;
            }
				
            default:
            {
                clean_path.push_back(c);
                break;
            }
				
        }
        
    }
    
    if( ! has_magic && ! clean_path.empty() )
    {
        /* Don't test the same path multiple times, which can happen if the path is absolute and the CDPATH contains multiple entries */
        std::set<wcstring> checked_paths;
        
        /* Keep a cache of which paths / filesystems are case sensitive */
        case_sensitivity_cache_t case_sensitivity_cache;
        
        for (size_t wd_idx = 0; wd_idx < directories.size() && ! result; wd_idx++) {
            const wcstring &wd = directories.at(wd_idx);
            
            const wcstring abs_path = apply_working_directory(clean_path, wd);
            
            /* Skip this if it's empty or we've already checked it */
            if (abs_path.empty() || checked_paths.count(abs_path))
                continue;
            checked_paths.insert(abs_path);
            
            /* If we end with a slash, then it must be a directory */
            bool must_be_full_dir = abs_path.at(abs_path.size()-1) == L'/';
            if (must_be_full_dir) 
            {
                struct stat buf;
                if (0 == wstat(abs_path, &buf) && S_ISDIR(buf.st_mode)) {
                    result = true;
                    /* Return the path suffix, not the whole absolute path */
                    if (out_path)
                        *out_path = clean_path;
                }
            }
            else
            {
                DIR *dir = NULL;
                
                /* We do not end with a slash; it does not have to be a directory */
                const wcstring dir_name = wdirname(abs_path);
                const wcstring base_name = wbasename(abs_path);
                if (dir_name == L"/" && base_name == L"/")
                {
                    result = true;
                    if (out_path)
                        *out_path = clean_path;
                }
                else if ((dir = wopendir(dir_name))) {
                    // We opened the dir_name; look for a string where the base name prefixes it
                    wcstring ent;
                    
                    // Check if we're case insensitive
                    bool case_insensitive = fs_is_case_insensitive(dir_name, dirfd(dir), case_sensitivity_cache);
                    
                    // Don't ask for the is_dir value unless we care, because it can cause extra filesystem acces */
                    bool is_dir = false;
                    while (wreaddir_resolving(dir, dir_name, ent, require_dir ? &is_dir : NULL))
                    {                    

                        /* Determine which function to call to check for prefixes */
                        bool (*prefix_func)(const wcstring &, const wcstring &);
                        if (case_insensitive) {
                            prefix_func = string_prefixes_string_case_insensitive;
                        } else {
                            prefix_func = string_prefixes_string;
                        }

                        if (prefix_func(base_name, ent) && (! require_dir || is_dir))
                        {
                            result = true;
                            if (out_path) {
                                /* We want to return the path in the same "form" as it was given. Take the given path, get its basename. Append that to the output if the basename actually prefixes the path (which it won't if the given path contains no slashes), and isn't a slash (so we don't duplicate slashes). Then append the directory entry. */
                                
                                out_path->clear();
                                const wcstring path_base = wdirname(const_path);
                                
                                
                                if (prefix_func(path_base, const_path)) {
                                    out_path->append(path_base);
                                    if (! string_suffixes_string(L"/", *out_path))
                                        out_path->push_back(L'/');
                                }
                                out_path->append(ent);
                                /* We actually do want a trailing / for directories, since it makes autosuggestion a bit nicer */
                                if (is_dir)
                                    out_path->push_back(L'/');
                            }
                            break;
                        }
                    }
                    closedir(dir);
                }
            }
        }
    }
    return result;
}
/* Returns true if modified variables were written, false if not. (There may still be variable changes due to other processes on a false return). */
bool env_universal_t::sync(callback_data_list_t *callbacks)
{
    UNIVERSAL_LOG("sync");
    scoped_lock locker(lock);
    /* Our saving strategy:
    
    1. Open the file, producing an fd.
    2. Lock the file (may be combined with step 1 on systems with O_EXLOCK)
    3. After taking the lock, check if the file at the given path is different from what we opened. If so, start over.
    4. Read from the file. This can be elided if its dev/inode is unchanged since the last read
    5. Open an adjacent temporary file
    6. Write our changes to an adjacent file
    7. Move the adjacent file into place via rename. This is assumed to be atomic.
    8. Release the lock and close the file
    
    Consider what happens if Process 1 and 2 both do this simultaneously. Can there be data loss? Process 1 opens the file and then attempts to take the lock. Now, either process 1 will see the original file, or process 2's new file. If it sees the new file, we're OK: it's going to read from the new file, and so there's no data loss. If it sees the old file, then process 2 must have locked it (if process 1 locks it, switch their roles). The lock will block until process 2 reaches step 7; at that point process 1 will reach step 2, notice that the file has changed, and then start over.
    
    It's possible that the underlying filesystem does not support locks (lockless NFS). In this case, we risk data loss if two shells try to write their universal variables simultaneously. In practice this is unlikely, since uvars are usually written interactively.
    
    Prior versions of fish used a hard link scheme to support file locking on lockless NFS. The risk here is that if the process crashes or is killed while holding the lock, future instances of fish will not be able to obtain it. This seems to be a greater risk than that of data loss on lockless NFS. Users who put their home directory on lockless NFS are playing with fire anyways.
    */
    const wcstring vars_path = explicit_vars_path.empty() ? default_vars_path() : explicit_vars_path;
    
    /* If we have no changes, just load */
    if (modified.empty())
    {
        this->load_from_path(vars_path, callbacks);
        return false;
    }
    
    const wcstring directory = wdirname(vars_path);
    bool success = true;
    int vars_fd = -1;
    int private_fd = -1;
    wcstring private_file_path;
    
    UNIVERSAL_LOG("Performing full sync");
    
    /* Open the file */
    if (success)
    {
        success = this->open_and_acquire_lock(vars_path, &vars_fd);
    }

    /* Read from it */
    if (success)
    {
        assert(vars_fd >= 0);
        this->load_from_fd(vars_fd, callbacks);
    }

    /* Open adjacent temporary file */
    if (success)
    {
        success = this->open_temporary_file(directory, &private_file_path, &private_fd);
    }
    
    /* Write to it */
    if (success)
    {
        assert(private_fd >= 0);
        success = this->write_to_fd(private_fd, private_file_path);
    }
    
    if (success)
    {
        /* Apply new file */
        success = this->move_new_vars_file_into_place(private_file_path, vars_path);
    }
    
    if (success)
    {
        /* Since we moved the new file into place, clear the path so we don't try to unlink it */
         private_file_path.clear();
    }
    
    /* Clean up */
    if (vars_fd >= 0)
    {
        close(vars_fd);
    }
    if (private_fd >= 0)
    {
        close(private_fd);
    }
    if (! private_file_path.empty())
    {
        wunlink(private_file_path);
    }
    
    if (success)
    {
        /* All of our modified variables have now been written out. */
        modified.clear();
    }
    
    return success;
}