1
0

ln.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. ln.c
  5. Abstract:
  6. This module implements the ln (link) utility.
  7. Author:
  8. Evan Green 22-Oct-2013
  9. Environment:
  10. POSIX
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/lib/types.h>
  16. #include <assert.h>
  17. #include <errno.h>
  18. #include <getopt.h>
  19. #include <libgen.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include "swlib.h"
  24. //
  25. // ---------------------------------------------------------------- Definitions
  26. //
  27. #define LN_VERSION_MAJOR 1
  28. #define LN_VERSION_MINOR 0
  29. #define LN_USAGE \
  30. "usage: ln [-fs] source_file target_file\n" \
  31. " ln [-fs] source_file ... target_directory\n" \
  32. "The ln utility creates a symbolic or hard link to the given file or \n" \
  33. "within the given target directory. Options are:\n" \
  34. " -f, --force -- Remove existing destination files.\n" \
  35. " -s, --symbolic -- Create symbolic links instead of hard links.\n" \
  36. " -v, --verbose -- Print files being linked.\n" \
  37. " --help -- Show this help text and exit.\n" \
  38. " --version -- Print the application version information and exit.\n"
  39. #define LN_OPTIONS_STRING "fsv"
  40. //
  41. // Define ln options.
  42. //
  43. //
  44. // Set this option to remove existing destination files.
  45. //
  46. #define LN_OPTION_FORCE 0x00000001
  47. //
  48. // Set this option to create symbolic links instead of hard ones.
  49. //
  50. #define LN_OPTION_SYMBOLIC 0x00000002
  51. //
  52. // Set this option to print links created.
  53. //
  54. #define LN_OPTION_VERBOSE 0x00000004
  55. //
  56. // ------------------------------------------------------ Data Type Definitions
  57. //
  58. //
  59. // ----------------------------------------------- Internal Function Prototypes
  60. //
  61. INT
  62. LnLink (
  63. ULONG Options,
  64. PSTR Source,
  65. PSTR Destination
  66. );
  67. //
  68. // -------------------------------------------------------------------- Globals
  69. //
  70. struct option LnLongOptions[] = {
  71. {"force", no_argument, 0, 'f'},
  72. {"symbolic", no_argument, 0, 's'},
  73. {"help", no_argument, 0, 'h'},
  74. {"version", no_argument, 0, 'V'},
  75. {"verbose", no_argument, 0, 'v'},
  76. {NULL, 0, 0, 0},
  77. };
  78. //
  79. // ------------------------------------------------------------------ Functions
  80. //
  81. INT
  82. LnMain (
  83. INT ArgumentCount,
  84. CHAR **Arguments
  85. )
  86. /*++
  87. Routine Description:
  88. This routine is the main entry point for the ln (link) utility.
  89. Arguments:
  90. ArgumentCount - Supplies the number of command line arguments the program
  91. was invoked with.
  92. Arguments - Supplies a tokenized array of command line arguments.
  93. Return Value:
  94. Returns an integer exit code. 0 for success, nonzero otherwise.
  95. --*/
  96. {
  97. PSTR AppendedPath;
  98. ULONG AppendedPathSize;
  99. PSTR Argument;
  100. ULONG ArgumentIndex;
  101. INT Option;
  102. ULONG Options;
  103. PSTR SourceBaseName;
  104. ULONG SourceCount;
  105. struct stat Stat;
  106. int Status;
  107. PSTR Target;
  108. BOOL TargetIsDirectory;
  109. int TotalStatus;
  110. SourceCount = 0;
  111. Target = NULL;
  112. Options = 0;
  113. TotalStatus = 0;
  114. //
  115. // Process the control arguments.
  116. //
  117. while (TRUE) {
  118. Option = getopt_long(ArgumentCount,
  119. Arguments,
  120. LN_OPTIONS_STRING,
  121. LnLongOptions,
  122. NULL);
  123. if (Option == -1) {
  124. break;
  125. }
  126. if ((Option == '?') || (Option == ':')) {
  127. Status = 1;
  128. goto MainEnd;
  129. }
  130. switch (Option) {
  131. case 'f':
  132. Options |= LN_OPTION_FORCE;
  133. break;
  134. case 's':
  135. Options |= LN_OPTION_SYMBOLIC;
  136. break;
  137. case 'v':
  138. Options |= LN_OPTION_VERBOSE;
  139. break;
  140. case 'V':
  141. SwPrintVersion(LN_VERSION_MAJOR, LN_VERSION_MINOR);
  142. return 1;
  143. case 'h':
  144. printf(LN_USAGE);
  145. return 1;
  146. default:
  147. assert(FALSE);
  148. Status = 1;
  149. goto MainEnd;
  150. }
  151. }
  152. ArgumentIndex = optind;
  153. if (ArgumentIndex > ArgumentCount) {
  154. ArgumentIndex = ArgumentCount;
  155. }
  156. if (ArgumentIndex < ArgumentCount) {
  157. Target = Arguments[ArgumentCount - 1];
  158. }
  159. SourceCount = ArgumentCount - ArgumentIndex;
  160. //
  161. // Fail if there were not enough arguments.
  162. //
  163. if ((Target == NULL) || (SourceCount <= 1)) {
  164. SwPrintError(0, NULL, "Argument expected. Try --help for usage");
  165. return 1;
  166. }
  167. SourceCount -= 1;
  168. //
  169. // Figure out if the target is a directory.
  170. //
  171. TargetIsDirectory = FALSE;
  172. Status = SwStat(Target, TRUE, &Stat);
  173. if (Status == 0) {
  174. if (S_ISDIR(Stat.st_mode)) {
  175. TargetIsDirectory = TRUE;
  176. }
  177. } else if (Status != ENOENT) {
  178. TotalStatus = Status;
  179. SwPrintError(TotalStatus, Target, "Failed to stat target");
  180. goto MainEnd;
  181. }
  182. //
  183. // It is an error to specify more than one operand and the final one is
  184. // not an existing directory.
  185. //
  186. if ((SourceCount > 1) && (TargetIsDirectory == FALSE)) {
  187. SwPrintError(0, Target, "Target is not a directory");
  188. Status = EINVAL;
  189. goto MainEnd;
  190. }
  191. //
  192. // If there are only two operands and the target is not a directory, then
  193. // just link the source and target.
  194. //
  195. if ((SourceCount == 1) && (TargetIsDirectory == FALSE)) {
  196. Status = LnLink(Options, Arguments[ArgumentIndex], Target);
  197. goto MainEnd;
  198. }
  199. assert(TargetIsDirectory != FALSE);
  200. //
  201. // Loop through the arguments again and perform the links.
  202. //
  203. while (ArgumentIndex < ArgumentCount) {
  204. Argument = Arguments[ArgumentIndex];
  205. ArgumentIndex += 1;
  206. //
  207. // Skip the target.
  208. //
  209. if (Argument == Target) {
  210. continue;
  211. }
  212. //
  213. // Get the final name component of the source, because
  214. // "ln mydir/myfile mydir2" results in a destination file of
  215. // "mydir2/myfile", not "mydir2/mydir/myfile".
  216. //
  217. SourceBaseName = basename(Argument);
  218. if (SourceBaseName == NULL) {
  219. TotalStatus = ENOMEM;
  220. continue;
  221. }
  222. //
  223. // Create an appended version of the path.
  224. //
  225. Status = SwAppendPath(Target,
  226. strlen(Target) + 1,
  227. SourceBaseName,
  228. strlen(SourceBaseName) + 1,
  229. &AppendedPath,
  230. &AppendedPathSize);
  231. if (Status == FALSE) {
  232. TotalStatus = EINVAL;
  233. continue;
  234. }
  235. Status = LnLink(Options, Argument, AppendedPath);
  236. free(AppendedPath);
  237. if (Status != 0) {
  238. TotalStatus = Status;
  239. }
  240. }
  241. MainEnd:
  242. if ((TotalStatus == 0) && (Status != 0)) {
  243. TotalStatus = Status;
  244. }
  245. return TotalStatus;
  246. }
  247. //
  248. // --------------------------------------------------------- Internal Functions
  249. //
  250. INT
  251. LnLink (
  252. ULONG Options,
  253. PSTR Source,
  254. PSTR Destination
  255. )
  256. /*++
  257. Routine Description:
  258. This routine creates a link (hard or symbolic) to the source path at the
  259. destination path.
  260. Arguments:
  261. Options - Supplies link options. See LN_OPTION_* definitions.
  262. Source - Supplies a pointer to the source of the link.
  263. Destination - Supplies a pointer to the destination of the link.
  264. Return Value:
  265. Returns an integer exit code. 0 for success, nonzero otherwise.
  266. --*/
  267. {
  268. PSTR LinkDestination;
  269. struct stat Stat;
  270. INT Status;
  271. LinkDestination = NULL;
  272. //
  273. // Check to see if the destination exists.
  274. //
  275. Status = SwStat(Destination, FALSE, &Stat);
  276. if (Status == 0) {
  277. //
  278. // It exists. If force is on, then try to unlink it.
  279. //
  280. if ((Options & LN_OPTION_FORCE) != 0) {
  281. Status = SwUnlink(Destination);
  282. if (Status != 0) {
  283. Status = errno;
  284. SwPrintError(Status, Destination, "Unable to delete");
  285. goto LinkEnd;
  286. }
  287. //
  288. // Force is off, so just complain and exit.
  289. //
  290. } else {
  291. Status = EEXIST;
  292. SwPrintError(Status, Destination, "Cannot create link at");
  293. goto LinkEnd;
  294. }
  295. }
  296. //
  297. // Create a symbolic or hard link.
  298. //
  299. if ((Options & LN_OPTION_SYMBOLIC) != 0) {
  300. if (SwSymlinkSupported != 0) {
  301. Status = SwCreateSymbolicLink(Source, Destination);
  302. } else {
  303. Status = SwCopy(0, Source, Destination);
  304. }
  305. if (Status != 0) {
  306. SwPrintError(Status, Destination, "Unable to link");
  307. goto LinkEnd;
  308. }
  309. //
  310. // Create a hard link.
  311. //
  312. } else {
  313. //
  314. // If the source is a symbolic link, use its destination.
  315. //
  316. Status = SwStat(Source, FALSE, &Stat);
  317. if (Status != 0) {
  318. SwPrintError(Status, Source, "Unable to stat");
  319. goto LinkEnd;
  320. }
  321. if (S_ISLNK(Stat.st_mode)) {
  322. Status = SwReadLink(Source, &LinkDestination);
  323. if (Status != 0) {
  324. SwPrintError(Status, Source, "Unable to read symbolic link");
  325. goto LinkEnd;
  326. }
  327. Source = LinkDestination;
  328. }
  329. Status = SwCreateHardLink(Source, Destination);
  330. goto LinkEnd;
  331. }
  332. if ((Options & LN_OPTION_VERBOSE) != 0) {
  333. printf("'%s' => '%s'\n", Destination, Source);
  334. }
  335. LinkEnd:
  336. if (LinkDestination != NULL) {
  337. free(LinkDestination);
  338. }
  339. return Status;
  340. }