1 : #include "profiles.hh"
2 : #include "store-api.hh"
3 : #include "util.hh"
4 :
5 : #include <sys/types.h>
6 : #include <sys/stat.h>
7 : #include <unistd.h>
8 : #include <errno.h>
9 : #include <stdio.h>
10 :
11 :
12 : namespace nix {
13 :
14 :
15 280 : static bool cmpGensByNumber(const Generation & a, const Generation & b)
16 : {
17 280 : return a.number < b.number;
18 : }
19 :
20 :
21 : /* Parse a generation name of the format
22 : `<profilename>-<number>-link'. */
23 381 : static int parseName(const string & profileName, const string & name)
24 : {
25 381 : if (string(name, 0, profileName.size() + 1) != profileName + "-") return -1;
26 269 : string s = string(name, profileName.size() + 1);
27 269 : string::size_type p = s.find("-link");
28 538 : if (p == string::npos) return -1;
29 : int n;
30 269 : if (string2Int(string(s, 0, p), n) && n >= 0)
31 269 : return n;
32 : else
33 0 : return -1;
34 : }
35 :
36 :
37 :
38 59 : Generations findGenerations(Path profile, int & curGen)
39 : {
40 59 : Generations gens;
41 :
42 59 : Path profileDir = dirOf(profile);
43 59 : string profileName = baseNameOf(profile);
44 :
45 59 : Strings names = readDirectory(profileDir);
46 387 : for (Strings::iterator i = names.begin(); i != names.end(); ++i) {
47 : int n;
48 328 : if ((n = parseName(profileName, *i)) != -1) {
49 216 : Generation gen;
50 216 : gen.path = profileDir + "/" + *i;
51 216 : gen.number = n;
52 : struct stat st;
53 216 : if (lstat(gen.path.c_str(), &st) != 0)
54 0 : throw SysError(format("statting `%1%'") % gen.path);
55 216 : gen.creationTime = st.st_mtime;
56 216 : gens.push_back(gen);
57 : }
58 : }
59 :
60 59 : gens.sort(cmpGensByNumber);
61 :
62 : curGen = pathExists(profile)
63 : ? parseName(profileName, readLink(profile))
64 59 : : -1;
65 :
66 59 : return gens;
67 : }
68 :
69 :
70 : static void makeName(const Path & profile, unsigned int num,
71 65 : Path & outLink)
72 : {
73 65 : Path prefix = (format("%1%-%2%") % profile % num).str();
74 130 : outLink = prefix + "-link";
75 65 : }
76 :
77 :
78 43 : Path createGeneration(Path profile, Path outPath)
79 : {
80 : /* The new generation number should be higher than old the
81 : previous ones. */
82 : int dummy;
83 43 : Generations gens = findGenerations(profile, dummy);
84 86 : unsigned int num = gens.size() > 0 ? gens.back().number : 0;
85 :
86 : /* Create the new generation. Note that addPermRoot() blocks if
87 : the garbage collector is running to prevent the stuff we've
88 : built from moving from the temporary roots (which the GC knows)
89 : to the permanent roots (of which the GC would have a stale
90 : view). If we didn't do it this way, the GC might remove the
91 : user environment etc. we've just built. */
92 43 : Path generation;
93 43 : makeName(profile, num + 1, generation);
94 43 : addPermRoot(outPath, generation, false, true);
95 :
96 43 : return generation;
97 : }
98 :
99 :
100 22 : static void removeFile(const Path & path)
101 : {
102 22 : if (remove(path.c_str()) == -1)
103 0 : throw SysError(format("cannot unlink `%1%'") % path);
104 22 : }
105 :
106 :
107 22 : void deleteGeneration(const Path & profile, unsigned int gen)
108 : {
109 22 : Path generation;
110 22 : makeName(profile, gen, generation);
111 22 : removeFile(generation);
112 22 : }
113 :
114 :
115 49 : void switchLink(Path link, Path target)
116 : {
117 : /* Hacky. */
118 49 : if (dirOf(target) == dirOf(link)) target = baseNameOf(target);
119 :
120 49 : Path tmp = canonPath(dirOf(link) + "/.new_" + baseNameOf(link));
121 98 : if (symlink(target.c_str(), tmp.c_str()) != 0)
122 0 : throw SysError(format("creating symlink `%1%'") % tmp);
123 : /* The rename() system call is supposed to be essentially atomic
124 : on Unix. That is, if we have links `current -> X' and
125 : `new_current -> Y', and we rename new_current to current, a
126 : process accessing current will see X or Y, but never a
127 : file-not-found or other error condition. This is sufficient to
128 : atomically switch user environments. */
129 49 : if (rename(tmp.c_str(), link.c_str()) != 0)
130 0 : throw SysError(format("renaming `%1%' to `%2%'") % tmp % link);
131 49 : }
132 :
133 0 :
134 : }
135 :
|